JS中的new和做用域链

new的运行机制

当代码new Animal("cat")执行时:html

  1. var obj=Object.create(Animal.prototype);前端

  2. 传入cat参数,构造函数Animal执行。同时构造函数内部的this被指定为obj。函数

  3. 若是构造函数返回了一个“对象”,那么这个对象就是new出来的结果。若是构造函数没有返回对象(即返回一个非对象值,例如数值,或者无返回值),那么new出来的结果为obj对象。通常状况下构造函数不返回值,除非你想要覆盖正常建立的对象(即obj)。this

例如:spa

function A(name){
  this.name=name;
  return 3;
}
var new1=new A("aa");
new1;//A {name: "aa"}

function B(name){
  this.name=name;
  return {};
}
var new2=new B("aa");
new2;//new2为一个空对象。

做用域链

JS权威指南中有一句很精辟的描述: “JavaScript中的函数运行在它们被定义的做用域里,而不是它们被执行的做用域里。”简单来讲,就是函数被调用时,它是运行在当时定义该函数时的环境中的。prototype

定义函数

定义函数a的时候,js解释器会将函数a的做用域链(scope chain)设置为定义a时所在的“环境”,并为a添加scope属性,a.scope=a的做用域链。若是a定义在全局环境,那么scope chain中只有window对象。code

调用函数

函数被调用时,会建立一个活动对象(call object)(也就是一个对象), 而后把全部函数a的局部变量和函数定义添加为该活动对象的属性, 并将该活动对象添加到a的做用域链的最顶端,此时a的做用域链包含2个对象:a的活动对象和window对象。htm

案例解析

为何调用func1(10)和func2(10)时,引用到了两个不一样的i?对象

function outerFn(i, j) {
    var x = i + j;
    return function innerFn(x) {
        return i + x;
    }
}
var func1 = outerFn(5, 6);
var func2 = outerFn(10, 20);
alert(func1(10)); //返回15
alert(func2(10)); //返回20

调用outerFn (5, 6)的时候定义了一个新的函数对象innerFn,而后该函数对象成为了outerFn函数的活动对象的一个属性。这时innerFn的做用域链是由outerFn的活动对象和全局对象组成的.。这个做用域链存储在了innerFn函数的内部属性[[scope]]中,而后返回了该函数,变量func1就指向了这个innerFn函数。blog

在func1被调用时,它自身的活动对象被建立,而后添加到了[[scope]]中存储着的做用域链的最前方。这时的做用域链才是func1函数执行时用到的做用域链。从这个做用域链中,能够看到变量‘i’的值实际上就是在执行outerFn(5,6)时产生的活动对象的属性i的值。下图显示了整个流程。

clipboard.png

下图是func2执行时的状况。由于在定义func1和func2时,函数outerFn中产生过两个不一样的活动对象,因此才致使调用func1(10)和func2(10)时,引用到了两个不一样的i。

clipboard.png

一个活动对象在函数执行的时候建立,同时被添加到该函数的做用域链的最前端。当函数执行完毕时,活动对象会被从该做用域链上删除。可是该活动对象是否会被垃圾回收器销毁,还要看其余地方是否还有使用到该活动对象。

参考自:
new运算符
Javascript做用域原理
[译]JavaScript:函数的做用域链

相关文章
相关标签/搜索