【转】【总结】函数上下文、this、调用栈、作用域链和活动对象等

函数上下文

关于函数内参数

http://www.cnblogs.com/ssh-007/p/5064699.html

执行上下文

较详细:http://www.shsxt.com/it/java/537.html

https://github.com/cbbfcd/all-of-javascript/blob/master/deep-into-javascript/this.mdown

var name = 'The Window';
var obj = {
   name: 'My obj',
   getName: function() {
        return this.name;
    }
};

// 猜测下面的输出和背后的逻辑(非严格模式下)
object.getName();						//'My obj'
(object.getName)();						//'My obj'
(object.getName = object.getName)();	//'The Window'

在函数被调用的时候

1.会创建一个 执行环境及相应的作用域链

2.使用arguments以及其他命名参数的值来 初始化函数的活动对象 (activation object,简称AO)。

3.在作用域上,函数会 逐层复制自身调用点的函数属性,完成作用域链的构建,直到全局执行环境。

例如:

function compare(value1, value2) {
	return value1 - value2;
}

var result = compare(5, 10);

图片: http://shsxt.shsxt.com/2017/1010/20171010091240828.png

  1. 在这段代码中,result通过var进行了变量声明提升,compare通过function函数声明提升,在代码执行之前我们的全局变量对象中就会有这两个属性。

  2. 每个执行环境都会有一个变量对象,包含存在的所有变量的对象。全局环境的变量对象始终存在,而像compare函数这样的局部环境的变量对象,则只在函数执行的过程中存在。当创建compare()函数时,会创建一个预先包含全局变量对象的作用域链,这个作用域链保存在内部的[[Scope]]属性中。

  3. 在调用compare函数时,会为它创建一个执行环境,然后复制函数的[[scope]]属性中的对象构建起执行环境的作用域链。此后,又有一个活动对象(变量对象)被创建并被推入执行环境作用域链的前端。此时作用域链包含两个变量对象:本地活动对象和全局变量对象。显然,作用域链本质上是一个指向变量对象的指针列表,它只引用但不包含实际的变量对象。

  4. 当访问函数的变量时,就会从作用域链中搜索。当函数执行完毕后,局部活动对象就会被销毁,内存中仅保存全局作用域。

闭包的情况

但是,闭包的情况有所不同,在一个函数内部定义的函数会将外部函数的活动对象添加到它的作用域链中去。

function create(property) {
  return function(object1, object2) {
      console.log(object1[property], object2[property]);
  };
}

var compare = create('name');
var result = compare({name: 'Nicholas'}, {name: 'Greg'}); // Nicholas Greg

// 删除对匿名函数的引用,以便释放内存
compare = null;

解析:

  1. 在匿名函数从create()中被返回后,它的作用域链被初始化为包含create()函数的活动对象和全局变量对象。

  2. 这样,该匿名函数就可以访问create中定义的所有遍历,更为重要的是当create()函数执行完毕后,其作用域链被销毁,但是活动对象不会销毁,因为依然被匿名函数引用。

  3. 当匿名函数别compare()被销毁后,create()的活动对象才会被销毁。

闭包与this

我们知道 this对象是基于函数的执行环境绑定的 ,在全局的时候,this等于window,而当函数作为某个对象的方法调用时,this等于那个对象。

不过,匿名函数的执行环境具有全局性,因此this常常指向window。

var name = 'The Window';
var obj = {
   name: 'My obj',
   getName: function() {
       return function() {
            return this.name;
       };
   }
};

obj.getName()(); // 'The Window'

前面说过,函数在被调用时会自动取得两个特殊变量: this和arguments,内部函数在搜索这两个变量时,只会搜索到其活动对象,所以永远不会访问到外部函数的这两个变量。如果我们想满足需求,可以固定this对象并更名即可。

var name = 'The Window';
var obj = {
   name: 'My obj',
   getName: function() {
       // 固定this对象,形成闭包,防止跟特殊的this重名
       var that = this;
       return function() {
          return that.name;
     };
  }
};

obj.getName()(); // 'My obj'

改变this指向还可以使用 apply、call 函数

this的绑定

上面对this的说明可以说是非常的浅薄了,现在我们详细的整理下this关键字.

this是函数作用域的特殊关键字,进入函数执行环境时会被自动定义,实现原理相当于 /自动传递调用点的对象\ :

var obj = {
   name: 'Nicholas',
   speak() {
        return this.name;
   },
   anotherSpeak(context) {
       console.log(context.name, context === this);
   }
};

obj.name;    //'Nicholas'
obj.speak();    // 'Nicholas'
obj.anotherSpeak(obj);    // 'Nicholas' true

可以看到,我们在anotherSpeak()中传递的context就是obj,也就是函数调用时,执行环境的this值。引擎的这种实现简化了我们的工作,自动传递调用点的环境对象作为this对象

我们要注意的是this只跟调用点有关,而跟声明点无关。这里你需要知道 调用栈,也就是使我们到达当前执行位置而被调用的所有方法的栈,即所有嵌套的函数栈。

this的四种绑定。

看文章吧。。

posted on 2017-10-17 11:35  xfh0192  阅读(208)  评论(0编辑  收藏  举报

导航