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中,为什么要尽可能使用局部变量?
Apr 06 Javascript
找出字符串中出现次数最多的字母和出现次数精简版
Nov 07 Javascript
js实现倒计时(距离结束还有)示例代码
Jul 24 Javascript
js实现下拉框选择要显示图片的方法
Feb 16 Javascript
jQuery实现的多屏图像图层切换效果实例
May 07 Javascript
javascript创建含数字字母的随机字符串方法总结
Aug 01 Javascript
jQuery插件FusionCharts绘制2D柱状图和折线图的组合图效果示例【附demo源码】
Apr 10 jQuery
快速解决Vue项目在IE浏览器中显示空白的问题
Sep 04 Javascript
vue click.stop阻止点击事件继续传播的方法
Sep 04 Javascript
webpack 开发和生产并行设置的方法
Nov 08 Javascript
JS/HTML5游戏常用算法之碰撞检测 包围盒检测算法详解【矩形情况】
Dec 13 Javascript
一秒学会微信小程序制作table表格
Feb 14 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递归调用数组值并用其执行指定函数的方法
2015/04/01 PHP
PHP 实现页面静态化的几种方法
2017/07/23 PHP
thinkPHP框架实现多表查询的方法
2018/06/14 PHP
PHP的静态方法与普通方法用法实例分析
2019/09/26 PHP
php源码的使用方法讲解
2019/09/26 PHP
PHP 8新特性简介
2020/08/18 PHP
Jquery显示和隐藏元素或设为只读(含Ligerui的控件禁用,实例说明介绍)
2013/07/09 Javascript
使用node.js 获取客户端信息代码分享
2014/11/26 Javascript
JavaScript常用数组算法小结
2016/02/13 Javascript
完美实现八种js焦点轮播图(下篇)
2020/04/20 Javascript
你知道setTimeout是如何运行的吗?
2016/08/16 Javascript
javascript设计模式之Adapter模式【适配器模式】实现方法示例
2017/01/13 Javascript
mac上node.js环境的安装测试
2017/07/03 Javascript
JavaScript定时器setTimeout()和setInterval()详解
2017/08/18 Javascript
详解微信小程序实现仿微信聊天界面(各种细节处理)
2019/02/17 Javascript
nodejs一个简单的文件服务器的创建方法
2019/09/13 NodeJs
浅谈vue项目利用Hbuilder打包成APP流程,以及遇到的坑
2020/09/12 Javascript
Python插件virtualenv搭建虚拟环境
2017/11/20 Python
利用Tkinter(python3.6)实现一个简单计算器
2017/12/21 Python
tensorflow 用矩阵运算替换for循环 用tf.tile而不写for的方法
2018/07/27 Python
Python os.rename() 重命名目录和文件的示例
2018/10/25 Python
如何用Python做一个微信机器人自动拉群
2019/07/03 Python
python开根号实例讲解
2020/08/30 Python
pytorch 计算Parameter和FLOP的操作
2021/03/04 Python
ziaja齐叶雅官方海外旗舰店:来自波兰的天然护肤品牌
2017/01/02 全球购物
英国建筑用品在线:Building Supplies Online(BSO)
2018/04/30 全球购物
人民教师求职自荐信
2014/03/12 职场文书
爱心捐书倡议书
2015/04/27 职场文书
鲁滨逊漂流记读书笔记
2015/06/26 职场文书
儿子满月酒致辞
2015/07/29 职场文书
三严三实·严以律己心得体会
2016/01/13 职场文书
同学联谊会邀请函
2019/06/24 职场文书
《中国古代诗歌散文欣赏》高中语文教材
2019/08/20 职场文书
如何用JS实现简单的数据监听
2021/05/06 Javascript
java中重写父类方法加不加@Override详解
2021/06/21 Java/Android
Flutter集成高德地图并添加自定义Maker的实践
2022/04/07 Java/Android