08.函数预编译

预编译

js运行三部曲

//js:单线程解释语言(翻译一句执行一句)

语法分析

// 在语句执行之前编译器会通篇扫描一遍看有没有低级错误
// 这个过程就是语法分析的过程

预编译

预编译前奏

test(); // 在这里也能执行函数
function test() {
    conloe.log('a');
}
test(); // 在这里能执行函数

var a = 123;
conloe.log('a');  // 打印123

conloe.log('a');  // 打印undefined
var a = 123;

conloe.log('a');  // 直接打印会报错,因为变量没声明

函数声明整体提升(不管函数写在哪里,系统都会把函数提到最前面,所以test()放函数上面能执行)

变量声明提升

var a = 123; 等价于 var a; a = 123;
// 系统会把变量提到最前面
console.log('a');
var a = 123;
等价于:
var a;
console.log('a');
a = 123;   // 所以上面例子打印的是undefined
imply global暗示全局变量:

即任何变量,如果变量未经声明就赋值,此变量就为全局对象所有。

// 全局对象是window (window就像一个仓库一样) window是一个对象
a = 123;   // 未声明(没有var) 归window所有 
相当于:
window {
    a : 123
}
console.log(a)  // 打印123  因为访问的就是window.a

// 注意这个赋值顺序 123赋值给未声明的b  然后声明a 然后把b的值赋给a  
// 所以打印window.a是undefined  打印window.b是123 
// 因为a不属于window(a是定义在函数中的局部变量)
var a = b = 123; // 123赋值给b b再赋值给a 连续赋值
function test() {
    var a = b = 123; 
}
test();
一切声明的全局变量,全是window的属性。
var a = 123;===> window.a = 123;

function test() {
    var b = 123;  
}
test();
console.log(window.b);  // 打印结果是undefined 因为b是函数中定义的局部变量

// 3.window就是全局
var a = 123;
console.log(a); --> console.log(window.a);

var a = 123;
var b = 234;
var c = 345;
==>window {
        a : 123,
        b : 234,
        c : 345
    }

预编译四部曲:

创建AO对象

创建AO对象就是Activation Object(活跃对象)

活跃对象就是执行期上下文,也可以理解为作用域。也就是函数产生的一个存储空间库

// 预编译发生在函数执行前的那一刻
function fn(a) {   // a是形参
    console.log(a);  
    var a = 123;  // 123赋值给a
    console.log(a);  
	function a () {}
    console.log(a);  
	var b = function () {}  // function () {}赋值给b
    console.log(b);
    function d () {}
}
fn(1);
// 1.创建AO对象
AO {
    
}

找形参和变量声明,将变量和形参名作为AO属性名,值为undefined

function fn(a) {   // a是形参
    console.log(a);  
    var a = 123;  // 123赋值给a
    console.log(a);  
	function a () {}
    console.log(a);  
	var b = function () {}  // function () {}赋值给b
    console.log(b);
    function d () {}
}
fn(1);
// 2.找形参和声明的变量,放到AO对象中,值是undefined,
// 因为a既是形参又是变量,所以只放一次
AO {
    a : undefined,
    b :undefined,
}

将实参值和形参统一

function fn(a) {   // a是形参
    console.log(a);  
    var a = 123;  // 123赋值给a
    console.log(a);  
	function a () {}
    console.log(a);  
	var b = function () {}  // b是声明的变量
    console.log(b);
    function d () {}
}
fn(1);  // 实参是1

// 3.将实参值和形参相统一 实参的值1赋值给a
AO {
    a : 1,
    b :undefined,
}

在函数体里面找函数声明,值赋予函数体

function fn(a) {   // a是形参
    console.log(a);  
    var a = 123;  // 123赋值给a
    console.log(a);  
	function a () {}
    console.log(a);  
	var b = function () {}  // function () {}赋值给b
    console.log(b);
    function d () {}
}
fn(1);  // 实参是1
// 4.在函数体里面找到函数声明,值赋予函数体,也就是把函数体赋值给函数
// 函数体:function a () {} 和 function d () {} 因为a有了不用放 只赋值 把d放进去
AO {
    a : function a () {},
    b :undefined,
    d : function d () {}
}

预编译完成之后开始执行函数

预编译练习

预编译创建AO对象的目的就是先把东西放好,方便执行的时候去用。

