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 相关文章推荐
jQuery拖动图片删除示例
May 10 Javascript
JQuery简单实现锚点链接的平滑滚动
May 03 Javascript
详解JavaScript ES6中的Generator
Jul 28 Javascript
jquery实现的用户注册表单提示操作效果代码分享
Aug 28 Javascript
javascript实现获取指定精度的上传文件的大小简单实例
Oct 25 Javascript
如何实现json数据可视化详解
Nov 24 Javascript
javascript实现无法关闭的弹框
Nov 27 Javascript
Angular-Ui-Router+ocLazyLoad动态加载脚本示例
Mar 02 Javascript
支付宝小程序tabbar底部导航
Nov 06 Javascript
微信小程序 wx:for遍历循环使用实例解析
Sep 09 Javascript
Vue中component标签解决项目组件化操作
Sep 04 Javascript
JS ES6异步解决方案
Apr 29 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
ThinkPHP实例化模型的四种方法概述
2014/08/22 PHP
thinkPHP+phpexcel实现excel报表输出功能示例
2017/06/06 PHP
PHP实现动态获取函数参数的方法示例
2018/04/02 PHP
thinkPHP框架动态配置用法实例分析
2018/06/14 PHP
PHP PDOStatement::rowCount讲解
2019/02/01 PHP
基于jquery+thickbox仿校内登录注册框
2010/06/07 Javascript
jquery的Tooltip插件 qtip使用详细说明
2010/09/08 Javascript
详解jquery uploadify 上传文件
2013/11/09 Javascript
Javascript中常见的校验如域名、手机、邮箱等等
2014/01/02 Javascript
nodejs中实现路由功能
2014/12/29 NodeJs
jQuery获得指定元素坐标的方法
2015/04/14 Javascript
JQuery radio(单选按钮)操作方法汇总
2015/04/15 Javascript
JS实现统计复选框选中个数并提示确定与取消的方法
2015/07/01 Javascript
跟我学习javascript的var预解析与函数声明提升
2015/11/16 Javascript
基于React.js实现原生js拖拽效果引发的思考
2016/03/30 Javascript
javascript原型继承工作原理和实例详解
2016/04/07 Javascript
vue中如何自定义右键菜单详解
2020/12/08 Vue.js
写了个监控nginx进程的Python脚本
2012/05/10 Python
python list中append()与extend()用法分享
2013/03/24 Python
Python中的exec、eval使用实例
2014/09/23 Python
Python挑选文件夹里宽大于300图片的方法
2015/03/05 Python
Python将xml和xsl转换为html的方法
2015/03/10 Python
Python中Iterator迭代器的使用杂谈
2016/06/20 Python
深入理解Python装饰器
2016/07/27 Python
vue.js实现输入框输入值内容实时响应变化示例
2018/07/07 Python
Django学习笔记之为Model添加Action
2019/04/30 Python
Python实现带下标索引的遍历操作示例
2019/05/30 Python
用python生成(动态彩色)二维码的方法(使用myqr库实现)
2019/06/24 Python
使用 Python 处理 JSON 格式的数据
2019/07/22 Python
基于nexus3配置Python仓库过程详解
2020/06/15 Python
python基于pexpect库自动获取日志信息
2021/02/01 Python
NYX Professional Makeup俄罗斯官网:世界知名的化妆品品牌
2019/12/26 全球购物
交通安全演讲稿
2014/01/07 职场文书
公司庆典活动邀请函
2014/01/09 职场文书
小学生新学期寄语
2014/01/19 职场文书
离婚协议书怎么写
2015/01/26 职场文书