Go to comments

JavaScript 预编译

复习

复习上一节学习的内容:

函数就像屋子一样,一个函数从定义的那一刻就形成了自己的一个域,姑且管它叫做"作用域"虽然说不太标准,这个"域"能干什么呢?

两个独立的屋子之间的变量是不能相互访问的,比如在test()里面不能访问b,在demo()里面也不能访问a,这是两个独立的屋子,它们之间任何关系都没有。

function test(){
    var a = 123;
    document.write(b); // test里面不能访问b
}

function demo(){
    var b = 234;
    document.write(a); // demo里面不能访问a
}

如果两个函数样嵌套呢?

function test(){
    var a = 123;
    
    function demo(){
        var b = 234;
        document.write(a);
    }

    demo();
    
    document.write(b);
}

test(); // Uncaught ReferenceError: b is not defined

互相嵌套的函数,外层函数不能访问里层的函数的东西,但里面的函数可以访问外层函数的东西,越往里面的权限却大。就像我国国情的祖孙关系,孙子可以向任何长辈要零花钱,反过来长辈不能向小辈要钱。

定义在全局里面的变量叫"全局变量",定义在全局里面的函数叫"全局函数",定义在局部函数里面的变量叫"局部变量"。

一、js运行三部曲

学预编译之前先了解一下js执行的过程


js有两个特点:

1. 第一是单线程

2. 第二是解释性语言


什么叫解释性语言?

解析性语句是翻译一句执行一句,翻译一句执行一句,不是通篇编译成一个文件然后再执行。


解释性语句的特点很容易理解,就是读一句执行一句,其实还没有这么的直观,读一句执行一句那是最后一步,js执行的时候分三步

第一步:叫“语法分析”或者叫语言分析,是通篇执行的一个过程,在js代码执行之前系统会扫描一遍,

              看看有没有低级语法错误,少些大括号啊,写中文字符什么的,

              系统通篇扫描一遍但不执行,这个通篇扫描的过程就叫做"语法分析"的过程。

第二步:通篇扫描完之后,系统会进行第二个过程叫“预编译”

第三步:然后才会解释一行执行一行,解释一行执行一行……

也就是说在解释执行之前还有两步,第一步叫"语法分析",第二步叫"预编译",预编译是干什么的呢?学预编译之前先铺垫点,看下面两个效果


第一个效果

函数test()执行,结果显而易见必须能输出a

function test(){

    console.log('a');

}

test(); // a

换一个位置执行,test()执行写在函数声明的上面还能执行吗?还是能执行输出a

test(); // a

function test(){

    console.log('a');

}

JS是解释执行解释一行…执行一行,下面的函数声明还没读出来呢,可结果还能执行,为什么能执行呢?就是因为有“预编译”的过程。


第二个效果

定义一个变量a等于123,在下面访问a必须能输出123

var a = 123;

console.log(a); // 123

输出语句放到变量声明上面能输出吗?能输出,但输出的结果是undefined

console.log(a); // undefined

var a = 123;

如果注释掉变量a声明的语句,变量a没有声明,输出a的结果会报错a is not defined,变量没声明就会出现报错

console.log(a); // Uncaught ReferenceError: a is not defined

// var a = 123;

变量声明写到输出语句下面,在视觉上也是没声明就调用了,为什么不报错而且还能输出undefined呢?这是一个特殊的效果,这个效果也来自于“预编译”的过程。

console.log(a); // undefined

var a = 123;


把上面的两个效果提纯成两句话

第一句:函数声明整体提升

第二句:变量   声明提升

有些人不学预编译只记住这两句话,也能解决一些问题,但解决不了深入的问题,学完预编译明白原理后,就永远不需要记这两句话了。

解释第一句:函数声明整体提升

如果写一个"函数声明"不管写到哪里,系统总会是把函数提到逻辑的最前面,所以不管在哪调用test()函数执行,是在函数声明下面调用,还是在函数声明上面调用,其实本质上都是在函数声明下面调用,系统会把函数声明永远提升到逻辑的最前面,这是函数声明整体提升的意思。

