浅谈JavaScript中的闭包
<!–
body
{
font-family: “Microsoft YaHei UI”,”Microsoft YaHei”,SimSun,”Segoe UI”,Tahoma,Helvetica,Sans-Serif,”Microsoft YaHei”, Georgia,Helvetica,Arial,sans-serif,宋体, PMingLiU,serif;
font-size: 10.5pt;
line-height: 1.5;
}
html, body
{
}
h1 {
font-size:1.5em;
font-weight:bold;
}
h2 {
font-size:1.4em;
font-weight:bold;
}
h3 {
font-size:1.3em;
font-weight:bold;
}
h4 {
font-size:1.2em;
font-weight:bold;
}
h5 {
font-size:1.1em;
font-weight:bold;
}
h6 {
font-size:1.0em;
font-weight:bold;
}
img {
border:0;
max-width: 100%;
}
blockquote {
margin-top:0px;
margin-bottom:0px;
}
–>
<!–
.wiz-todo, .wiz-todo-img {width: 16px; height: 16px; cursor: default; padding: 0 10px 0 2px; vertical-align: -10%;-webkit-user-select: none;} .wiz-todo-label {margin-top: 8px; margin-bottom: 8px; line-height: 1;} .wiz-todo-label-checked { /*text-decoration: line-through;*/ color: #666;} .wiz-todo-label-unchecked {text-decoration: initial;} .wiz-todo-completed-info {padding-left: 44px; display: inline-block; } .wiz-todo-avatar { width:20px; height: 20px; vertical-align: -20%; margin-right:10px; border-radius: 2px;} .wiz-todo-account, .wiz-todo-dt { color: #666; }
–>
在JavaScript中,闭包是指这样一个函数:它有权访问另一个函数作用域中的变量。
创建一个闭包的常用的方式:在一个函数内部创建另一个函数。比如:
function compareByProperty(propertyName){
returnfunction(obj1,obj2){
return obj1[propertyName] - obj2[propertyName];
}
}
该例中,compareByProperty内部的匿名函数有权利访问compareByProperty函数中的活动变量。 调用:
var compareNames = compareByProperty("name");
var result = compareNames({name:"jack"},{name:"rose"});// < 0
在compareByProperty()执行完之后,其活动对象不会被销毁,这是因为匿名函数的作用域链仍在引用这个活动对象。也就是说:当compareByProperty()函数返回之后,其本身执行环境的作用域会被销毁,但是它的活动对象仍留在内存中,供匿名函数使用。直到这个匿名函数被销毁了,这个活动对象才会被销毁。所以,当执行:compareNames = null;之后,解除掉了匿名函数,compareByProperty()的活动对象也随之被销毁。下图展示了调用compareNames()时产生的作用域链: 1、闭包与变量:思考一下这样一个问题:
function createFunction(){
var func =newArray();
vari;
for(i=0;i<10;i++){
func[i]=function(){
return i;
};
}
return func;
}
你可能会觉得func数组里面的函数应该返回其对应的索引值,比如你觉得下面这些代码的执行结果是:0-9
var funArr = createFunction();
var i;
for(i=0;i<funArr.length;i++){
console.log(funArr[i]());
}
(我强烈建议你在你的浏览器中执行一下这些代码)。 但是,很不幸,它们每一个都返回10,不用觉得不科学,这和我们日常的思维习惯不同。请注意:闭包的作用域链保存的是包含这个闭包的活动对象。也就是说,func里面的每一个元素,其作用域链保存(或者说“引用”更为恰当)的都是 createFunction()这个函数的活动对象(其中包括 i )每一次循环,i 都会变化,最后返回createFunction()返回时,i 的值是10,看到这里,如果你真的理解了,那么你就知道,为什么func里面的每一个元素调用后都是返回10. 如果将代码改变一下,结果就完全不一样了:
function createFunction(){
var func =newArray();
var i;
for(i =0; i<10;i++){
func[i]=function(num){
returnfunction(){
return num;
}
}(i);
}
return func;
}
在上面的代码中,我们不将一个匿名函数赋值给func[i],而是立即调用一个匿名函数,然后将这个匿名函数的返回值(也是一个匿名函数) 赋值给func[i],这样做能达到我们预想的效果(func的每一个元素调用后的返回值都是其对应的下标)的原因是:我们传入了变量i,由于函数参数是值传递的,所以变量i的值会被复制给参数num,而在这个匿名函数内部,创建了一个闭包,这个闭包访问的是包含它的那个函数的活动变量(num),这样一来,func数组里面的每一个元素调用后都会得到一个自己的num变量的副本,所以就可以返回各自对应的num的值了。 2、闭包中的this对象(为什么又是this!!!)看一个例子:
var name = "The window";
var myObj = {
name : "My Object",
getNameFunc : function(){
return function(){
return this.name;
}
}
}
console.log(myObj.getNameFunc()()); //非严格模式下
程序运行的结果是在控制台打印出”The window”;为什么? 我们看这一句:myObj.getNameFunc()(),拆解一下:var a = myObj.getNameFunc();a(); 当调用myObj.getNameFunc()时,其作用域是myObj这个对象,它返回了一个闭包,我们将这个闭包赋值给a当调用a时,其作用域不再是 myObj这个对象了,这次我们是在window这个对象中用它,这个时候,由于闭包的特性(访问包含这个闭包的那个函数的活动变量),this就指向window这个对象,而this.name自然就是指在全局定义的”The window”。 我们把代码改一下:
var name = "The window";
var myObj = {
name : "My Object",
getNameFunc : function(){
var that = this;
return function(){
return that.name;
}
}
}
console.log(myObj.getNameFunc()()); //非严格模式下
在这里,调用myObj.getNameFunc()时,作用域是myObj这个对象,它先是将this(即myObj)赋值给 that然后,返回一个闭包,我们将这个闭包赋值给b。前面我们提到,函数getNameFunc()返回之后,它自身的作用域会被销毁,但是,由于存在闭包仍然在引用着它的活动变量,所以,这个来自于getNameFunc()的活动变量(this)并不会被销毁,而是一直保留在内存中,供闭包使用,直到闭包被销毁(如:赋值为null),再没有别的闭包引用这个活动变量,这个活动变量才会被垃圾回收。这样,当调用b时,访问到的that.name,是来自于myObj的。 总之:如果想访问this 或者 是arguments对象,必须要将这两个对象的引用保存到另一个闭包能访问到变量中。