JavaScript闭包与作用域链实例分析


Posted in Javascript onJanuary 21, 2019

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

闭包定义

闭包指的是有权访问另一个函数作用域中的变量的函数。创建闭包的常见方式,就是在一个函数A内部创建另一个函数B,那么函数B就是一个闭包,可以访问函数A作用域中的所有变量。

JavaScript的闭包与作用域链密不可分,因此本文可以和JavaScript的作用域链相对照分析,一定可以对JavaScript的闭包和作用域链有更深的理解。

下面我们仍然以createComparisonFunction为例进行闭包的分析。

//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 compareName = createComparisonFunction("name");
var compareAge = createComparisonFunction("age");
//step3: call compare
var object1 = {
  name : "Nicholas",
  age : 25
};
var object2 = {
  name : "Greg",
  age : 27
};
var result1 = compareName(object1, object2); // 1
var result2 = compareAge(object1, object2); // -1
//step4: dereference closure for recycle memory
compareName = null;
compareAge = null;

在这个例子中,匿名函数function(object1, object2)是一个闭包,能访问createComparisonFunction作用域里的所有变量,自然也包含propertyName属性, 因为propertyName参数的不同,导致比较的属性也有所不同,从而函数执行结果也有不同。

闭包与变量

从JavaScript的作用域链中,我们了解到JavaScript是通过作用域链来确定函数执行环境的作用域的,这种机制会引出一个值得注意的副作用,即闭包只能取得包含函数中任何变量的最后一个值。闭包是通过引用外部函数的活动对象来访问该活动对象中的所有变量,因此在外部函数执行过程中,这些变量的值可能会变化,但是在外部函数执行完毕之后,外部函数的活动对象便不会再改变,因此在执行闭包的时候,闭包通过作用域链访问到外部函数的活动对象中的所有变量都只可能是在外部函数执行完毕之后,外部函数的活动对象中最后所保存的值。我们通过一个例子来说明这种副作用。

function createFunctions(){
  var result = new Array();
  for (var i = 0; i < 10; i++){
    result[i] = function(){
      return i;
    };
  }
  return result;
}
var functions = createFunctions();
for(var i = 0; i < functions.length; i++){
  console.log(functions[i]());
}

输出的结果是

10 10 10 10 10 10 10 10 10 10

从表面上看,似乎每个函数都应该返回自己的索引值,但实际上,每个函数都返回10。因为每个函数的作用域链中都保存着createFunctions函数的活动对象,所以他们引用的都是这个createFunctions函数的活动对象中的变量i,在createFunctions函数返回之后,变量i的值是10,此时每个函数都引用着保存变量i的同一个变量对象,所以每个函数内部i的值都是10。

我们以调用functions[3]()为例,图解一下:

JavaScript闭包与作用域链实例分析

Closure函数的Function对象的作用域链引用的createFunctions的活动对象中保留的变量i的值为10。所以不管是functions[3]()还是functions[5](),其运行时上下文的作用域链引用的createFunctions的活动对象都是同一个活动对象,该活动对象中保留的变量i的值是10。

如何避免这种局面?我们可以通过创建另一个匿名函数让闭包的行为符合预期。

function createFunctions(){
  var result = new Array();
  for (var i = 0; i < 10; i++){
    result[i] = function(num){
      return function(){
        return num;
      }
    }(i);
  }
  return result;
}
var functions = createFunctions();
for(var i = 0; i < functions.length; i++){
  console.log(functions[i]());
}

输出的结果是

0 1 2 3 4 5 6 7 8 9

这个代码片段与前面的代码的区别在于立即调用了一个匿名函数function(num),使得闭包function()引用的是function(num)的活动对象,访问的是该活动对象中的变量num而不是createFunctions活动对象中的变量i,而在立即调用function(num)的num是索引值0,1,2…9。

我们仍旧以调用functions[3]()为例,图解一下:

JavaScript闭包与作用域链实例分析

在执行createFunctions函数的时候,会依次调用function(0), function(1) … function(9), 生成function(0), function(1) … function(9)这10个function(num)的活动对象,而result[0], result[1] … result[9]这10个匿名函数对象的作用域链分别引用这10个function(num)的活动对象,而其中的变量num的值也对应的为0, 1 … 9。

