闭包,官方对闭包的解释是:一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分。
我理解的闭包就是在函数A里定义一个函数B,函数B可以操作函数A里面的变量。因为JavaScript的垃圾回收机制,当一个函数执行完之后,就会被销毁,当我们调用闭包时,也就是调用函数B时,JavaScript会认为这个函数没有执行完毕,所以闭包所在的内存空间A就不会被销毁,我们依然可以通过B来访问A里面的变量。
有句话叫存在即合理,所以既然闭包成为面试必考题是有其原因的,主要原因之一就是闭包是全局变量和函数之外的第三种存在形式的局部变量,首先我们聊一下全局变量和函数
全局变量
JavaScript的var默认定义的就为全局变量,但是全局变量容易削弱程序的灵活性,尤其是在多人协作时,容易发生变量冲突,解决这个问题有两种方式,
- 定义命名空间
定义命名空间是只创建一个全局变量,并定义该变量为当前应用容器,把其它的变量追加到这个变量容器内
var MY={};
my.name={
big_name:"zhangsan",
small_name:"lisi"
};
my.work={
school_work:"study",
family_work:"we are"
}; - 利用匿名函数将脚本包裹起来,对的,没错,这就是一个闭包
这种方式其实和命名空间类似,其实它应该有另一个名字,叫单例模式,即一个只执行一次的类,我们可以通过执行后的实例暴露出的方法来访问该类的一些东西。
(function(){
var exp={};
var name="aa";
exp.method=function(){
return name;
};
window.ex=exp;
})();
alert(ex.method())
函数
函数是JavaScript中一个很有趣的存在,在JavaScript中,一切皆对象,函数也是,但是对象又是由函数创建的,有点类似于是鸡生蛋还是蛋生鸡的问题。
就如我们在前面提到的,JavaScript的垃圾回收机制,一旦一个函数执行完毕,就会被销毁,但是闭包的存在又使得函数即使执行完毕,也能依然存在。JavaScript中的封装和继承都是使用的闭包,而且闭包能让代码更加优雅易懂。例如下面的代码:
- 封装
var person = function(){
//变量作用域为函数内部,外部无法访问
var name = "default";
return {
getName : function(){
return name;
},
setName : function(newName){
name = newName;
}
}
}();
print(person.name);//直接访问,结果为undefined
print(person.getName());
person.setName("abruzzi");
print(person.getName());
得到结果如下:
undefined
default
abruzzi
2.继承
function Person(){
var name = "default";
return {
getName : function(){
return name;
},
setName : function(newName){
name = newName;
}
}
};
var p = new Person();
p.setName("Tom");
alert(p.getName());
var Jack = function(){};
//继承自Person
Jack.prototype = new Person();
//添加私有方法
Jack.prototype.Say = function(){
alert("Hello,my name is Jack");
};
var j = new Jack();
j.setName("Jack");
j.Say();
alert(j.getName());