AO对象与GO对象——预编译(1)

JS引擎都做了什么?

1.检查通篇的语法错误(语法分析)
1.5预编译的过程(预编译)
2.解释一行执行一行(解释执行)

函数声明提升

text();
function text() { console.log(1); } // 1

var关键字的声明提升,赋值不提升

var关键字声明的变量会自动提升到函数作用域顶部

所谓的“提升”(hoist),也就是把所有变量声明都拉到函数作用域的顶部。

此外,反复多次使用 var 声明同一个变量也没有问题。

console.log(a) // Uncaught ReferenceError: a is not defined
console.log(a);
var a; // undefined
console.log(a);
var a = 10; // undefined
// 提升的仅仅是变量的声明而不是初始化

undefined和error不一样!!!

一个奇奇怪怪的例子

console.log(a);
function a(a) {
    var a = 10;
    var a = function () {
        
    }
}
var a = 1;


// 打印结果竟然不是undefined!
//    ƒ a(a) {
//        var a = 10;
//        var a = function () {
//
//        }
//    }

暗示全局变量

预编译有个特点:任何变量,如果未声明就赋值,那该变量为全局变量,即暗示全局变量(imply global)。并且所有的全局变量都是window的属性。

var a = 1;
b = 2;
console.log(window.b); // 1
console.log(window.a); // 2
// var操作符在全局作用域下声明的变量自动变为window的对象 如a
// 在函数内定义变量时省略var会创建全局变量 如b
function test() {
    var a = b = 2;
}
test();
console.log(b); // 2
console.log(a); // Uncaught ReferenceError: a is not defined
console.log(window.a); // undefined
// 函数体先声明了一个局部变量a,然后不使用var声明了一个全局变量b,将b赋初值为2,最后用b的值初始化a 
// 因此b作为全局变量在函数test()外部仍能访问
// a作为局部变量,只有函数作用域
// window对象打印不存在的属性时显示undefined
// 在第二局console报错时不会继续向下执行了,所以注释掉这一句

AO对象

AO对象全称为:activation object (活跃对象/执行期上下文/函数上下文),在函数执行前执行函数预编译,此时会产生一个AO对象,AO对象保存该函数的参数变量。

函数预编译步骤:

  1. 产生空的AO对象
  2. 找形参和变量声明,作为AO对象的属性名,值为undefined。
  3. 实参值赋值给形参。
  4. 在函数里面寻找函数声明,函数名作为AO对象的属性名(若形参、变量声明与函数名重叠,则函数体值会覆盖参数值与undefined),值为函数体。

执行

// eg.1
function test(a) {
    console.log(a);
    var a = 1;
    console.log(a);
    function a() { };
    console.log(a);
    var b = function () { };
    console.log(b);
    function d() { };
}
test(2);
  1. 创建AO对象
    AO{
    //此时AO对象为空
    }

  2. 确定AO对象的属性名
    e.g.1AO{
    a:undefined; //函数参数
    b:undefined; //函数里面声明的变量
    }

  3. 将实参值赋值给形参
    AO{
    a:undefined ->

    ​ 2 -> //函数参数
    b:undefined; //函数里面声明的变量
    }

  4. 处理函数里面的声明函数
    AO{
    a:undefined ->

    ​ 2 ->

    ​ function a () {} -> //函数声明提前,变量名一样,值覆盖

    ​ 1,

    b:undefined ->

    ​ function (){}, //函数里面声明的变量
    d:function d () {}
    }

此时函数预编译已经完成的,预编译后执行代码:
第一条执行的是控制台打印出a的值,所以输出function a () {};
第二条语句赋值给a,则AO对象中a的值被覆盖为2;
第三条语句控制台打印a的值为1;
第四条为声明,预编译处理过所以直接跳过
第五条打印出a的值,一样为1;
第六条为赋值,赋值b的值为function () {};
第七条打印出b的值function () {};
第八条声明,预编译处理过所以直接跳过;

所以输出结果应该是
function a () {};
1;
1;
function () {};
执行效果如图:
在这里插入图片描述

// eg.2
function test(a, b) {
    console.log(a);
    c = 0;
    var c;
    a = 5;
    b = 6;
    console.log(b);
    function b() { };
    function d() { };
    console.log(b);
}
test(1);

AO = {
    a: undefined ->
        1 -> // 此时打印第一个console
        5,
    b: undefined ->
        function b() { } ->
        6, // 此时打印第二个console,并且由于下面对b()的函数声明
           //在预处理时做过了,直接pass,第三个console也输出这个b
    c: undefined ->
        0,
    d: function d() { },
    
}