// 预编译练习①
function fn(a) {   // a是形参
    console.log(a);    // 这里打印的是AO对象中的数据 也就是function a () {}
    var a = 123;  // // 123赋值给a
    console.log(a);   // 这里打印的是AO对象中的数据 也就是123
	function a () {}
    console.log(a);    // 这里打印的是AO对象中的数据 也就是123
	var b = function () {}  // function () {}赋值给b
    console.log(b);  // 这里打印的是AO对象中的数据 也就是function () {}
    function d () {}
}
fn(1);  // 实参是1
// 4.在函数体里面找到函数声明,值赋予函数体,也就是把函数体赋值给函数
// 函数体:function a () {} 和 function d () {} 因为a有了不用放 只赋值 把d放进去
AO {
    a : function a () {},
    b :undefined,
    d : function d () {}
}
// 预编译练习②
function test(a, b) {
    console.log(a);  // 打印AO的初始值1
    c = 0;  // 0赋值给c
    var c;  
    a = 3;  // 3赋值给a
    b = 2;  // 2赋值给b
    console.log(b);  // 打印2,因为b被赋值
    function b () {}
    function d () {}
    console.log(b);  // 打印2,因为b被赋值
}
test(1);  //这里只写一个参数默认是给第一个形参

// 1.创建AO对象  AO {}
// 2.找形参和变量并赋值undefined
AO {
    a : undefined,
    b : undefined,
    c : undefined
}
// 3.实参和形参值统一
AO {
    a : 1,
    b : undefined,
    c : undefined
}
// 4.找到函数体并赋值
AO {
    a : 1,
    b : function b () {},
    c : undefined,
    d : function d () {}
}
// 预编译练习③
function test(a,b) {
    console.log(a);  // 打印AO中的初始值function a () {}
    console.log(b);  // 打印AO中的初始值undefined
    var b = 234;    // 234赋值给b
    console.log(b);  // 打印234
    a = 123;         // 123赋值给形参a
    console.log(a);  // 打印123
    function a () {}  // 这里没有赋值过程,所以a的值不变
    var a;            // 这里没有赋值过程,所以a的值不变
    b = 234;        // 234赋值给b
    var b = function () {}  // function () {}赋值给形参b
    console.log(a);  // 打印123
    console.log(b);  // 打印function () {}
}
test(1);  //这里只写一个参数默认是给第一个形参
// 1,创建AO对象  AO {}
// 2.找形参和变量并赋值undefined
AO {
    a : undefined,
    b : undefined
}
// 3.实参形参相统一
AO {
    a : 1,
    b : undefined
}
// 4.找到函数体并赋给AO对象
AO {
    a : function a () {},
    b : undefined
}
// 预编译练习④
console.log(a);  // 打印的是function a() {} 
// 根据预编译步骤可知 函数体是最后一步,权限最高 
var a = 123;
function a() {}

全局预编译

生成了一个GO对象 Global Object

console.log(a);  // 打印结果为GO初始值function a (){}
var a = 123;     // 把123赋值给了a
function a (){}
console.log(a);  // 打印结果为123

// 1.创建GO对象
// 2.找出形参和变量并赋值undefined
GO {
    a : undefined
}
// 3.没有实参,所以跳过第三步
// 4.找到函数体并赋给AO对象
GO {
    a : function a (){}
}

window === GO

也就是一个变量未经声明就赋值了,是需要放到GO中预编译的。

window === GO  
console.log(window.a) ==> console.log(GO.a) ==> console.log(a)

function test() {
    var a = b = 123; // b是未声明就赋值的变量,归window所有,也就是归GO所有
    console.log(window.b);  //打印123
    console.log(a);  //打印123
    console.log(window.a);  //打印undefined 因为window里面没有a
}
test();
// 执行test的时候会生成一个AO,变量只有a
AO {
    a : undefined,
}
GO {
    b : 123
}

GO/AO的优先级

// 函数预编译发生在函数执行前的一刻
// 练习一:
// 打印的时候相同名字的AO上有就打印AO上的,没有就打印GO上的
console.log(test);  // 打印全局的test 也就是function test () {} 
function test(test) {
    console.log(test);  // 打印AO中的初始值function test () {}
    var test = 234;     //234赋值给AO中的test 
    console.log(test);  // 打印234
    function test () {} 
}
test(1);
var test = 123;  // 注意这是全局的 123赋值给全局的test

