还是上面的题目,做个变形。
var x = 0;
var foo = {
x:1,
bar:function () {
console.log(this.x);
var that = this;
return function () {
console.log(this.x)
console.log(that.x)
}
}
}
foo.bar() // 1
foo.bar()() // this: 0, that: 1
上面的例子中ba'r里面返回了一个匿名函数,这个匿名函数可以在外部被调用即:foo.bar()() 读取到了bar的执行上下文的变量对象 that,这个函数就形成了一个闭包。
好了,我们理解了上面的套路,下面来解释闭包就好理解了。
闭包就是能够读取其它函数内部变量的函数
在Javascript语言中,只有函数内部的子函数才能读取局部变量,因此可以把闭包简单理解成“定义在一个函数内部的函数”
var x = 0;
var bar:function () {
var n = 999;
return function () {
return n;
}
}
var outer = bar();
outer() // 999
用途:
- 读取函数内部的变量
- 让这些变量的值始终保持在内存中
我们修改一下上面的代码
var add;
var bar = function () {
var n = 999;
add = function () {
n += 1;
}
return function () {
return n;
}
}
var outer = bar();
outer() // 999
add();
outer(); // 1000
说明,n一直保存在内存当中,而没有在bar()执行完成之后被销毁;
原因:
bar里面的匿名函数被赋值给了outer,这个导致在outer没有被销毁的时候,该匿名函数一直存在内存当中,而匿名函数的存在依赖于bar,所以bar需要使用都在内存当中,所以bar并不会在调用结束后呗垃圾回收机制给收回。
而上面的add接受的也是一个匿名函数,该匿名函数本身也是闭包,所以也可以在外部操作里面的变量。
注意点
- 会导致内存泄漏,慎用
- 闭包会修改内部变量的值,所以在使用闭包作为对象的公用方法时要谨慎。
闭包的一个应用,单例模式
单例模式的定义是产生一个类的唯一实例
单例模式在js中经常会遇到,比如 var a = {}; 其实就是一个单例子。
但是我们写一个更有意义的单例:
var singleton = function( fn ){
var result;
return function(){
return result || ( result = fn .apply( this, arguments ) );
}
}
更简洁一点的:
var singleton = (function () {
var instance;
return function (object) {
if(!instance){
instance = new object();
}
return instance;
}
})();