function test(){} // 函数声明整体提升


// -------------[systm]-----------------

test();


// function test(){    // test函数声明,已经被系统提升
// }


test();

第二句:变量声明提升

变量声明也提升,但提升的没有函数声明整体提升的这么邪乎,

变量  声明提升,中间要有空格,一定要分开读叫"变量"、"声明提升"。


 var a = 123;  变量声明 再加 变量赋值,是两步叫"定义变量"

var a = 123; // 变量声明 + 变量赋值

相当于把 var a = 123; 拆成两份,第一份是var a,第二份是a = 123,并且并把 var a; 放到程序的最前面

var a; // 提升到程序最前面


// -------------[systm]-----------------


document.write(a); // 所以即使在这访问变量a,也能打印出的值是undefined

a = 123;

所以这样 var a = 123;  变量声明 + 赋值写在一起也是一样的,就相当于把 var a; 隐式的飞(提升)上去了

document.write(a); // undefined

var a = 123;


但是函数声明整体提升 和 变量声明提升,这两句话太过肤浅了,解决不了所有的问题。

比如下面函数名字是a,变量名是a,然后后访问a,访问的是什么?

console.log(a); // 打印结果是什么?

function a(){

}

var a = 123;

还可以再复杂,函数的参数也是a,函数里面再定义一个变量a,函数里面还定义一个函数a,在函数里执行这个被嵌套函数a(),全部的名字都是a

console.log(a);

function a(a){

     var a = 234;

     var a = function(){}
     
     a();
}

var a = 123;

这是那两句话不能解决的,那两句话只把“预编译过程”的两个现象给抽象出来当做方法来用,那不是知识点,学完"预编译"这些问题就都能轻松解决。

学习预编译前先铺垫点知识点,第一个知识点"暗示全局变量",这个知识点和预编译没什么太大关系但必须得懂。

二、预编译前奏

imply是暗示的意思,男生不太好意思表白,向喜欢的女生暗示一下,

imply global是暗示全局变量的意思。


暗示全局变量的语法是:任何一个变量如果未经声明,就直接赋值了系统不报错,这就生成了一个暗示全局变量。

比如直接访问一个 没有声明的变量a 肯定会报错

console.log(a); // Uncaught ReferenceError: a is not defined

但是变量a未经声明就直接赋值系统不会报错,访问变量a可以输出它的值10,就好像它声明了一样,但和真正的声明不一样,这个叫"暗示全局变量"。

a = 10;

console.log(a); // 10

1、任何变量如果未经声明就赋值,此变量就归全局对象(window)所有

"全局对象"是什么呢?全局对象是window

window是一个对象,对象上面可以加一些属性,全局对象上面加的属性很有意思,比如在window对象上加了一个属性a等于10,访问 window.a 就输出了10,然后单纯访问一个a也输出10

window.a = 10;

console.log(window.a); // 10

console.log(a); // 10

2、一切在全局上声明的任何变量,即使声明了它也归window所有

在全局上声明变量了b等于234,访问b输出234,访问window.b也输出234,一切声明在全局的变量全是window的属性

var b = 234;

console.log(b); // 234

console.log(window.b); // 234


第二条很重要,声明在全局的变量归window所有,解释这句话的意思是window就是全局的域全局的域是什么意思呢?

下面声明一个变量a等于123,这个变量a放在哪里?

1). window是一个对象,他就像一个仓库一样

2). 如果把变量a定义在全局上,就相当于再window上面挂了一个属性a值是123 ,访问变量a的时候会到window上面找变量a

var a = 123; // 全局上定义一个变量a

window{
   a : 123, // window对象上挂里一个属性a等于123
}

console.log(window.a); // 123

console.log(a); // 全局范围上访问变量a其实访问的就是window.a

一切定义在全局范围的变量都归window所有,全局范围上访问变量a其实访问的就是window.a     var a = 123; ===> window.a = 123;  

3、看一个问题  var a = b = 123;

下面是一个"连等"的过程,js里面存在这种的连等连等意思是先把123赋给b,再把b的值赋给a

