长久以来,闭包是前端同学面试必考的问题。会用闭包也成了高级前端开发者的标志,今天就来彻底弄清楚闭包的每一个细节。
1.闭包是什么?
比较官方的定义是:一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分。
我个人的简化理解是:闭包就是函数及函数上下文环境的集合。上下文可以理解为函数可以访问到的所有变量。
很多人肯定这样写过JavaScript代码:
var paramA = "test";
var functionA = function(){
console.log(paramA);
}
这样写其实就是使用了闭包的概念,只是很多人并没有意识到这是闭包。这段代码值得注意的地方是函数functionA内部调用了函数外的变量paramA。这就是闭包的鲜明特征。
2.为什么会有闭包?
闭包是JavaScript链式作用域的副产品。闭包不是JavaScript独有的特性,和JavaScript有类似作用域设计的语言也存在闭包,比如Python。
链式作用域的设计决定了子作用域中的函数可以访问到父级作用域的变量,嵌套函数可以访问到外部函数的变量。
借用一张图来说明一下JavaScript的链式作用域。
3.闭包如何使用?
前面的小例子虽然体现了闭包的特征,但并不是一个真正的闭包函数。看下面这个例子:
function a() {
var i = 0;
function b() { console.log(i++); }
return b;
}
var c = a();
c();
这里函数b就是一个闭包函数,那为什么要用c去调用函数a呢?这是为了防止JavaScript的垃圾回收机制生效。在Javascript中,如果一个对象不再被引用,那么这个对象就会被GC回收。如果两个对象互相引用,而不再被第3者所引用,那么这两个互相引用的对象也会被回收。因为函数a被b引用,a又被c引用。这样函数a和b才不会被回收。
4.闭包有什么作用?
我认为闭包的主要作用有两点:
1.保持变量常驻内存,不被回收。
2.实现私有的方法和属性,禁止外部访问。
第一点很好理解,比如上面的例子中i就会常驻内存,每次执行c函数都会得到i的值。这种情况很常见,比如游戏中的的的分数等。
第二点是闭包最重要的用处,还是以上面的例子来说。i是函数a的局部变量,如果想修改i的值,只有调用函数b。其他外部函数是无法访问到变量i的,这样就保证了变量i的安全。
5.闭包的缺点
事物都有两面性,闭包在带来方便的同时也有一些弊端。
因为闭包函数会使变量常驻内存,如果使用不当。比如在循环中使用闭包,有可能导致内存压力过大。
在IE中会导致内存泄漏,这是IE的bug并不是闭包的问题。
6.我的观点
我猜测闭包是JavaScript在设计之初未曾想到过的用法,如果业务中没有强烈的需求,尽量不要使用闭包。