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 innerText和innerHtml应用
Jan 28 Javascript
Javascript核心读书有感之语言核心
Feb 01 Javascript
js立即执行函数: (function ( ){})( ) 与 (function ( ){}( )) 有什么区别?
Nov 18 Javascript
jquery结合html实现中英文页面切换
Nov 29 Javascript
javascript中对象的定义、使用以及对象和原型链操作小结
Dec 14 Javascript
ES6新特性之数组、Math和扩展操作符用法示例
Apr 01 Javascript
获取url中用&amp;隔开的参数实例(分享)
May 28 Javascript
Vue中之nextTick函数源码分析详解
Oct 17 Javascript
不到200行 JavaScript 代码实现富文本编辑器的方法
Jan 03 Javascript
这样回答继承可能面试官更满意
Dec 10 Javascript
JavaScript实现点击切换验证码及校验
Jan 10 Javascript
JS新手入门数组处理的实用方法汇总
Apr 07 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
超外差式晶体管收音机的组装与统调
2021/03/01 无线电
PHP 时间日期操作实战
2011/08/26 PHP
php遍历目录与文件夹的多种方法详解
2013/11/14 PHP
Codeigniter实现发送带附件的邮件
2015/03/19 PHP
大家须知简单的php性能优化注意点
2016/01/04 PHP
javascript之锁定表格栏位
2007/06/29 Javascript
自写简单JS判断是否已经弹出页面
2010/10/20 Javascript
Javascript变量函数浅析
2011/09/02 Javascript
BootStrap glyphicon图标无法显示的解决方法
2016/09/06 Javascript
利用jquery获取select下拉框的值
2016/11/23 Javascript
JavaScript常见的五种数组去重的方式
2016/12/15 Javascript
详解nodejs中的process进程
2017/03/19 NodeJs
javascript 中Cookie读、写与删除操作
2017/03/29 Javascript
原生nodejs使用websocket代码分享
2018/04/07 NodeJs
layui写后台表格思路和赋值用法详解
2019/11/14 Javascript
40行代码把Vue3的响应式集成进React做状态管理
2020/05/20 Javascript
JS中队列和双端队列实现及应用详解
2020/09/29 Javascript
vue 组件基础知识总结
2021/01/26 Vue.js
python使用装饰器作日志处理的方法
2019/07/11 Python
如何用Python来理一理红楼梦里的那些关系
2019/08/14 Python
Python 3.6打包成EXE可执行程序的实现
2019/10/18 Python
tensorflow之获取tensor的shape作为max_pool的ksize实例
2020/01/04 Python
PyCharm如何导入python项目的方法
2020/02/06 Python
Python基于Socket实现简单聊天室
2020/02/17 Python
一款纯css3实现的漂亮的404页面的实例教程
2014/11/27 HTML / CSS
使用javascript和HTML5 Canvas画的四渐变色播放按钮效果
2014/04/10 HTML / CSS
RUIFIER官网:英国奢侈高级珠宝品牌
2020/06/12 全球购物
屈臣氏泰国官网:Watsons TH
2021/02/23 全球购物
Internet主要有哪些网络群组成
2015/12/24 面试题
超市实习总结自我鉴定
2013/09/19 职场文书
如何进行有效的自我评价
2013/09/27 职场文书
人力资源管理专业毕业生推荐信
2013/11/07 职场文书
社团成立邀请函
2014/01/08 职场文书
本科生自荐信
2014/06/18 职场文书
2015年度培训工作总结范文
2015/04/02 职场文书
MySQL 执行数据库更新update操作的时候数据库卡死了
2022/05/02 MySQL