var a = b = 123;


在一个函数test里面写连等,然后执行函数test()

function test(){

    var a = b = 123;
}

test();

这是一个连续赋值的过程:

赋值一定是"自右向左"的  左 <-- 右  先把123赋给b,再把b赋值给a,

但是这个赋值存在一个现象,就是把123赋值给b的时候,这个b是没有经过声明的,它会先把123赋给b,然后再去声明a,然后在把b的值赋给a,

能理解这个过程吗?因为var a= 123; 的时候,不会先把123赋给a,绝对会先声明a


先把123赋给b,然后在声明变量a,再把b赋值给a,这个过程中连续赋值中导致一个结果,b未经过声明就赋值了,未经声明就赋值这个值归window所有。

归window所有的话,在全局的范围内访问 window.a 输出undefined,访问 window.b 输出123,因为b归window所有

function test(){

    var a = b = 123;
}

test();

console.log(window.a); // undefined

console.log(window.b); // 123

一切声明在全局的变量都是window属性(window就是全局),记住定律window就是全局,在全局范围内定义变量归window所有

看下面的代码,在局部函数里面定义一个局部变量b,在全局window上肯定没有对应的b,访问 window.b 返回undefine

function test(){

     var b = 123; // 局部变量

}

test();

console.log(window.b); // undefined

window就是全局,全局的变量全局的函数都在window身上,在全局去访问这个变量、去访问这个函数,就相当于在window身上去拿这个东西。

var a = 123;
var b = 234;
var c = 345;


// 但凡在全局里定义的变量,在window上有对应的属性
// window {
//     a : 123,
//     b : 234,
//     c : 345
// }


console.log(a); // 123

如果访问这个a,其实就相当于在访问window.a,因为window就是全局 console.log(a) --> console.log(window.a)

接下来是预编译过程

三、预编译过程(四部曲)

1、下面代码打印的结果是什么?

要想知道结果必须要弄明白一个问题,这里面又有函数、又有变量它们都提升。是谁先提升到谁前面、谁覆盖谁,肯定有一个覆盖的问题。

怎么覆盖符合什么样的原则?参数、变量、函数都叫一个名a,这就是预编译发生的奇妙过程。

function fn(a){

    console.log(a);
    
    var a = 123;
    
    console.log(a);
    
    function a(){}
    
    console.log(a);
    
    var b = function(){}
    
    console.log(b);
    
    function d(){}
}

fn(1);

预编译发生在函数执行的前一刻。

也就是函数刚刚要执行之前,预编译就完事了在那等着。然后函数执行的时候,预编译已经发生完了,预编译做的事就是把这些矛盾给调和了。


01/ 预编译第一步:

首先生成了一个AO对象,叫创建AO对象,翻译过来Activation Object叫活跃对象

这个活跃对象(AO)简单的说就是我们理解的作用域,其实叫执行期上下文(这个执行期间上下文是由这个函数执行而产生的存储空间库)。

AO{

}

第一步不重要,重要的在第二步

注意:现在学的是函数的预编译,然后在学全局的预编译,全局的特别简单


02/ 第二步:

找函数里面的形参 和 变量声明,并且将变量声明的名 和 形参的名 作为AO对象的"属性名",属性值统一都是undefined

1). 函数里有形参名 a

2). 变量声明有 var a = 123,a名出现了两次,在AO对象里挂一次就行了

3). 还有 var b = function(){} 也是变量声明,这个虽然是函数,但变量的类型是值决定的,

     但第一步就是找的是变量声明,b是一个变量的声明

4). 他们的值统一全是undefined

     AO{

         a : undefined,

         b : undefined

     }


03/ 第三步:

将"实参值" 和 "形参"相统一,实参是1,形参是a。也就是将实参的值放到形参里面去

    AO{

        a : 1,

        d : undefined

    }


04/ 第四步:

找函数体里面的函数声明

1). 函数声明有 function a(){} 还有 function d(){}

     这个 var b = function(){} 叫函数表达式,函数声明和函数表达式是两回事 

