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

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

文章目录 JS引擎都做了什么?函数声明提升var关键字的声明提升,赋值不提升一个奇奇怪怪的例子暗示全局变量 AO对象GO对象AO和GO综合练习

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对象保存该函数的参数变量。

函数预编译步骤:

产生空的AO对象找形参和变量声明,作为AO对象的属性名,值为undefined。实参值赋值给形参。在函数里面寻找函数声明,函数名作为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);

创建AO对象
AO{
//此时AO对象为空
}

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

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

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

处理函数里面的声明函数
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对象先产生,用于存放全局变量,也称为全局作用域。

预编译之前进行

生成空的GO对象将变量声明的变量名当做GO对象的属性名,值为undefinded将声明函数的函数名当做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,
}

生成GO对象
GO{
}

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

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

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

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

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

}

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

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

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

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

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

输出a的值:200

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

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

输出b的值:300

输出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实际上是为了解决作用域、作用域链相关所产生的一切问题

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/995453.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-21
下一篇 2022-05-21

发表评论

登录后才能评论

评论列表(0条)

保存