共计 2129 个字符,预计需要花费 6 分钟才能阅读完成。
一、变量的作用域
要了解闭包,首先必须了解 Javascript 非凡的变量作用域。
变量的作用域无非就是两种:全局变量和局部变量。
Javascript 语言的非凡之处,就在于函数外部能够间接读取全局变量。
另一方面,在函数内部天然无奈读取函数内的局部变量。
二、如何从内部读取局部变量?
出于种种原因,咱们有时候须要失去函数内的局部变量。然而,后面曾经说过了,失常状况下,这是办不到的,只有通过变通方法能力实现。
那就是在函数的外部,再定义一个函数。
function f1(){
var n=999;
function f2(){alert(n); // 999
}
}
在下面的代码中,函数 f2 就被包含在函数 f1 外部,这时 f1 外部的所有局部变量,对 f2 都是可见的。然而反过来就不行,f2 外部的局部变量,对 f1 就是不可见的。这就是 Javascript 语言特有的 ” 链式作用域 ” 构造(chain scope),子对象会一级一级地向上寻找所有父对象的变量。所以,父对象的所有变量,对子对象都是可见的,反之则不成立。
既然 f2 能够读取 f1 中的局部变量,那么只有把 f2 作为返回值,咱们不就能够在 f1 内部读取它的外部变量了吗!
function f1(){
var n=999;
function f2(){alert(n);
}
return f2;
}
var result=f1();
result(); // 999
三、闭包的概念
上一节代码中的 f2 函数,就是闭包。
各种业余文献上的 ” 闭包 ”(closure)定义十分形象,很难看懂。我的了解是,闭包就是可能读取其余函数外部变量的函数。
因为在 Javascript 语言中,只有函数外部的子函数能力读取局部变量,因而能够把闭包简略了解成 ” 定义在一个函数外部的函数 ”。
所以,在实质上,闭包就是将函数外部和函数内部连接起来的一座桥梁。
四、闭包的用处
闭包能够用在许多中央。它的最大用途有两个,一个是后面提到的能够读取函数外部的变量,另一个就是让这些变量的值始终保持在内存中。
怎么来了解这句话呢?请看上面的代码。
function f1(){
var n=999;
nAdd=function(){n+=1}
function f2(){alert(n);
}
return f2;
}
var result=f1();
result(); // 999
nAdd();
result(); // 1000
在这段代码中,result 实际上就是闭包 f2 函数。它一共运行了两次,第一次的值是 999,第二次的值是 1000。这证实了,函数 f1 中的局部变量 n 始终保留在内存中,并没有在 f1 调用后被主动革除。
为什么会这样呢?起因就在于 f1 是 f2 的父函数,而 f2 被赋给了一个全局变量,这导致 f2 始终在内存中,而 f2 的存在依赖于 f1,因而 f1 也始终在内存中,不会在调用完结后,被垃圾回收机制(garbage collection)回收。
这段代码中另一个值得注意的中央,就是 ”nAdd=function(){n+=1}” 这一行,首先在 nAdd 后面没有应用 var 关键字,因而 nAdd 是一个全局变量,而不是局部变量。其次,nAdd 的值是一个匿名函数(anonymous function),而这个匿名函数自身也是一个闭包,所以 nAdd 相当于是一个 setter,能够在函数内部对函数外部的局部变量进行操作。
五、应用闭包的留神点
1)因为闭包会使得函数中的变量都被保留在内存中,内存耗费很大,所以不能滥用闭包,否则会造成网页的性能问题,在 IE 中可能导致内存泄露。解决办法是,在退出函数之前,将不应用的局部变量全副删除。
2)闭包会在父函数内部,扭转父函数外部变量的值。所以,如果你把父函数当作对象(object)应用,把闭包当作它的专用办法(Public Method),把外部变量当作它的公有属性(private value),这时肯定要小心,不要轻易扭转父函数外部变量的值。
eg1:
var name = "The Window";
var object = {
name : "My Object",
getNameFunc : function(){return function(){return this.name;};
}
};
alert(object.getNameFunc()());
这个是一个匿名函数不是 object 对象的属性和成员函数,则匿名函数中的 this 指向的必定是 window
对象了,this.name 相当于 window.name
eg2:
var name = "The Window";
var object = {
name : "My Object",
getNameFunc : function(){
var that = this;
return function(){return that.name;};
}
};
alert(object.getNameFunc()());
指向成员函数的父级