…
第一段代码
1 2 3 4 5 6 7 8 9 10 11 12
| function a(){
var i = 0; function b(){ alert(++i); } return b; }
var c = a();
c();
|
这段代码有2个特点:
1.函数b嵌套在函数a内部
2.函数a返回函数b
这样在执行完var c = a()后,变量c实际上是指向了函数b , b中用到了变量i
再执行c()后就会弹出窗口显示i的值(第一次为1)
这段代码就创建了一个闭包。
因为函数a外的变量c引用了a内的函数b
当函数a的内部函数b被函数a外的一个变量引用的时候,就创建了一个我们所说的‘闭包’
第二段代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| var outter = [];
function closeTest(){
var array = ['one','two','three','four']; for (var i = 0; i < array.length; i++){ var x = {}; x.no = i; x.text = array[i]; x.invoke = function(){ console.log(i); } outter.push(x); } }
closeTest();
console.log(outter[0].invoke());
console.log(outter[1].invoke());
console.log(outter[2].invoke());
console.log(outter[3].invoke());
|
很多初学者可能会认为答案是这样的: 0 1 2 3
但,实际上是: 4 4 4 4
其实,在每次迭代的时候,这样的语句x.invoke = function(){ console.log(i)}并没有被执行
只是构建了一个函数体为console.log(i)的函数对象。
当 i = 4 时,迭代停止,外部函数返回,当再去调用outter[0].invoke()时,i的值依旧为4
因此outter数组中的每一个元素的invoke都返回i的值:4
如何解决这一问题呢?
我们可以声明一个匿名函数,并立即执行他
第三段代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| var outter = [];
function closeTest2(){
var array = ['one','two','three','four']; for (var i = 0; i < array.length; i++){ var x = {}; x.no = i; x.text = array[i]; x.invoke = function(no){ return function(){ console.log(no); } }(i); outter.push(x); } }
closeTest2();
console.log(outter[0].invoke());
console.log(outter[1].invoke());
console.log(outter[2].invoke());
console.log(outter[3].invoke());
|
在这个例子中,我们为x.invoke赋值的时候
先运行一个可以返回一个函数的函数
然后立即执行之,这样,x.invoke的每一次迭代其实相当于执行了这样的语句:
第四段代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| //x == 0
x.invoke = function(){console.log(0);}
//x == 1
x.invoke = function(){console.log(1);}
//x == 2
x.invoke = function(){console.log(2);}
//x == 3
x.invoke = function(){console.log(3);}
|
这样就可以得到正确的结果了
闭包允许你引用存在于外部函数中的变量
但是
闭包并不是使用该变量创建时的值,相反,他是使用该变量最后的值
第五段代码
一个例子,在person之外的地方无法访问其内部的变量,而通过闭包的形式来访问
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| var person = function(){
//变量作用域为函数内部,外部无法访问 var name = "default";
return { getName : function(){ return name; }, setName : function(newName){ name = newName; } } }();
console.log(person.name);//直接访问没结果为undefined
console.log(person.getName());// 结果: default
console.log(person.setName('leo'));
console.log(person.getName());// 结果: leo
|
闭包的另一个重要用途是实现面向对象的对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| function Person2(){
//变量作用域为函数内部,外部无法访问 var name = "default";
return { getName : function(){ return name; }, setName : function(newName){ name = newName; } } }
var leo = Person2();
console.log(leo.getName());//结果:default
leo.setName('leo')
console.log(leo.getName());//结果: leo
.
var john = Person2();
console.log(john.getName());//结果:default
john.setName('john')
console.log(john.getName());//结果: john
|
由此代码可知,leo和john都可以称为是Person2这个类的实例,因为这两个实例对name这个成员的访问是独立的,互不影响的