2). 然后将函数声明的名,作为AO对象的属性名也挂到AO里面,

     a的名已经有了不用挂了,但是函数名d还没有,把d挂到AO上面,挂上之后他需要什么样的属性值

     AO{

         a : 1,

         b : undefined,

         d : 

     }

3). 属性值是把 函数体 的整体赋到值里面去,

     这时候 a的属性值 和 d的属性值,相应的变成 a函数体function a(){} 和 d的函数体function d(){}

     也就是把 函数体 全部复制到AO里面

     a原来的值是1,现在被function a(){} 覆盖了,因为第四部的优先级是最高的

     AO{

         a : function a(){},

         b : undefined,

         d : function d(){}

     }


现在,AO对象创建完了:

AO对象中文名是“执行期上下文”,他的作用就是我们理解的作用域。

AO对象创建完之后马上就要执行函数了,因为预编译发生在执行前一刻


执行第一行:

打印console.log(a),到AO对象里面找这个a,a是的值function a(){},所以第一次打印a就是 function a(){}


然后第二行:

执行这句 var a = 123 准确的说是执行 a = 123,

1). 因为在预编译的第二步就找形参和变量声明,并且把形参和变量声明的"名",作为AO对象的"属性名"了,

2). 这个过程其实就是变量提升的过程,变量声明已经把 var a 提升上去了,

     预编译已经把 var a 声明的过程看过了,不用看了,但是 a = 123 这个语句还没有读

3). 这次操作的是AO对象里面的a,把AO对象里a的值改成123

    AO{

        a : 123,

        b : undefined,

        d : function d(){}

     }


第三行 :

再打印console.log(a),到AO里面找a,a已经是123了,打印结果是123


第四行:

function a(){} 这句忽略不看,因为在预编译的时候已经提升上去了,预编译看过的地方就不用再看了


第五行:

console.log(a)  再打印a结果还是123


第六行:

var b = function(){}

var b 已经提升上去了,现在执行剩下 b = function(){}。AO里的b的值undefined,现在b的值变function (){}

    AO{

        a : 123,

        b : function(){},

        d : function d(){}

    }


第七行:

console.log(b) 打印b输出 function(){}


执行结果:

image.png


2、接着,看下面这道题,打印的都是什么?

function test(a ,b){

    console.log(a);
    
    c = 0;
    
    var c;
    
    a = 3;
    
    b = 2;
    
    console.log(b);
    
    function b() {}
    
    function d() {}
    
    console.log(b);

}

test(1);

预编译四部:


第一步:生成AO对象

AO{

}


第二步:找"形参"和"变量"的名,把形参名a、b和变量名c,作为AO属性名,值为undefined

AO{

     a : undefined,

     b : undefined,

     c : undefined

}


第三步:实参值和形参相统一,a变成1就完事了

AO{

     a : 1,

     b : undefined,

     c : undefined

}


第四步:

找函数声明,函数声明有两个,一个声明了b、一个声明了d,

把b和d都放到AO上,b已经有了把d放上来,值是函数体,把值全部赋上来,

b的值原来是undefined现在换成function b(){}

AO{

     a : 1,

     b : function b(){},

     c : undefined,

     d : function d(){}

}


AO完成了,执行:


第一行:console.log(a);  打印a输出 1


第二行:执行c = 0; ,c的原来是undefined,现在变成0

AO{

     a : 1,

     b : function b(){},

     c : 0,

     d : function d(){}

}


第三行:var c;   不用看了,已经提升了


第四行:a = 3;   a的值由原来是1,现在等于3了

AO{

     a : 3,

     b : function b(){},

     c : 0,

     d : function d(){}

}


第五行:b = 2;  b刚才是函数体,现在变成 2

AO{

     a : 3,

     b : 2,

     c : 0,

     d : function d(){}

}


第六行:console.log(b);   访问b输出2


第七行:function b() {} 这句忽略,都提升上去了


第八行:function d() {} 这句忽略,都提升上去了

    

第九行:onsole.log(b);  再访问b还是输出 2


执行结果:1

                 2

                 2

3、再练练

