对于闭包二字, 我们常常听说, 可能更加知道面试的时候, 面试官必问知识点之一
在MDN 中, 对闭包是这样定义的
一个函数和对其周围状态(lexical environment,词法环境)的引用捆绑在一起(或者说函数被引用包围),这样的组合就是闭包(closure)。也就是说,闭包让你可以在一个内层函数中访问到其外层函数的作用域
。在 JavaScript 中,每当创建一个函数,闭包就会在函数创建的同时被创建出来。
要想理解闭包, 必然需要理解js的变量作用域: 全局变量和局部变量
我们都知道, 在函数内部可以读取全局变量
var a = 1;
function fn () {
console.log(a) // 1
}
fn()
但是如果我们把a变量定义在函数内部 , 需要在函数外部使用. 则失败
那么我们怎样才可以使用函数内部的局部变量, 这就引进了闭包
function fn() {
var a = 1
function fn2() {
console.log(a)
}
return fn2
}
var f = fn()
f()
在本例中 f
是执行fn
执行时创建的fn2
函数
在上述代码中 函数fn2是被包含在fn内部的, 这时fn内部的所有局部变量对fn2是可以访问的. 但是反过来不可以, fn2内部的局部变量对fn是不可见的, 这就是js的链式作用域
结构, 子对象会一级一级地向上寻找所有父对象的变量。所以,父对象的所有变量,对子对象都是可见的,反之则不成立。
所以fn2函数就是闭包
, 第一个函数fn
执行以后, f
被赋值了 f = function fn2() { console.log(a)}
, 每次调用fn函数的时候, 都是返回的这个函数, 因为这个函数在第一个函数内部, 所以即使第一个函数执行完, 第二个函数依然能访问到a(作用域链)
根据阮一峰的理解闭包就是能够读取其他函数内部变量的函数
而且在JavaScript高级程序设计中也有提到`闭包是指有权访问另一个函数作用域中的变量的函数
那么闭包的作用
很显然就是
1. 可以在函数外部访问到函数内部的局部变量
: 函数执行, 形成私有的执行上下文, 使内部私有变量不受外界干扰,起到保护
(避免命名冲突)和保存
(解决循环绑定引发的索引问题)作用
2. 让这些变量始终保存在内存中, 不会随着函数的结束而自动销毁
: 是变量不会被垃圾回收机制回收
使用闭包的注意点
1. 产生内存消耗问题
: 由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。
2.由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。
思考题
以下两个例子是阮一峰
老师给出的思考题
var name = "The Window";
var object = {
name : "My Object",
getNameFunc : function(){
return function(){
return this.name;
};
}
};
alert(object.getNameFunc()())
结果是The Window
why?
我们可以把最后一句拆分一下
var fn1 = object.getNameFunc();
console.log(fn1());
由于执行fn1()函数时是 window.fn1(), 所以这里的this指向是window, 所以window.name就等于The window
var name = "The Window";
var object = {
name : "My Object",
getNameFunc : function(){
var that = this;
return function(){
return that.name;
};
}
};
alert(object.getNameFunc()());
结果是My Objec
我们用同样的方式
var fn1 = object.getNameFunc();
console.log(fn1());
对于object.getNameFunc()
我们可以知道, 它的this指向的是它的调用者object, 在执行fn1之前, 声明了一个that变量来保存object, 所以执行fn1()时, 此时的that就是object, that.name当然就是
My Object