所以不管是functions[3]()还是functions[5](),其运行时上下文的作用域链都会引用在执行createFunctions函数时候所执行的function(3)或者function(5)这些function(num)函数的活动对象,这些活动对象都是不同的活动对象,其中保留的num值分别为3, 5。

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

Javascript 相关文章推荐
javascript图片相似度算法实现 js实现直方图和向量算法
Jan 14 Javascript
使用insertAfter()方法在现有元素后添加一个新元素
May 28 Javascript
完美兼容各大浏览器的jQuery插件实现图片切换特效
Dec 12 Javascript
JQuery移动页面开发之屏幕方向改变与滚屏的实现
Dec 03 Javascript
浅析Javascript匿名函数与自执行函数
Feb 06 Javascript
基于RequireJS和JQuery的模块化编程日常问题解析
Apr 14 Javascript
vue多级多选菜单组件开发
Sep 08 Javascript
JavaScript中在光标处插入添加文本标签节点的详细方法
Mar 22 Javascript
基于Vue实例对象的数据选项
Aug 09 Javascript
JQuery 获取多个select标签option的text内容(实例)
Sep 07 jQuery
基于form-data请求格式详解
Oct 29 Javascript
keep-alive不能缓存多层级路由菜单问题解决
Mar 10 Javascript
js中事件对象和事件委托的介绍
Jan 21 #Javascript
JavaScript作用域链实例详解
Jan 21 #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
You might like
PHP脚本的10个技巧(4)
2006/10/09 PHP
PHP CURL 多线程操作代码实例
2015/05/13 PHP
浅析ThinkPHP缓存之快速缓存(F方法)和动态缓存(S方法)(日常整理)
2015/10/26 PHP
PHP中的函数声明与使用详解
2017/05/27 PHP
phpQuery采集网页实现代码实例
2020/04/02 PHP
input 输入框获得/失去焦点时隐藏/显示文字(jquery版)
2013/04/02 Javascript
jQuery设置div一直在页面顶部显示的方法
2013/10/24 Javascript
javascript中的变量作用域以及变量提升详细介绍
2013/10/24 Javascript
客户端js判断文件类型和文件大小即限制上传大小
2013/11/20 Javascript
使图片旋转的3种解决方案
2013/11/21 Javascript
Jquery easyUI 更新行示例
2014/03/06 Javascript
connect中间件session、cookie的使用方法分享
2014/06/17 Javascript
node.js中的fs.mkdir方法使用说明
2014/12/17 Javascript
jquery使用正则表达式验证email地址的方法
2015/01/22 Javascript
jQuery自定义动画函数实例详解(附demo源码)
2015/12/10 Javascript
Treegrid的动态加载实例代码
2016/04/29 Javascript
浅谈javascript中new操作符的原理
2016/06/07 Javascript
Bootstrap实现input控件失去焦点时验证
2016/08/04 Javascript
浅谈Angularjs link和compile的使用区别
2016/10/21 Javascript
jquery.zclip轻量级复制失效问题
2017/01/08 Javascript
angular实现IM聊天图片发送实例
2017/05/08 Javascript
对layer弹出框中icon数字参数的说明介绍
2019/09/04 Javascript
swiper4实现移动端导航栏tab滑动切换
2020/10/16 Javascript
Python正确重载运算符的方法示例详解
2017/08/27 Python
用PyInstaller把Python代码打包成单个独立的exe可执行文件
2018/05/26 Python
对python中 math模块下 atan 和 atan2的区别详解
2020/01/17 Python
详解pyqt5的UI中嵌入matplotlib图形并实时刷新(挖坑和填坑)
2020/08/07 Python
Python hashlib模块的使用示例
2020/10/09 Python
Priority Pass机场贵宾室会籍计划:全球超过1200间机场贵宾室
2018/08/26 全球购物
2014基层党员干部学习全国两会心得体会
2014/03/17 职场文书
调研座谈会发言材料
2014/08/23 职场文书
中学生的1000字检讨书
2014/10/11 职场文书
《怀念母亲》教学反思
2016/02/19 职场文书
公司周年庆寄语
2019/06/21 职场文书
好段摘抄大全(48句)
2019/08/08 职场文书
SQL Server实现分页方法介绍
2022/03/16 SQL Server