GO {
    test : function test () {},
}

AO {
    test : function test () {},
}
// 练习二:
GO {
    global : undefined,  // 执行之后是100
    fn : function (){...}
}
var global = 100;
function fn() {
    console.log(global);  // 打印100
}
// 执行前函数预编译  函数里面只有一个打印global 没有值 所以找GO  GO里面有100
fn();

// 练习三:
GO {
    global : 100,  // 有声明全局变量,然后把值写进去
    fn : function (){...}
}
global = 100;  
function fn() {
    console.log(global);  // undefined
    global = 200;
    console.log(global);  // 200
    var global = 300;
}
AO {  
    global : undefined,  // 函数体内有变量声明  有自己的就用自己的 没有才用GO内的
} 
fn();
var global;

// 练习四
GO {
    a : 100test : function (){...}c : 234
}
function test () {
    console.log(b);  // undefined
    if(a) {  // if要执行的时候才有用  预编译的时候不执行 所以不看
        var b = 100;
    }
    console.log(b);  // undefined if要执行的时候才有用  预编译的时候不执行 所以没用
    c = 234;  // c没有声明 所以归GO所有
    console.log(c);  // 234
}
var a;
AO {
     a : undefined,
     b : undefined,  // 不看if
}
test();
a = 10;
console.log(c);  // 234
// 百度面试题
// 1.百度面试题一
AO {
    foo : function foo() {}  // 函数体权限最高
}
function bar() {
    return foo; 
    foo = 10;
    function foo() {}
    var foo = 11;    
}
console.log(bar());  // function foo (){}

// 2.百度面试题二
AO {
   foo : 11  // 函数体之后  11又赋值给foo 所以打印11
}
function bar() {
    foo = 10;
    function foo() {}
    var foo = 11;  
    return foo;
}
console.log(bar());  // 11
// 知识回顾
// 1.创建GO/AO对象  2.找形参和变量赋值undefined
// 3.形参实参相统一  4.函数体赋值给GO/AO
console.log(b);  // undefined  因为打印之前还没赋值
var b = function () {}  
console.log(b);  // // function () {}  因为打印之前还没赋值

console.log(b);  // function b() {}   因为函数体权限最高
function b() {}  

作业

// 题目一:注意AO和GO各提各的(有重复正常)
GO {
    a : 100,
    demo : function () {},  // 函数体
    f : 123  // 函数内部赋值 
}
a = 100;
function demo(e) {
    function e() {}
    arguments[0] = 2;  // 这里是给形参e赋值为2
    document.write(e);  // 打印出2
    if(a) {   // 循环是在执行之后才起作业  所以不看
        var b = 123;
        function c() {
            // if里面不能声明function(以前可以)
        }
    }
    var c;
    a = 10;  // 给a赋值10
    var a;
    document.write(b);  /// 打印AO中的b  undefined
    f = 123;    // 变量未声明  归GO所有
    document.write(c);  // undefined 
    document.write(a);  // 打印AO中的a 10
}
var a;
demo(1);
document.write(a);  // 打印全局的a 100
document.write(f);  // 123

AO {
    e : 2,
    b : undefined,
    c : undefined,
    a : 10
}
// 题目二:
var str = false + 1;  // 转换成数字相加  0 + 1 
document.write(str);  // 1
var demo = false == 1;  // false是0 所以false == 1判断结果是false  false赋值给demo
document.write(demo);  // 打印false 
if(typeof(a)&&-true + (+undefined) + ""){  
  // a没定义不报错,会打印str类型的undefined str类型的undefined是true +undefined转换为str类型的NaN “”是false    转换成数字相加就是 1 + (-1) + 1 + 0 = 1
    document.write('基础扎实');
}
if(11 + "11" * 2 ==33) {  //*会把字符串转换成数字再进行运算  所以结果是33
    document.write('基础扎实');
}
!!" " + !!"" - !!false || document.write('你觉得能打印吗?');  // true false false 1 + 0 - 0 = 1 true  一真即真  所以不往后看  所以不会打印
//  " "中间有空格是空格字符串 “”中间没空格才是空串
// 百度面试题 写出foo的值
(window.foo || (window.foo = 'bar')); // || 的优先级比 = 高  所以后一半加了小括号
// 先看括号的部分  “bar”赋值给foo  然后再看window.foo
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

好好学习_fighting

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值