JavaScript作用域链实例详解


Posted in Javascript onJanuary 21, 2019

本文实例讲述了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实现的网页局布刷新效果
Dec 01 Javascript
下载网站打开页面后间隔多少时间才显示下载链接地址的代码
Apr 25 Javascript
jQuery lazyLoad图片延迟加载插件的优化改造方法分享
Aug 13 Javascript
js获取url中指定参数值的示例代码
Dec 14 Javascript
jQuery实现鼠标经过提示信息的地图热点效果
Apr 26 Javascript
jQuery插件kinMaxShow扩展效果用法实例
May 04 Javascript
JavaScript进阶练习及简单实例分析
Jun 03 Javascript
JSONP跨域请求
Mar 02 Javascript
Node.js  REPL (交互式解释器)实例详解
Aug 06 Javascript
vue的全局提示框组件实例代码
Feb 26 Javascript
深入分析element ScrollBar滚动组件源码
Jan 22 Javascript
WebPack工具运行原理及入门教程
Dec 02 Javascript
Jquery的Ajax技术使用方法
Jan 21 #jQuery
js变量声明var使用与不使用的区别详解
Jan 21 #Javascript
Vue中Axios从远程/后台读取数据
Jan 21 #Javascript
vue项目中实现的微信分享功能示例
Jan 21 #Javascript
在vue项目中引入highcharts图表的方法
Jan 21 #Javascript
js的对象与函数详解
Jan 21 #Javascript
JS实现求5的阶乘示例
Jan 21 #Javascript
You might like
深入Apache与Nginx的优缺点比较详解
2013/06/17 PHP
php使用百度天气接口示例
2014/04/22 PHP
学习php中的正则表达式
2014/08/17 PHP
PHP微信红包API接口
2015/12/05 PHP
深入理解PHP中的empty和isset函数
2016/05/26 PHP
js png图片(有含有透明)在IE6中为什么不透明了
2010/02/07 Javascript
JavaScript中的undefined学习总结
2013/11/30 Javascript
10分钟学会写Jquery插件实例教程
2014/09/06 Javascript
javascript在当前窗口关闭前检测窗口是否关闭
2014/09/29 Javascript
JavaScript中字面量与函数的基本使用知识
2015/10/20 Javascript
javascript结合Flexbox简单实现滑动拼图游戏
2016/02/18 Javascript
jQuery实现table中的tr上下移动并保持序号不变的实例代码
2016/07/11 Javascript
jQuery 实现ajax传入参数含有特殊字符的方法总结
2016/10/17 Javascript
Bootstrap CSS组件之下拉菜单(dropdown)
2016/12/17 Javascript
JQuery validate 验证一个单独的表单元素实例
2017/02/17 Javascript
浅谈Vue.nextTick 的实现方法
2017/10/25 Javascript
JavaScript实现获取select下拉框中第一个值的方法
2018/02/06 Javascript
Vue数据监听方法watch的使用
2018/03/28 Javascript
详解vue-cli3多页应用改造
2019/06/04 Javascript
原生js canvas实现鼠标跟随效果
2020/08/02 Javascript
Vue 数据响应式相关总结
2021/01/28 Vue.js
Python的函数嵌套的使用方法
2014/01/24 Python
从零学Python之入门(二)基本数据类型
2014/05/25 Python
浅谈MySQL中的触发器
2015/05/05 Python
Python中shutil模块的学习笔记教程
2017/04/04 Python
python实现远程通过网络邮件控制计算机重启或关机
2018/02/22 Python
Django使用Mysql数据库已经存在的数据表方法
2018/05/27 Python
PyQt5使用QTimer实现电子时钟
2019/07/29 Python
python中eval与int的区别浅析
2019/08/11 Python
python实现在列表中查找某个元素的下标示例
2020/11/16 Python
绝对经典成功的大学生推荐信
2013/11/08 职场文书
财务会计专业推荐信
2013/11/30 职场文书
感恩节活动策划方案
2014/05/16 职场文书
群众路线对照检查材料思想汇报怎么写
2014/09/18 职场文书
公司承诺书格式范文
2015/04/28 职场文书
《一面五星红旗》教学反思
2016/02/23 职场文书