JavaScript 闭包机制详解及实例代码

作者:cy012124 时间:2024-10-22 22:14:02 

首先要区分两个概念,一是匿名函数,一是闭包。

所谓匿名函数,就是创建函数没有给定函数名。经常出现的包括函数表达式,就是定义一个匿名函数,然后将函数赋值给某个变量,而此时这个变量就相当于该函数的函数名,例如:


var sayHi = function(){
 alert("Hi");
}; //注意这个分号
sayHi(); //调用函数

还有一种常用匿名函数的情况是回调函数,如 JQuery 中常用到的:


$("p").click(function(){
 alert("click");
});

此外,还有利用匿名函数作为某函数的返回值:


function sayNameWithAge(age){
 return function(person){
   if(person.age == age){
     return person.name;
   }
 }
}

那么,闭包又是怎么一回事呢?所谓的闭包,其实就是一个函数,而这个函数有一点比较特别,它有权能够去访问其他函数作用域的变量。

从定义中我们发现,其实在上面的匿名函数例子中,就存在这样的闭包。在最后一个例子中,匿名函数访问了函数 sayNameWithAge 的参数 age,那么,这个作为返回值的匿名函数就是一个闭包。

要彻底理解闭包,就必须理解函数调用时的整个机制,这里从作用域链的相关知识来进行讲解。

首先看下面的例子:


function sayName(name){
 alert(name);
}
sayName("Jack");

在上面的函数 sayName 被调用的时候,就会创建一个对应的执行环境和作用域链,如下图所示:

JavaScript 闭包机制详解及实例代码

当 sayName 函数被调用时,创建了相应的作用域链,而作用域中包含两个引用分别指向两个对象,其中一个是全局变量对象,这个全局对象是在函数创建的时候就已经创建了,只是在调用函数的时候才将其复制到作用域链中;而另一个就是函数的活动对象,这个对象是在调用函数的时候才创建的。

在函数中访问一个变量时,就会从作用域中搜索对应名字的变量。

而当函数执行完毕后,函数的活动对象会被销毁,而全局变量对象却永远保存在内存中。

但是,上面所说的都是普通函数的情况,对于闭包而言,又是另外一种情况:

以上面的 sayNameWithAge 函数为例:


function sayNameWithAge(age){
 return function(person){
   if(person.age == age){
     return person.name;
   }
 }
}
//创建函数
var sayName = sayNameWithAge(18);
//调用函数
var name = sayName({name:"Jack",age:18});
//解除对匿名函数的引用
sayName = null;

当上面的 sayName 函数被调用的时候,产生的作用域链如下所示:

JavaScript 闭包机制详解及实例代码

当匿名函数被 return 后,它的作用域链被创建,并且包含了外部函数的活动对象和全局变量对象,这样一来,这个匿名函数就可以访问 sayNameWithAge 函数中定义的所有变量,也就是一个闭包。

这样的闭包会存在一个问题,就是当 sayNameWithAge 函数执行完毕的时候(JS 的垃圾处理机制大多是标记清除),其活动对象被闭包所引用,所以活动对象并不会被销毁,只有当匿名函数被销毁后,sayNameWithAge 的活动对象才会被销毁,所以上面的最后一行解除对匿名函数的引用不仅是为了销毁闭包的对象,也是为了销毁外部函数的活动对象。所以,慎重使用闭包!!!

关于闭包,还有一个需要注意的地方,就是在闭包中访问其他函数的变量,实际上是因为闭包的作用域链中有指向其他函数的活动对象的引用,而不是闭包自身的活动对象中保存着这些变量。看下面的例子:


function outer(){
 var result = new Array();
 for(var i = 0; i < 5; i ++){
   result[i] = function(){
     return i;
   };
 }
 return result;
}

按照设想,最后 outer 返回的数组各个项中的值应该是与其下标一致的。但是,最后的结果却是每个项的值都是 5
不难想象,在上面的所有闭包的作用域链中,都有一个引用指向了 outer 的活动对象中的参数 i,而且是指向同一个对象。

当 outer 函数执行完毕的时候,i 的值是 5。也就是说,所有闭包中访问 i 的时候取到的值都是 5

那么,我们可以通过另一种方法来实现预想的效果:


function outer(){
 var result = new Array();
 for(var i = 0; i < 5; i ++){
   result[i] = (fuction(index){
     return index;
   })(i);
 }
 return result;
}

这里我们为匿名函数定义一个参数 index,并在每次循环中立即调用该函数,将 i 的当前值复制给参数 index(注意 JS 中是按值传递),并将返回的 index 赋值给 result。

此外,闭包中需要注意的另一个问题是 this 对象。

this 对象在 JS 中是在函数运行时基于函数的执行环境绑定的。而匿名函数的执行环境具有全局性,也就是说,在匿名函数中,this 对象通常指向 window。


var name = "Tom";
var person = {
 name : "Jack",
 sayName : function(){
   return (function(){
     return this.name;
   })();
 }
}
person.sayName(); //Tom

上面在闭包中访问 this.name,其中的 this 对象并非取得自身或是 person 的 this 对象,而是指向 window。

如果需要在闭包中访问外部函数的 this 对象,那么,可以在外部函数中定义一个变量,将 this 对象传给该变量。


var name = "Tom";
var person = {
 name : "Jack",
 sayName : function(){
   var self = this;
   return (function(){
     return self.name;
   })();
 }
}
person.sayName(); //Jack

感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!

来源:http://lib.csdn.net/article/javascript/12370?knId=495

标签:JavaScript,闭包机制
0
投稿

猜你喜欢

  • JS 中对象equals方法的实现

    2020-07-08 03:49:19
  • 如何编写一个过滤掉HTML代码的函数?

    2009-11-08 18:53:00
  • Numpy对于NaN值的判断方法

    2022-12-15 15:08:21
  • 基于js实现的限制文本框只可以输入数字

    2024-04-25 13:06:46
  • flask框架jinja2模板与模板继承实例分析

    2023-08-26 22:21:34
  • numpy的Fancy Indexing和array比较详解

    2022-05-12 23:31:46
  • Linux下远程连接Jupyter+pyspark部署教程

    2021-03-31 18:35:59
  • python常见的占位符总结及用法

    2023-10-11 10:39:58
  • Python适配器模式代码实现解析

    2023-04-15 12:58:14
  • Python最火、R极具潜力 2017机器学习调查报告

    2023-10-10 01:40:47
  • vue中的路由传值与重调本路由改变参数

    2024-04-27 16:10:12
  • python读取指定字节长度的文本方法

    2022-04-17 10:42:45
  • Python3.x对JSON的一些操作示例

    2022-08-09 00:23:54
  • linux下源码安装mysql5.6.20教程

    2024-01-16 20:13:42
  • SELECT...INTO的具体用法

    2024-01-29 02:20:43
  • python boto和boto3操作bucket的示例

    2022-08-05 03:22:49
  • 评论是倒序好还是顺序好?

    2007-11-09 10:40:00
  • JS小游戏之象棋暗棋源码详解

    2024-04-23 09:24:55
  • Python3 获取文件属性的方式(时间、大小等)

    2022-05-06 09:13:41
  • SQL离前端有多远?

    2009-05-21 10:37:00
  • asp之家 网络编程 m.aspxhome.com