欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

JavaScript作用域链实例详解

程序员文章站 2023-11-20 18:53:34
本文实例讲述了javascript作用域链。分享给大家供大家参考,具体如下: 跟其他语言一样,变量和函数的作用域揭示了这些变量和函数的搜索路径。对于javascript而...

本文实例讲述了javascript作用域链。分享给大家供大家参考,具体如下:

跟其他语言一样,变量和函数的作用域揭示了这些变量和函数的搜索路径。对于javascript而言,理解作用域更加重要,因为在javascript中,作用域可以用来确定this的值,并且javascript有闭包,闭包是可以访问外部环境的作用域的。
每一个javascript的函数都是function对象的一个实例,function对象有一个内部属性[[scope]],这个属性只能被javascript的引擎访问。通过[[scope]]属性可以访问函数的作用域链,从而可以搜索变量和函数,判断变量和函数位于作用域链中的哪一个活动对象中。

简单的作用域链

当一个函数被创建的时候,因为函数是function对象的一个实例,因此也会有[[scope]]这个内部属性,scope属性指向一个作用域链,作用域链中默认至少包含一个全局对象变量。

function compare(value1, value2){
  if (value1 < value2) {
    return -1;
  } else if (value1 > value2) {
    return 1;
  } else {
    return 0;
  }
}
var result = compare(5, 10);

以上代码先定义了一个compare()函数,然后在全局作用域中调用了这个函数。 在创建compare()函数的时候,该函数的作用域链如下图所示:

JavaScript作用域链实例详解

compare()函数在全局作用域中被调用执行的时候,javascript引擎会创建一个运行时上下文(execution context)的内部对象,一个运行时上下文定义了一个函数执行时的环境。函数诶次执行时的运行时上下文都是不同的,因此多次调用就会导致多个运行时上下文的创建与销毁。

每个运行时上下文都有自己的作用域链,用于变量和函数这些标识符的解析。

运行时上下文在函数调用执行时被创建,在函数执行完毕的时候被销毁。在运行时上下文创建的时候,首先会复制该被调用函数的[[scope]]属性中的对象,然后一个活动对象(作为变量对象使用)会被创建并推入运行时上下文作用域链的前端。对于这个例子中compare()函数的运行时上下文而言,其作用域链包含两个变量对象:compare()的活动对象(activation object of compare())与全局变量对象(global object)。

对于简单的作用域链,就是这样了,但是有闭包的情况会有所不同。

JavaScript作用域链实例详解

闭包的作用域链

//step1: define createcomparisonfunction
function createcomparisonfunction(propertyname){
  return function(object1, object2){
    var value1 = object1[propertyname];
    var value2 = object2[propertyname];
    if (value1 < value2) {
      return -1;
    } else if (value1 > value2) {
      return 1;
    } else {
      return 0;
    }
  };
}
//step2: call createcomparisonfunction
var compare = createcomparisonfunction("name");
//step3: call compare
var result = compare({name: "nicholas"}, {name: "gerg"});
//step4: dereference closure for recycle memory
compare = null;

我们分下列几个步骤来图解作用域链:

step1: 定义createcomparisonfunction;

JavaScript作用域链实例详解

在创建createcomparisonfunction函数之后,createcomparisonfunction可以被调用了,因此一个createcomparisonfunction的function对象被保留下来;
此时内存中保留对象:

1. global object

2. createcomparisonfunction对象 & scope chain

step2: 执行createcomparisonfunction;

JavaScript作用域链实例详解

在执行createcomparisonfunction的过程中,首先会创建createcomparisonfunction的运行时上下文对象 + scopechain + 活动对象,其次会创建一个闭包(匿名函数),
函数执行时内存中保留对象:

1. global object
2. createcomparisonfunction的function对象 + scope chain
3. createcomparisonfunction的运行时上下文对象 + scope chain
4. createcomparisonfunction的活动对象
5. closure(anonymous)的function对象 + scope chain

在执行完createcomparisonfunction之后,createcomparisonfunction的运行时上下文对象+scopechain会被销毁,但是createcomparisonfunction的活动对象因为被closure(anonymous)对象的scopechain所引用,因此不会被销毁。

函数执行完内存中保留对象:

1. global object
2. createcomparisonfunction的function对象 + scope chain
3. closure(anonymous)的function对象 + scope chain
4. createcomparisonfunction的活动对象

对比step1, step2在执行完之后,增加了两个对象:

1. closure(anonymous)的function对象 + scope chain
2. createcomparisonfunction的活动对象

这个是因为执行createcomparisonfunction所产生的闭包被compare所引用了,而这个闭包函数的scope chain又引用了createcomparisonfunction的活动对象,因此内存增加了这两个对象。

step3: 执行compare;

JavaScript作用域链实例详解

在执行在执行闭包(compare)的时候,首先会创建闭包的运行时上下文对象 + scopechain + 活动对象,然后执行闭包。

闭包执行时内存中保留对象:

1. global object
2. createcomparisonfunction的function对象 + scope chain
3. 闭包closure(anonymous)的function对象 + scope chain
4. createcomparisonfunction的活动对象
5. 闭包closure(anonymous)的运行时上下文 + scope chain
6. 闭包closure(anonymous)的活动对象

执行完闭包closure(anonymous)之后,闭包closure(anonymous)的活动对象会被销毁,闭包closure(anonymous)的运行时上下文 + scope chain也会被销毁。

闭包执行完内存中保留对象:

1. global object
2. createcomparisonfunction的function对象 + scope chain
3. 闭包closure(anonymous)的function对象 + scope chain
4. createcomparisonfunction的活动对象

对比step2,step3执行完毕之后,内存中保留的对象没有增加,这就是正常执行一个函数的情况。

在正常情况下,执行完一个函数之后,内存中保留的对象应该与执行前一样的。

执行闭包因为没有引入新的引用,所以内存中保留的对象保持了一致。

那么问题来了,createcomparisonfunction的活动对象到底怎么才能被销毁呢?

我们首先看createcomparisonfunction的活动对象存在的原因是闭包closure(anonymous)的function对象的scope chain引用了createcomparisonfunction的活动对象,其目的是因为闭包中需要访问propertyname这个createcomparisonfunction的活动对象中的属性。

如果闭包closure(anonymous)的function对象被销毁之后,createcomparisonfunction的活动对象因为没有被任何对象引用,也会被销毁。

step4解除了compare对闭包的引用,就使得闭包没有被任何对象引用,闭包销毁,从而使得createcomparisonfunction的活动对象因为没有被任何对象引用,也会被销毁,这样就回收了这些对象所占用的内存。

使用闭包的问题就是内存消耗会比一般的函数大,因为要保存额外的活动对象,所以在不需要使用闭包的时候,需要将闭包解引用,回收额外的活动对象所占用的内存。

执行完step4之后内存中保留的对象:

1. global object

2. createcomparisonfunction的function对象 + scope chain

对比step1,step4在执行完毕之后,没有额外的对象被保留在内存中,引用闭包所产生的额外对象都得到了回收。

更多关于javascript相关内容感兴趣的读者可查看本站专题:《javascript面向对象入门教程》、《javascript错误与调试技巧总结》、《javascript数据结构与算法技巧总结》、《javascript遍历算法与技巧总结》及《javascript数学运算用法总结

希望本文所述对大家javascript程序设计有所帮助。