首页 > 技术文章 > 变量提升和执行环境对象

zhansu 2016-08-20 17:16 原文

相关知识点:《单页Web应用》28页,高程111页以及underscore源码

  高程111页讲过函数声明和函数表达式的唯一区别:解析器会率先读取函数声明,并使其在执行任何代码前可用;至于函数表达式,则必须等到解析器执行到它所在的代码行,才会真正被解释执行。看下面两个例子:

alert(sum(10,10));    //20
    
function sum(num1, num2){
    return num1 + num2;
}     
 alert(sum(10,10));    //causes an error
    
 var sum = function(num1, num2){
       return num1 + num2;
 }; 

 

  然后我在看underscore源码的时候遇到了困惑的地方,提炼后的源码如下:

(function() {
    var root = this;
    var _ = function(obj) {
        return new wrapper(obj);
    };
    root['_'] = _;
    .....
    var wrapper = function(obj) {
        this._wrapped = obj;
    };

}).call(this);

  困惑点:函数_先定义的,函数wrapper后定义的,那函数_执行的时候,岂不是在返回wrapper实例时,因为无法获得wrapper函数内容报错了。困惑了我好长时间。

  现在疑惑解决了,其实本身不是应该困惑的东西。咱们假设是按照常规的<script>方式引入underscore.js脚步,我们自己写的js代码肯定是放在了underscore.js的后面,所以当浏览器读到我们自己写的代码的时候,underscore的所有代码已经执行了一遍。执行的具体细节,见下面。

  单页Web应用也有相关的知识点:JS引擎在进入作用域时,会对代码分两轮处理。第一轮,初始化变量;第二轮,执行代码。在第一轮,JS引擎分析代码,并做了以下3件事情:

  (1)声明并初始化函数参数。

  (2)声明局部变量,包括将匿名函数赋值给一个局部变量(注:即函数表达式,此时是undefined),但并不初始化它们。

  (3)声明并初始化函数((注:确切的说是函数声明函数)。

  对于第二轮,该书只是说执行代码,我认为不只是执行代码,第二轮还包括初始化第一轮未初始化的局部变量和函数表达式。

  

  可以这么理解,在浏览器读到咱们自己写的代码的之前,underscore的所有代码已经执行了一遍,函数wrapper在执行第二轮中已经完成了初始化,所以当浏览器读到咱们自己写的调用函数_时,函数_已经可以引用wrapper函数了。

  等等,函数_怎么能引用wrapper函数呢,在underscore.js的立即执行函数完成后,局部变量wrapper已经被垃圾回收机制给回收了,怎么还能被_应用呢。嗯,闭包机制嘛。真正使我困惑是:浏览器怎么知道在初始化_的时候,_内部引用了一个外部函数,根据我以前的理解,初始化函数时,浏览器不会执行函数内部的内容,既然不执行,怎么知道内部有个函数。现在自己猜测在初始化函数的时候,浏览器已经读取了函数内部的内容,并对内容里的变量进行了一次作用域链搜索,不过这个时候搜索到的wrapper应该是undefined吧,因为wrapper的初始化在_初始化的后面,直到调用_的时候,wrapper才真正完全被_引用了。目前先如此理解吧。还要抓紧时间看单页的知识。

 

  

推荐阅读