GO对象

GO对象全称为 global object(全局对象,等同于window),在开始预编译时产生的对象,比AO对象先产生,用于存放全局变量,也称为全局作用域。

预编译之前进行

  1. 生成空的GO对象
  2. 将变量声明的变量名当做GO对象的属性名,值为undefinded
  3. 将声明函数的函数名当做GO对象的属性名,值为函数体
console.log(a);
var a = 123;
function a() {
    
}
console.log(a);
// GO: {
//     a: undefined ->
//         function a() { } -> // 此时执行第一句console,预编译结束
//         123, //此时执行第二句console
// }

在这里插入图片描述

AO和GO综合练习

1.求输出结果

console.log(a); // undefined
a = 100;
function test() {
    console.log(a); // undefined
    a = 200;
    console.log(a); // 200
    var a = b = 300;
}
test();
console.log(b); // 300
var a;
console.log(a); //100
GO: {
    a: undefined ->
    100,
    function test() { },    
    b: undefined ->
    300,
}
AO: {
    a: undefined ->
        200 ->
        300,
}
  1. 生成GO对象
    GO{
    }

  2. 将声明变量添加进GO对象内,值为undefined
    GO{
    a:undefined;
    }

  3. 将声明函数添加进GO对象呢,值为函数体
    GO{
    a:undefined;
    function test() { … };
    }

  4. 预编译完成,执行代码:输出a,此时a的值为:undefined

  5. a赋值,此时GO对象内的a值变为100
    GO{
    a:100;
    function test() { … };
    }

  6. 执行函数test(),生成AO对象
    AO{

  7. }

  8. 将函数声明的变量添加进AO对象,值为undefined
    AO{
    a:undefined;
    }

  9. 函数没有传递参数,跳过函数预编译的第三步

  10. 函数里面没有声明函数,跳过函数预编译的第四步

  11. 执行函数,打印a,此时AO里面有属性a,则输出AO里面a的值,即输出: undefined

  12. AO的a赋值200,此时AO的a的值为200
    AO{
    a:200;
    }

  13. 输出a的值:200

  14. 将300的值赋值给b再赋值给a,此时b未声明,所以b为全局变量,将b添加进GO对象内,将a的值改为300
    GO{
    a:100;
    function test() { … };
    b:300;
    }
    AO{
    a:300;
    }

  15. 输出a的值:300,函数执行完成

  16. 输出b的值:300

  17. 输出a的值,此时a的值为GO里面的值:100

2.求输出结果

	//GO{
	//生成的GO对象
	//a:undefined;
	//test:function test(){ ... };
	//}
    function test() {
        console.log(b);//undefined
        if (a) {//AO里面没有a的属性,自动寻找GO里面a的属性,此时a的值为undefined,语句不执行
            var b = 50;
        }
        console.log(b);//undefined
        c = 100;//未声明则为全局变量,添加在GO对象内
        console.log(c); //100
    }
    var a;
    test();//执行函数预编译
    //AO{
    //生成的AO对象
	//b:undefined;
	//}
    a = 10;//GO里面的a赋值为10
    console.log(c);//100
function test() {
    return a;
    a = 1;
    function a() {
        var a = 2;
    }
}
console.log(test());
// AO: {
//     a: function a() { },
    
// }

//ƒ a() {
//        var a = 2;
//    }
function test() {
    a = 1;
    function a() { };
    var a = 2;
    return a;
}
console.log(test());
// AO: {
//     a: undefined ->
//         a(){ } ->
//         1 ->
//         2,
    
// }
a = 1;
function test(e) {
    function e() { }
    arguments[0] = 2; //与实参同步
    console.log(e); //2
    if (a) {
        var b = 3; // 这里也会变量提升,不要忘记
    }
    var c;
    a = 4;
    var a;
    console.log(b); //undefined
    f = 5;
    console.log(c); // undefined
    console.log(a); // 4
}
var a;
test(1);

// GO: {
//     a: undefined ->
//     1,
//     test: test(){ },
// }
// AO: {
//     e: undefined ->
//         1 ->
//         e(){ } ->
//         2,
//     b: undefined,
//     c: undefined,
//     a: undefined ->
//         4,   
// }

AO、GO实际上是为了解决作用域、作用域链相关所产生的一切问题

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值