JavaScript深度学习(三)—— 预编译、作用域、作用域链

预编译

预编译前奏

  1. 函数声明整体提升
  2. 变量 声明提升(只声明提升,直接赋值不提升)

函数预编译

  1. 创建A0对象 Activation Object
  2. 找形参和变量声明,将形参和变量声明作为AO属性名,值为undefined
  3. 将实参值和形参统一
  4. 在函数体内找函数声明(把函数声明的名作为属性值加入AO),值赋予函数体
  5. 执行
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);
//执行过程
1.创建AO
A0{
    
}
2.找实参和变量声明,作为AO属性名,值为undefined
AO{
    a:undefined,
    b:undefined
}
3.将实参形参统一
AO{
    a:1,
    b:undefind
}
4.找函数声明,将值赋予函数体
AO{
    a:1,
    b:undefined,
    d:
}
AO{
    a:function a(){},
    b:undefined,
    d:function(){}
}
5.执行
打印 function a(){}
a=123
打印 123
打印 123
b=function(){}
打印 function(){}

全局预编译

+ 1.创建G0对象 Global Object (window)
+ 2.找形参和变量声明,将形参和变量声明作为AO属性名,值为undefined
+ 3.找函数声明(把函数声明的名作为属性值加入AO),值赋予函数体
+ 4.执行

预编译练习题

1.
        function bar() {
            return foo;
            foo = 10;

            function foo() {}
            var foo = 11;
        }
        console.log(bar());
2.
        console.log(bar());
        function bar() {
            foo = 10;

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

作用域&作用域链

执行期上下文

当函数执行的前一刻,会创建一个称为执行期上下文的内部对象(就是前面说的的AO)。

一个执行期上下文定义了一个函数执行时的环境。

函数每次执行时对应的执行期上下文都是独一无二的。

所以多次调用一个函数会导致创建多个执行期上下文。

当函数执行完毕,他创建的执行期上下文被销毁。

解释:我们每次执行函数的前一刻系统都会创建 AO 对象,
这个 AO 就定义了变量提升 函数提升等,
比如说定义了一个函数 test,当你调用他的时候他会创建一个 AO,
当你再次调用的时候他还会再次创建另一个 AO,
AO只是一个临时的存储对象,当函数执行完毕时,AO 就会被销毁。 

[[scope]] -->作用域

每个JavaScript函数都是一个对象,对象中有些属性我们可以访 问,但有些不可以,这些属性仅供 JavaScript 引擎存取,[[scope]]就是其中一个, [[scope]]指的就是我们所说的作用域,其中存储了运行期上下文的集合。

作用域链

[[scope]]中所存储的执行期上下文对象的集合,这个集合呈链式链 接,我们把这种链式链接叫做作用域链。在哪个函数里边查找变量,就去哪个函数的 作用域链顶端依次向下查找

function a(){    
    function b(){ 
        function c(){ 
        }
        c();
    }
    b();
}
a();
解析: 
a 函数被定义:a. [[scope]]------->0:GO 
a 函数执行前:a. [[scope]]------->0:a 的 AO   
                                  1:GO 

b 函数被定义:b. [[scope]]------->0:a 的 AO   
                                  1:GO 
b 函数执行前:b. [[scope]]------->0:b 的 AO 
                                  1:a 的 AO
                                  2:GO 
                                  
c 函数被定义:c. [[scope]]------->0:b 的 AO
                                  1:a 的 AO
                                  2:GO
c 函数执行前:c. [[scope]]------->0:c 的 AO
                                  1:b 的 AO 
                                  2:a 的 AO  
                                  3:GO 

闭包的解决方法

在形成闭包的函数外面加一层立即执行函数。

function test(){ 
    var arr = [];
    for(var i = 0;i < 10;i ++){
    <!--此处为解决方法-->
       (function (j){
            arr[j] = function (){
            document.write(j + " "); 
            }         
        }(i))     
    }    
    return arr; 
} 
var myArr = test(); 
for(var j = 0;j < 10;j ++){ 
    myArr[j](); 
}
  • 如果不加立即执行函数,本想打印出 0123456789,结果会打印十个10。

    原理:循环里面的函数会在循环结束后执行,此时i的值已经变成了10.

  • 我们在 for 循环里加一个立即执行函数。

    原理: 形参传一个 j 进去,实参传 i,把里边 的 i 换成 j 就完美解决了。

    每次形成闭包所绑定的值j都是固定的,就这样绑定十次,自然不会出错。
发布了6 篇原创文章 · 获赞 0 · 访问量 81

猜你喜欢

转载自blog.csdn.net/djc_18706636375/article/details/105626089
今日推荐