1. 什么是闭包
在一个函数内部创建另一个函数,即为闭包,闭包的特殊之处在于如果将这个闭包(内部的函数)返回,即使外部函数执行完毕,闭包仍能正常访问外部函数中的变量。
2. 闭包的原理
闭包仍能访问外部函数中的变量的原因是,即使外部函数执行完毕,此时该函数的作用域链被销毁,但是该函数的变量对象仍在闭包的作用域中。详细介绍参见闭包。
3.闭包的使用
3.1 经典例子
//未使用闭包
var arr = [];
for(var i=0;i<10;i++){
arr[i] = function(){
console.log(i)
}
}
for(let a of arr){
a(); // 皆为10
}
//使用闭包
var arr = [];
for(var i=0;i<10;i++){
//这里使用了立即执行函数,在该函数内部,返回了一个函数,即为闭包,此时情况符合预期
arr[i] = (function(i){
return function(){
console.log(i);
}
})(i);
}
for(let a of arr){
a(); //0,1,2,3,4,5,6,7,8,9
}
3.2 闭包中的this
在不使用bind,call,apply的情况下,闭包中的this即为全局对象(window)。先看代码
var obj = {
name:'wang',
age:12,
getName:function(){
return function(){
console.log(this===global); //浏览器下全局对象为window
}
}
}
obj.getName()(); //打印true
从以上代码中可以看出,闭包中的this即为全局对象,为什么不是外层函数的this?原因是每个函数在被调用时,都会自动获得两个特殊的变量:this和arguments。 闭包在搜索这两个变量时,只会在其活动对象中进行搜索,不会沿着作用域往上查找,因此永远不可能直接访问到外部函数中的this值。如果想使用外层函数中的this,可以将this保存到闭包可以访问的变量里,就可以让闭包访问到这个this。
var obj = {
name:'wang',
age:12,
getName:function(){
var self = this;
return function(){
console.log(self.name);
}
}
}
obj.getName()(); //打印处'wang'
3.3 块级作用域
ES5下,javascript没有块级作用域,利用匿名函数可以模仿出块级作用域。
(function(){
var name='wang';
var age=12;
})() //需将匿名函数用括号包裹起来。原因是javascript将function关键字作为函数声明的开始,而函数声明后面不能跟括号,而函数表达式则无此限制。
console.log(name,age) //undefined,undefined
以上的代码使用匿名函数,并立即执行该函数,模拟了一个块级作用域,在匿名函数内声明的变量,外部函数无法使用。
以上是在全局作用域中模仿块级作用域,当在函数内部,临时需要一些变量,使用同样的方法,也可以构造块级作用域。
function outer(num){
(function(){
for(var i=0;i<num;i++){
console.log(i);
}
})(); //闭包
console.log(i); //undefined
}
3.4 私有变量
javascript没有提供私有成员的语法,但是利用在函数内部定义的变量,包括函数的参数,函数内部的变量和函数,无法在函数外被访问的特性,使用闭包,也可以模拟出私有成员。
有权访问函数内变量的方法被成为特权方法。
特权方法的定义有两种:
- 在构造函数中定义特权方法。
function Person(){ //私有变量和私有函数 var name='wang'; function getName(){ return name; } //特权方法 this.publicMethod = function(){ name='zhou'; return getName(); } } var person = new Person(); console.log(person.publicMethod()) //打印出'zhou' console.log(person.name) //undefined - 静态私有变量
可以在私有作用域里创建私有变量或函数,以及特权方法。
(function(){
//私有变量和私有函数
var name='wang';
function getName(){
return name;
};
//构造函数,注意两点:
//1.使用的是函数表达式,而不是函数声明,因为在此处函数声明只能声明出局部函数。
//2.Person未声明,初始化未经声明的变量,会创建一个全局变量。
//以后这些操作是为了将Person创建为一个全局的函数。
Person = function(){};
//特权方法定义在Person函数的原型上,这将使私有变量/函数被所有的实例共享,原因是,特权方法定义在原型上,所以所有的实例都共享该方法,而该方法又是一个闭包,这个闭包的作用域中包含着name。
Person.prototype.publicMethod = function(){
name='zhou';
return getName();
}
})();
- 单例模式的增强 单例即是只有一个实例的对象,以对象字面量创建的对象即为单例对象。可以为单例对象添加私有变量和特权方法,增强单例对象。
var person = function(){
//私有变量/函数
var name='wang';
function getName(){
return name;
}
return {
publicPorterty:true,
publicMethod:function(){
name='zhou';
return getName();
}
}
}() //立即执行。
//该函数在定义了私有变量/函数后,返回一个对象,由于这个对象是在函数体中定义的,有权访问函数内定义的变量和方法。
以上严重参考高程,记录下这些文字,是为了方便记忆,如能帮到你,也是极好的。