引言
对于 js 程序员而言, 很多时候, 我们经常会被问到闭包。由于变量搜索是从内到外的特性, 最直白的解释就是:变量的值是由定义时候的上下文决定的, 而不是代码执行的时候决定的 。 跟面试官这么解释, 往往会被认为是把闭包与变量的作用域搞混了, 认为你对闭包理解不够。而实际上, 并非如此。
变量的声明周期
与闭包有关的概念就是变量的生命周期
var function(){
var a=1;
return function(){
a++;
console.log(a);
}
}
var f = func();
var a=100;
f() // 2
f() // 3
f() // 4
f() // 5
函数调用结束后, a并没有消失, 而是再一个闭包环境中一直存活者, 局部变量的生命周期被延续了。
闭包的更多作用
变量封装
var cache = {};
var mult = function(){
var args = Array.prototype.join.call(arguments, ',');
if(cache[args]) {
return cache[args];
}
var a=1;
for(var i=0; i<arguments.length; i++){
a = a*arguments[i];
}
return cache[args] = a;
}
以上的代码中, cache 与 mult 被暴露再全局作用于下, 用闭包进行封装
var mult = (function(){
var cache = {};
return function(){
var args = Array.prototype.join.call(arguments, ',');
if(cache[args]) {
return cache[args];
}
var a=1;
for(var i=0; i<arguments.length; i++){
a = a*arguments[i];
}
return cache[args] = a;
}
})();
对于上面的代码还可以进一步封装, 但是本文只是讲解闭包。
延续局部变量的寿命
过程和数据的集合, 是面向对象编程最基本的套路, 对象在方法的执行中过程中被引用, 而闭包则是在方法执行的过程中以上下文的形式包含了内容, 面向对象能实现的, 必要也能实现。
闭包实现
var extent = fucntion(){
var value-0;
return {
call: function(){
value++;
console.log(value);
}
}
}
面向对象实现
vat extent = {
value: 0,
call: function(){
this.value++;
console.log(this.value);
}
}
闭包与内存
经常有人说,闭包的坏处, 一种说法就是闭包会造成内存泄漏, 所以尽量少用闭包。而这种说法最直接的原因就是对于主动回收闭包的内存毫无意识。
局部如果被封闭在闭包形成的环境中, 那么这个局部变量就会一直存在下去, 这样看, 闭包确实会使一些数据无法及时的被销毁。 而对于内存的影响, 并不能说成事内存泄漏。 如果要回收这些变量, 我们可以手动的把这些变量设置为null, null 意味这切断了变量与它此前引用值之间的连接。 当垃圾收集器下次运行的时候, 就会删除这些值并回收他们所占用的内存。