在JavaScript中,根据作用域链规则,函数内部可以很容易的获取全局变量,函数内部的局部作用域中的变量只有子函数才可以读取,要从外部获取内部变量却不是很简单。闭包就是用来解决这个问题的,既然子函数可以读取局部作用域变量,那就把子函数当做函数返回值,返回给父函数不就能够从外部获得局部变量。这就是闭包,将函数内部与外部连接起来。
一、理解闭包
先看一个简单的闭包。
var name = 'global'
function f() {
var name = 'local'
function f1(){
console.log(name)
}
return f1()
}
f() //local
简单解释一下,f()返回了函数内部执行的f1()的结果
稍微对上面代码做一些改动
var name = 'global'
function f() {
var name = 'local'
function f1(){
console.log(name)
}
return f1
}
var f2 = f() //function f1() { console.log(name) }
f2() //local
这样看上去有点像this中的执行上下文,f2是一个函数啊,他是在Window下执行的,难道不应该是name=global吗?
但其实并不是,js采用的是词法作用域,函数执行时依赖的是函数定义时决定的作用域,而不是执行时决定的作用域,也就是说当你定义这个闭包时,这个name就已经确定了。
二、闭包的用途
1、很明显,第一个最大的用途就是读取函数内部的变量。
2、另外一个就是将某些不希望被垃圾回收机制回收的值保存下来
什么是垃圾回收机制,看个例子:
function f() {
var n=1
n+=1
console.log(n)
}
f() //2
f() //2
... //2
无论执行多少次n都是2,也就是说每次函数执行完n都被初始化了,也就是回收了。
当我们使用闭包时:
function f() {
var n = 1
function f1() {
n += 1
console.log(n)
}
return f1
}
var f2 = f()
f2() //2
f2() //3
f2() //4
...
n会一直增加,也就是说n被保留了下来。
三、闭包的局限
1、由于闭包可以保存变量的特性,如果滥用闭包会造成大量的内存被占用,所以,最好的方式就是在退出函数之前将闭包中的不再需要的变量清除。
2、闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值。