先口算一下,第一行 console.log(a) ,a打印什么?

其实可以记住一个规律,一旦有重名的,比如下面这道题有变量a、有函数a,在第一行访问的还是a,那a一定是输出函数体,

因为函数在预编译的第四步,函数权限最高,不管a之前填什么值,第四步都被函数体给覆盖了。

function test(a ,b){

    console.log(a);

    console.log(b);

    var b = 234;

    console.log(b);

    a = 123;

    console.log(a);

    function a(){};

    var a;

    b = 234;

    var b = function(){};

    console.log(a);

    console.log(b);
}

test(1);

预编译:


第一步:AO{}


第二步:

找形参和变量声明。

没有变量声明就两个形参a、b,

虽然函数里面一大堆,声明了变量a,声明了变量b,但就是a和b这两个

AO{

     a : undefined,

     b : undefined

}


第三步:

形成实参相统一,a变成1拉倒

AO{

     a : 1,

     b : undefined

}


第四步:

找函数声明,

这里面只有一个函数声明function a() {},

这个叫 var b = function(){} 函数表达式,只有函数声明能提升,函数表达式不能提升

a的值原来是1,现在变成function a(){}

AO{

     a : function a() {},

     b : undefined

}


开始执行:


第一行:console.log(a) 打印 function a() {}


第二行:console.log(b) 打印 undefined


第三行:

var b = 234

var b不用看了,b = 234,

上AO里找b,然后AO里的b变成234

AO{

     a : function a() {},

     b : 234

}


第四行:console.log(b) 打印 234


第五行:a = 123  AO里的a变成123

AO{

     a : 123,

     b : 234

}


第六行:console.log(a) 打印 123


第七行:function a(){}   这行不看了


第八行:var a;   这行也不用看了


第九行:b = 234;  AO里面的b还是234

AO{

     a : 123,

     b : 234

}


第十行:var b = function (){}   AO里的b变成函数 function (){}

AO{

     a : 123,

     b :  function (){}

}


第十一行:console.log(a)   访问a还是打印123


第十二行:console.log(b)   访问b打印函数 function (){}


输出:function a() {}

          undefined

          234

          123

          123

          function(){}      // 注意这里是函数没有名字的

4、全局的预编译

上面是函数体系里的预编译,预编译不只发生在函数体系里,预编译还发生在全局。

在全局声明一变量a等于123,在声明变量a上面访问这个a行不行?输出结果是undefined,因为变量提升了

console.log(a); // undefined

var a = 123;

全局里面已经有一个变量a了,在全局里面再定义一个a函数,在a函数声明a变量声明上面打印a返回的是什么? 

返回的是a的函数体 ƒ a(){} ,因为全局也有预编译环节

console.log(a); // ƒ a(){}

var a = 123;

function a(){}

四、全局预编译

全局预编译环节和函数里的一样,只不过少了一个环节,全局没有参数所以就三步,但是全局的第一步会发生点变化,变化是生成了一个叫GO的对象。

1、看一个小例子

var a = 123;

function a(){}

console.log(a); // 123

预编译:


    第一步:

    生成GO{}


    第二步:

    GO对象里面,a的属性值是undefined

    GO{

         a : undefined

    }


    然后直接跳到第四部:

    a函数体整体提升,a属性值变成function a(){}

    GO{

         a : function a(){}

    }


执行:


    第一句:

    var a = 123

    a = 123,GO里a的属性值就变成123了

    GO{

         a : 123

    }


    第二句:function a(){}   提升了不用看


    第三句:console.log(a)  访问a输出123

2、window是什么?

window就是GO

GO是一个对象,window也是一个对象,这两个对象就是一个东西,GO就是window,是一个人的两个名,

所以我们定义完的所有变量都要放到GO里去储存,放到到GO里面去储存就是放到window里面去存储,所以window上面自然就有了我们定义变量的引用。


