闭包经典问题示例:
由一个经典例子入手:
function box(){
var arr = [];
for(var i=0;i<5;i++){
arr[i] = function(){
return i;
}
}
return arr;
}
alert(box());
这个例子其实要说明问题的是:本来期望返回的arr数组应该是[0,1,2,3,4],但实际事与愿违。这是一个典型闭包问题,网上有很多解释,这里不重复解释。
这里从函数定义的角度来解释这个问题,试想为什么会有人错误的认为arr应该是[0,1,2,3,4]呢?其实错误原因在于,他们误以为每次进for循环,就会把匿名函数的执行结果赋值给arr[i]。事实上并不是这样,执行for循环的时候,里面的匿名函数并不会执行,它只是声明了一个匿名函数而已,并没有调用这个匿名函数!而arr储存的只不过是指向这个函数对象的指针而已!
现在稍做改变:
function box(){
var arr = [];
for(var i=0;i<5;i++){
var fun = function(){
return i;
}
arr[i] = fun(); //这里调用了上面的函数!
}
return arr;
}
alert(box());
对比改变后的代码,很好理解,这次输出arr=[0,1,2,3,4]完全没有问题了,因为每次for循环都会调用一次fun()。
当然也可以引入 IIFE(Immediately Invoked Function Expression:声明即执行的函数表达式)解决,总之和原始代码的差别就是,进for循环的时候,到底有没有立即执行这个(匿名)函数!
function box(){
var arr = [];
for(var i=0;i<5;i++){
arr[i] = (function(){
return i;
})(i);
}
return arr;
}
alert(box());
闭包中的this对象:
也从一个经典示例入手:
var name = "The Window";
var object = {
name: "My object",
getNameFunc: function() {
return function() {
return this.name;
};
}
}
alert(object.getNameFunc()()); // "The Window"
结果表明this指向window,而不是object,为什么?
首先,object.getNameFunc()就是代码中返回的那个匿名函数,其实就是闭包,如果getNameFunc()中直接使用this,那this指向object是没有问题的,但是现在this是在返回的匿名函数中使用的,object.getNameFunc()()就相当于在全局环境中直接调用这个匿名函数!自然this指向window,我们只是被object这个前缀给迷惑了!
换种思路,从闭包的定义来说,比如全局环境中定义一个函数,这个函数里面包含它自己的变量,现在我想在全局环境下使用这个函数的变量,由于这个变量只是一个局部变量,在全局环境中是无法直接使用的!于是,我们就想到了闭包来解决这个问题。为什么要说这一堆废话呢?就是要让你回忆一下闭包的初衷!这段废话就是要在全局环境下通过闭包来访问另一个函数内部的局部变量(上面例子中之所以没访问到,是因为不能直接这么使用this而已)!那么,闭包就是一般返回的一个匿名函数(注意闭包和匿名函数并不完全等同!),而执行返回的这个匿名函数时,它的执行环境具有全局性!所以闭包内这个this指向的是全局作用域!
var name = "The Window";
var getNameFunc = function() {
var name = 'my obiect';
return function() {
return this.name;
};
};
alert(getNameFunc()()); // "The Window"
这个例子没有object对象的干扰,想必更好理解!实际上就是在全局环境下,直接执行的这个返回的匿名函数,所以this指向的window!
另外,为了防止this的指向与我们预期的不一样,在实际编码中,经常用到var that = this;来提前保存我们期望的作用域,然后用that.name获取对象的变量。