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数组的扩展实现代码集合
Jun 01 Javascript
Jquery上传插件 uploadify v3.1使用说明
Jun 18 Javascript
原生js实现改变随意改变div属性style的名称和值的结果
Sep 26 Javascript
jQuery使用empty()方法删除元素及其所有子元素的方法
Mar 26 Javascript
PHP结合jQuery实现红蓝投票功能特效
Jul 22 Javascript
Javascript验证方法大全
Sep 21 Javascript
js对象浅拷贝和深拷贝详解
Sep 05 Javascript
js中小数向上取整数,向下取整数,四舍五入取整数的实现(必看篇)
Feb 13 Javascript
Jquery中attr与prop的区别详解
May 27 jQuery
BootStrap 标题设置跨行无效的解决方法
Oct 25 Javascript
React学习之受控组件与数据共享实例分析
Jan 06 Javascript
javascript自定义加载loading效果
Sep 15 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
php获得当前的脚本网址
2007/12/10 PHP
php 获取全局变量的代码
2011/04/21 PHP
php中flush()、ob_flush()、ob_end_flush()的区别介绍
2013/02/17 PHP
CodeIgniter采用config控制的多语言实现根据浏览器语言自动转换功能
2014/07/18 PHP
对PHP新手的一些建议(PHP学习经验总结)
2014/08/20 PHP
php合并数组中相同元素的方法
2014/11/13 PHP
详解Yii2 定制表单输入字段的标签和样式
2017/01/04 PHP
简单谈谈PHP中的trait
2017/02/25 PHP
使用PHPStorm+XDebug搭建单步调试环境
2017/11/19 PHP
Laravel5.1 框架路由基础详解
2020/01/04 PHP
asp javascript 实现关闭窗口时保存数据的办法
2007/11/24 Javascript
JavaScript创建命名空间(namespace)的最简实现
2007/12/11 Javascript
解析JavaScript中的字符串类型与字符编码支持
2016/06/24 Javascript
JS闭包与延迟求值用法示例
2016/12/22 Javascript
JQuery Dialog对话框 不能通过Esc关闭的原因分析及解决办法
2017/01/18 Javascript
js实现可以点击收缩或张开的悬浮窗
2017/09/18 Javascript
jQuery实现的简单对话框拖动功能示例
2018/06/05 jQuery
js实现文件上传功能 后台使用MultipartFile
2018/09/08 Javascript
详解ES7 Decorator 入门解析
2019/02/18 Javascript
解决vue安装less报错Failed to compile with 1 errors的问题
2020/10/22 Javascript
原生js实现九宫格拖拽换位
2021/01/26 Javascript
自己使用总结Python程序代码片段
2015/06/02 Python
Python用threading实现多线程详解
2017/02/03 Python
python3.X 抓取火车票信息【修正版】
2018/06/19 Python
python将excel转换为csv的代码方法总结
2019/07/03 Python
使用Pandas对数据进行筛选和排序的实现
2019/07/29 Python
Python中remove漏删和索引越界问题的解决
2020/03/18 Python
python中常用的数据结构介绍
2021/01/12 Python
美国在线购物频道:Shop LC
2019/04/21 全球购物
Bloomingdale’s阿联酋:选购奢华时尚、美容及更多
2020/09/22 全球购物
Ajax和javascript的区别
2013/07/20 面试题
中专毕业生自我鉴定
2013/11/21 职场文书
敬老院院长事迹材料
2014/05/21 职场文书
2015年学生资助工作总结
2015/05/25 职场文书
Linux下使用C语言代码搭建一个简单的HTTP服务器
2022/04/13 Servers
NoSQL优缺点与MongoDB数据库简介
2022/06/05 MongoDB