关于闭包

来源:互联网 发布:js鼠标经过事件 编辑:程序博客网 时间:2024/05/16 15:36

闭包是指有权访问另一个函数作用域中的变量的函数 //摘自javascript高级程序设计

要理解闭包首先要理解作用域链,每一个javascript代码(全局代码或函数)都有一个与之相关的作用域链,这个作用域链可以看作是一个串联着这段代码或函数作用域中范围内的变量的对象列表。当代码或函数需要查找x属性的时候它会沿着链从第一个元素开始查找,直到最后一个元素。如果找到了,那么就会使用这个x的值,如果没有找到则继续向下查找,直到没有任何一个对象含有x属性,那么抛出异常。这就是作用域链的概念。

下面我们来看几个简单的例子:

function a(){var x = 1;console.log(x);}a()

输出为:1

var x = 2;function a(){console.log(x);}a();


输出为2

var x = 2;function a(){console.log(x);}a();x = 3;a();


输出为2  3

其实这就是闭包的概念,在函数内部可以使用函数外部的变量

理解对象链的创建规则是非常重要的,当定义一个函数时,它实际上保存一个作用域链。当调用这个函数时,它创建一个新的对象来存储它的局部变量,并将这个对象添加至保存的那个作用域上同时创建一个新的更长的表示函数调用作用域的“链”。对于嵌套函数来讲,事情变得更有趣,每次调用外部函数时,内部函数又会重新定义一遍。因为每次调用外部函数的时候,作用域链都是不同的。//以上这段话摘自javascript权威指南 第六版

下面我们看第四个例子:

var x = 2;function a(){var x = 1;function b(){console.log(x);}b();}a();


输出为:1

再看这个例子:

function a(){var x = 1;return {b:function (){console.log(x);},setX:function(value){x = value;}};}var obj = a();obj.b();obj.setX(10);obj.b();


输出应为1 10

作用域链的这种配置机制引出了一个值得注意的副作用,即闭包只能取得包含函数中任何变量的最后一个值。别忘了闭包所保存的是整个变量对象,而不是某个特殊的变量。下面这个例子可以清晰的说明这个问题。

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


这个函数会返回一个函数数组。表面上看似乎每个函数都应该返回自己的索引值,即位置0 的函数返回0,位置1的
函数返回1,以此类推。但实际上,每个函数都返回10。因为每个函数的作用域链中都保存着c()函数的活动对象,所以他们引用的都是同一个变量i。当c函数返回后,变i的值是10。此时每个函数都引用这保存变量i的同一个变量对象,所以在每个函数内部i的值都是10.但是我们可以通过创建另一个匿名函数强制让闭包的行为符合预期
如下:

function c(){var result = new Array();for(var i=0;i<10;i++){result[i] = function(num){return function(){return num;};}(i);}return result;}


在重写了前面的c()函数后,每个函数就返回各自不同的索引值了。在这个版本中,我们没有直接把闭包赋值给数组,而是定义了一个匿名函数,并将立即执行该匿名函数的结果赋值给数组。这里的匿名函数有一个参数num,也就是最终的函数要返回的值。在调用每个匿名函数时,我们传入了变量i。由于函数参数是按值传递的,所以就会将变量i的当前值复制给参数num。而这个匿名函数的内部,又创建并返回了一个访问num的闭包。这样一来,result数组中的每一个函数都有自己num变量的一个副本,因此可以返回各自不同的值了。

(个人认识 请多指正)

原创粉丝点击