GO就等于window(window.a == GO.a

下面GO.a和window.a是一回事,直接打印 window.a 和直接打印 a 两条语句是一样的,访问a到GO里去找,访问window.a更直接了,直接到window里去取

// GO{
//     a : 123
// }

var a = 123;

function a(){}

console.log(window.a);

console.log(a);

任何全局变量都是window上的属性,换句话说全局变量肯定是GO里面的东西,所以一定是window的属性。

3、再和暗示全局变量的知识点在结合一下

暗示全局变量说,一个变量如果没经声明就赋值了,也要归window所有。

换句话说如果这个变量没经声明就赋值了,是放到GO里去预编译的。


比如下面代码中,b就是一个暗示全局变量

1). 执行test()的时候生成一个test的AO,但是生成AO的时候发生一个有意思的现象,

2). AO能把a拿出来,但是对b无动于衷拿不出来,

3). 所以这就涉及到暗示全局变量的知识,b没声明就赋值了,b归全局的GO所有。

// GO{
//     b : 123
// }

function test(){
    var a = b = 123;
}

test();

// AO{
//     a : undefined
// }

既然b归GO所有,GO又是window,所以在函数里打印window.b 是可以输出123的,因为window是GO可以调用window.b 

function test(){

    var a = b = 123;

    console.log(window.b); 

}

test();

但是要在函数里访问window.a,GO里面没有a输出的是undefined

function test(){

    var a = b = 123;

    console.log(window.a); // undefined

}

test();

4、先生成GO,还是先生成AO?

先生成的是GO

函数的预编译发生在函数的执行的前一刻,

那全局的GO一定发生在全局刚刚要执行的前一刻,script标签它就是全局,把script标签抽象一下也相当于一个大的function,所以一定是先生成GO

5、下面看一个真正恶心的

console.log(test);

function test(test) {

    console.log(test);

    var test = 234;
    
    console.log(test);
    
    function test(){
    }

}

test(1);

var test = 123;

GO预编译:


第一步:生成GO{}


第二步:

变量var test提升,属性是undefined

GO{

       test: undefined

}


第三步:

test函数整体提升,test属性变成test函数体(函数体里面有点复杂,用……代替)

GO{

     test: function test(){

         ......

     }

}


开始执行:


第一行:console.log(test),在全局输出的是GO里面的test函数体 function() test{......}


第二行:test函数提升了就不看了


第三行:

开始执行 test(1) 函数,

1). 在执行函数的时候,有执行的前一刻,先生成AO,

2). AO里面是变量test提升,值是undefined

AO{

      test: undeined

}


test函数AO第三步:

形参实参相统一,test属性值变成1

AO{

     test: 1

}


test函数AO第四步:

还有一个test函数整体提升,AO对象的test属性值变成test函数体

AO{

     test: function test(){}

}


执行


test函数里第一行:

 console.log(test)   输出函数体function test (){}

这时候有一个小问题,GO里面有test,AO里面也有test,打印谁里面的test?

1). 打印AO里面的test,几层嵌套关系,内层自己有的就用内层自己的,内层自己没有再往上找,

2). 这个时候AO和GO会形成一个链式关系,这个链式关系的访问顺序是可近的来,

     AO上有就用AO的,AO上没有看GO上有没有

     如果AO上没有test,会义无反顾的到GO上去找test

所以这里打印AO里面的test函数体 function test(){}


test函数里第二行:

test = 234 ,test的值变成234

AO{

     test: 234

}


test函数里第三行: console.log(test)  再访问test输出234


最后结果:

    image.png

6、由浅入深,再看一个简单的

下面打印结果为什么是100

var global = 100;

function fn(){
    console.log(global);
}

fn();

全局预编译


第一步:一开始生成GO{}


第二步:

GO里面只有global,值是undefined

GO{

      global : undefined

}


第三步:

函数fn整体提升,值是函数体

GO{

    global : undefined,

    fn : function fn(){

        console.log(global);

    }

}


开始执行:


第一行:global的值变成100

GO{

    global : 100,

    fn : function fn(){

        console.log(global);

    }

}


第二行:fn函数已经提升上去不看了


然后:fn()执行,


执行fn()函数:生成fn函数对应的AO,AO里面什么都没有空的

AO{

}


