闭包
闭包是指一个函数,该函数有权访问另一个函数作用域中的变量。创建闭包的常用方式就是在一个函数内部创建另一个函数。
1 作用域链
当创建一个函数时,会预先创建一个包含其外部环境变量对象的作用域链,保存在其内部的[[Scope]]属性中。
每个函数都有自己的执行环境,作用域链就是保证在函数执行时对所有有权访问的变量和函数的有序访问。
函数执行时,首先创建其执行环境,然后复制[[Scope]]属性构建执行环境的作用域链,将函数的活动对象作为变量对象(最初只有arguments一个参数)推入作用域链的前端。
作用域链本质上是一个指向变量对象的指针列表。函数访问一个变量时,就沿着作用域链搜索具有相同名字的变量,只到找到为止。如果在全局变量对象中都找不到,一般会报错。
2 闭包与变量
在创建闭包时,实际上其作用域链中会包含外部函数的活动对象。若当外部函数执行完毕返回匿名函数后,外部函数其执行环境的作用域链会被销毁,但是其活动对象会被保留在内存中,原因是返回的匿名函数中的作用域链依旧在引用它。
对于内部函数中的this和arguments两个变量,只会搜索到其活动对象为止,所以闭包匿名函数其this对象通常指向window。
2.1 创建块级作用域
//可以用匿名函数模仿块级作用域
//可以减少全局变量
(function(){
//块级作用域(私有作用域)
})()
2.2 私有变量
为自定义类型创建私有变量和特权方法
1.构造函数实现
function Person(name){
this.getName = function(){
return name;
};
this.setName = function (value) {
name = value;
};
}
var person = new Person("Nicholas");
alert(person.getName()); //"Nicholas"
person.setName("Greg");
alert(person.getName()); //"Greg"
这种方式就必须使用构造函数模式创建实例,是个实例中都有自己的方法。
2.静态私有变量实现
(function(){
var name = "";
Person = function(value){
name = value;
};
Person.prototype.getName = function(){
return name;
};
Person.prototype.setName = function (value){
name = value;
};
})();
var person1 = new Person("Nicholas");
alert(person1.getName()); //"Nicholas"
person1.setName("Greg");
alert(person1.getName()); //"Greg"
var person2 = new Person("Michael");
alert(person1.getName()); //"Michael"
alert(person2.getName()); //"Michael"
特权方法在原型上定义,体现的是原型模式。但私有变量和私有函数都是由实例共享的。
为单例对象创建私有变量和特权函数
1.模块模式
// 返回的对象字面量中只包含公开的属性和方法
function BaseComponent(){
}
function OtherComponent(){
}
var application = function(){
//private variables and functions
var components = new Array();
//initialization
components.push(new BaseComponent());
//public interface
return {
getComponentCount : function(){
return components.length;
},
registerComponent : function(component){
if (typeof component == "object"){
components.push(component);
}
}
};
}();
console.log(application);
application.registerComponent(new OtherComponent());
alert(application.getComponentCount()); //2
2.增强的模块模式
//单例必须是某种类型的实例
function BaseComponent(){
}
function OtherComponent(){
}
var application = function(){
//private variables and functions
var components = new Array();
//initialization
components.push(new BaseComponent());
//create a local copy of application
var app = new BaseComponent();
//public interface
app.getComponentCount = function(){
return components.length;
};
app.registerComponent = function(component){
if (typeof component == "object"){
components.push(component);
}
};
//return it
return app;
}();
alert(application instanceof BaseComponent);
application.registerComponent(new OtherComponent());
alert(application.getComponentCount()); //2