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

所属分类: 网络编程 / JavaScript 阅读数: 683
收藏 0 赞 0 分享

本文实例讲述了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]()为例,图解一下:

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]()为例,图解一下:

在执行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错误与调试技巧总结》、《JavaScript数据结构与算法技巧总结》、《JavaScript遍历算法与技巧总结》及《JavaScript数学运算用法总结

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

更多精彩内容其他人还在看

AngularJs IE Compatibility 兼容老版本IE

本文主要介绍AngularJs IE Compatibility 兼容老版本IE的问题及解决办法,有兴趣的小伙伴可以参考下
收藏 0 赞 0 分享

AngularJs Modules详解及示例代码

本文主要介绍AngularJs Modules的相关知识,这里整理了详细的资料及简单示例代码,有兴趣的朋友可以参考下
收藏 0 赞 0 分享

AngularJs Scope详解及示例代码

本文主要介绍AngularJs Scope的知识,这里整理了详细资料及示例代码,有兴趣的小伙伴可以参考下
收藏 0 赞 0 分享

node.js中module.exports与exports用法上的区别

Node.js 引入了模块(Module)概念,一个模块可以通过module.exports 或 exports 将函数、变量等导出,以使其它 JavaScript 脚本通过require() 函数引入并使用。那么node.js中module.exports与exports有什么
收藏 0 赞 0 分享

基于JS实现发送短信验证码后的倒计时功能(无视页面刷新,页面关闭不进行倒计时功能)

这篇文章主要介绍了基于JS实现发送短信验证码后的倒计时功能(无视页面刷新,页面关闭不进行倒计时功能)的相关资料,非常不错,具有参考借鉴价值,需要的朋友可以参考下
收藏 0 赞 0 分享

基于jQuery实现发送短信验证码后的倒计时功能(无视页面关闭)

最近做了一个项目,其中有需求要求实现发送短信验证码后倒计时功能,其中有个难点:要求关闭页面也进行倒计时。好吧,下面小编把jquery 发送验证码倒计时的实现代码分享给大家,大家可以参考下
收藏 0 赞 0 分享

js绘制购物车抛物线动画

这篇文章主要为大家详细介绍了js绘制购物车抛物线动画,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
收藏 0 赞 0 分享

vue.js入门教程之绑定class和style样式

小编之前介绍了通过vue.js计算属性,不知道大家都学会了吗。那这篇文章中我们将一起学习vue.js实现绑定class和style样式,有需要的朋友们可以参考借鉴。
收藏 0 赞 0 分享

纯JS实现可拖拽表单的简单实例

下面小编就为大家带来一篇纯JS实现可拖拽表单的简单实例。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
收藏 0 赞 0 分享

js实现StringBuffer的简单实例

下面小编就为大家带来一篇js实现StringBuffer的简单实例。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
收藏 0 赞 0 分享
查看更多