执行fn函数里面的语句:console.log(global)

1). 首先到fn自己的AO上面,fn自己AO找什么都没有,

2). AO自己没有往上找,往上到GO上去找,

3). GO上有global:100,所以打印GO里面的global属性的值,输出100


如果fn函数里定义了一个变量global变量值是200呢?AO里有就不会访问GO的了,可近的来近的优先,打印输出200,

var global = 100;

function fn(){

    var global = 200;
    
    console.log(global);

}

fn(); // 200

7、加大难度,真正变态的

global = 100;

function fn(){

    console.log(global);

    global = 200;

    console.log(global);
    
    var global = 300;

}

fn();

var global;

console.log(global);

第一步:先生成GO对象

GO{

}


第二步:找变量声明,变量global提升值是undefined

GO{

    global : undefined

}


第三步:函数fn整体提升

GO{

    global : undefined,

    fn : function(){......}

}


执行


第一句:global的值变成100

GO{

    global : 100,

    function : function(){...}

}


第二句:执行fn()函数


AO预编译:

第一步:生成AO对象

第二步:fn函数里面找形参和变量声明,形参没有但是有变量声明global

AO{

    global : undefined

}

第三步:没有实参,不与形参统一

第四部:没有可提升的函数


执行fn函数


第一行:console.log(global) 打印undefined

             有fn函数有字节的global变量,值是undefined,所以找的是fn函数自己的global


第二行:global = 200,AO的global变200

AO{

    global : 200

}


第三行:console.log(global) 打印global输出 200


第四行:global = 300,AO内的global的值变300

AO{

    global : 300

}


执行全局:console.log(global)  输出100


一定要记住AO预编译的四句语法,这四条规律从语文的语法上、结构上没有任何问题。

8、再加大难度

function test(){

    console.log(b);

    if(a){
        var b = 100;
    }

    console.log(b);

    c = 234;

    console.log(c);

}

var a;

test();

a = 10;

console.log(c);

01/

GO{

    a: undefined,

    test: function test(){......}

}


02/

test()函数执行的时候,生成一个AO

AO{

}


03/

找AO里面的东西,找形参变量声明,

1). 不看if条件,if执行的时候才判断,if(a)决定if里面能不能执行,预编译根本就不看

2). 有变量var b,直接提升

刚才的语法是完全没有问题的

AO{

    b: undefined

}


04/ 

执行test函数里第一句:console.log(b);  打印undfined


05/ 

执行test函数:if(a)

1). AO里面没有变量a

2). 变量a到GO里面找,此时的a是undefined,

3). a的值是undefined,判断体里面 b = 100 不能走,


06/

执行text函数:console.log(b);  打印b还是undefined


07/

然后执行:c = 234;  AO里面没有c,c是暗示全局变量归GO

GO{

    a: undefined,

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

    C: 234

}


08/

text函数里面打印c:console.log(c);  AO里没有变量c,往上到找GO里面找c输出234


09/

test();  执行完,


10/

a = 10 GO里面a属性的值变10

GO{

    a: 10,

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

    C: 234

}


11/

console.log(c); 最后访问c是234


结果:undefined

          undefined

          234

          234

五、百度2013年的两个笔试题

第一个题

function bar(){
    return foo;
    foo = 10;
    function foo(){
    }
    var foo = 11;
}

console.log(bar());  // foo(){}

1. 在函数内最上面有return foo,就相当于在最上面console.log(foo),

2. 在最上面 return foo 有一条谨记,如果下面有函数声明的名是foo的话,就什么也不用看了,一定打印的是函数,

3. 因为函数提升是在第四步权限最高。

所以这题打印函数体


第二个题

1. return foo在最下面也有一条谨记,

2. 只要这个foo名的上面被赋过值,直接输出这个值就好了,

3. 因为它执行顺序是最下面的,不管怎么提升覆盖,return上面foo=11,foo就输出11。

console.log(bar()); // 11

function bar() {
    foo = 10;
    function foo() {
    }
    var foo = 11;
    return foo;
}



Leave a comment 0 Comments.

Leave a Reply

换一张