我眼中的JavaScript闭包
一、变量的作用域
要理解闭包,首先必须理解Javascript特殊的变量作用域。
变量的作用域无非就是两种:全局变量和局部变量。
JavaScript语言在函数内部可以访问外部的变量,但是在函数外部不可以使用函数内部的变量,也就是局部变量。
在函数内部可以访问外部的变量。
比如这样:
var num=20; function func1(){
console.log(num);
}
func1(); //得到的值为20
另一方面,在函数外部自然无法读取函数内的局部变量。
比如这样:
function func1(){ var num=20; } console.log(num); // num is not defined(报错)
二、如何在函数外部去得到局部变量的值?
有时候,我们想要去得到函数内部的变量,应该怎么办呢?我们可以在函数的内部,再定义一个函数,从而来达到我们的目的。
function func1(){ var num = 20; function func2(){ return num } return func2; } var result = func1(); console.log(result()); //得到的值为20
在上面的代码中,函数func2被包括在函数func1内部,这时func2可以调用func1中的所有局部变量。但是反过来就不行,func1不能调用func2内部的局部变量。这就是Javascript语言特有的作用域链。子级会一级一级地向上寻找所有父级的变量,最终找到全局变量为止。
在这个段代码中,函数func2就是闭包。
三、闭包的概念
官方的解释是:闭包是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分。相信很少有人能直接看懂这句话,因为他描述的太学术。
我们可以简单地理解为,闭包就是能够读取其他函数内部变量的函数,在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。
四、闭包的用途
闭包最大的用处有两个:
① 可以读取函数内部的变量
② 变量持久化,让变量的值始终保持在内存中
如何来理解变量持久化呢?请看下面这段代码:
function fn1(){ var num = 18; function fn2(){ return ++num; } return fn2; } var result = fn1(); console.log(result()); //得到的值为19 console.log(result()); //得到的值为20 console.log(result()); //得到的值为21
在这段代码中,result实际上就是闭包fn2函数。它一共运行了三次,第一次的值是19,第二次的值是20,第三次的值是21。这证明了,函数fn1中的局部变量num一直保存在内存中,并没有在fn1调用后被自动清除。
为什么会这样呢?原因就在于fn1是fn2的父函数,而fn2被赋给了一个全局变量,这导致fn2始终在内存中,而fn2的存在依赖于fn1,因此fn1也始终在内存中,不会在调用结束后,被垃圾回收机制(garbage collection)回收。
五、闭包的优缺点
优点:
① 逻辑连续,当闭包作为另一个函数调用的参数时,避免你脱离当前逻辑而单独编写额外逻辑。
② 方便调用上下文的局部变量。
缺点:
由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。
闭包和作用域有着密不可分的关系,真的是“想说爱你不容易”!
全文完!