闭包
闭包,在JavaScript中很有意思。JavaScript是链式作用域,所以可以将函数内部与函数外部连接起来。由于在Javascript中,只有函数内部的子函数才能读取函数内部的变量,因此可以把内部的函数返回,通过返回的函数就能够读取到函数内部的变量。
于是,可以把闭包简单理解成定义在一个函数内部的函数,并且该内部函数访问了外部的变量。
console.log("-----闭包-----");
function fun(){
var a = 1;
return function f(){
console.log(a);
};
}
var result = fun()();
闭包与变量
链式作用域会带来一个问题,在闭包中,取得的包含函数的变量,是其最终值。举个例子
console.log("-----闭包与变量-----");
function createFunctions(){
var array = new Array();
for(var i=0;i<10;i++){
array[i] = function(){
return i;
}
}
return array;
}
var result = createFunctions();
console.log(result[0]()); //10
上面的例子中,得到的是10并不是0
可以通过建立一个匿名函数。并且立即执行该匿名函数来解决
function createFunctions(){
var array = new Array();
for(var i=0;i<10;i++){
array[i] = function(num){
return function(){
return num;
}
}(i);
}
return array;
}
var result = createFunctions();
console.log(result[0]()); //0
关于this对象
console.log("-----关于this对象-----");
var name = "The Windows";
var o = {
name : "The O",
getNameFun : function(){
return function(){
return this.name;
};
}
}
console.log(o.getNameFun()()); //The Windows
var object = {
name : "The Object",
getNameFun : function(){
var that = this;
return function(){
return that.name;
};
}
}
console.log(object.getNameFun()()); //The Object
var myObject = {
name : "myObject",
getName : function(){
return this.name;
}
}
console.log(myObject.getName());
console.log((fun=myObject.getName)()); //The Windows
模仿块级作用域
JavaScript 没有块级作用域的概念。这意味着在块语句中定义的变量,实际上是在包含在函数中而非语句中创建的
function fun(num){
for(var i=0;i<num;i++){
//do something
}
console.log(i);
}
fun(5); //5
可以看出变量i并不会如同在C++和Java等语言中,随着循环的结束而销毁,即便是重新声明,不初始化,也不会改变它的值。
可以利用匿名函数来膜(模?)仿块级作用域来解决这个问题
(function(){
//块级作用域
})();
使用这种方式,可以限制向全局作用域中添加过多的变量和函数,同时减少闭包占用内存的问题。
私有变量
JavaScript中任何在函数中定义的变量,都可以认为是私有变量,因为不能在函数的外部访问这些变量,但是可以通过闭包来访问这些变量。私有变量包括函数的参数、局部变量和在函数内部定义的其他函数。
console.log("---私有变量---");
function Person(name){
this.age = 22;
this.getName = function(){
return name;
};
this.setName = function(value){
name = value;
}
}
var person = new Person("Nico");
console.log(person.age); //22
console.log(person.name); //undefined
console.log(person.getName()); //Nico
上面的例子中有个问题,就是每个实例都会创建一组相同的方法。为了解决这个问题,可以使用静态私有变量。
静态私有变量
console.log("-----静态私有变量-----");
(function(){
var name = "";
Person = function(value){
name = value;
}
Person.prototype.getName = function(){
return name;
};
Person.prototype.setName = function(value){
name = value;
}
})();
var staticPerson1 = new Person("Nico");
var staticPerson2 = new Person("Sora");
console.log(staticPerson1.getName()); //Sora
这个例子中的 Person 构造函数与 getName() 和 setName()方法一样,都有权访问私有变量 name 。在这种模式下,变量 name就变成了一个静态的、由所有实例共享的属性。也就是说,在一个实例上调用 setName() 会影响所有实例。而调用 setName() 或新建一个 Person 实例都会赋予 name属性一个新值。结果就是所有实例都会返回相同的值。
以这种方式创建静态私有变量会因为使用原型而增进代码复用,但每个实例都没有自己的私有变量。
模块模式
模块模式主要是为单列创建私有变量和特权方法的。其语法如下:
console.log("-----模块模式-----");
var single = function(){
var privateProperty = "private";
function privateFun(){
return "privateFun"
};
return {
publicProperty : "public",
publicFun : function(){
return "publicFun";
},
getPrivate : function(){
return privateProperty;
}
};
}();
console.log(single.publicFun()); //publicFun
console.log(single.getPrivate()); //private
模块模式使用了一个返回对象的匿名函数。在这个匿名函数内部,首先定义了私有变量和函数。然后,将一个对象字面量作为函数的值返回。返回的对象字面量中只包含可以公开的属性和方法。
由于这个对象是在匿名函数内部定义的,因此它的公有方法有权访问私有变量和函数。从本质上来讲,这个对象字面量定义的是单例的公共接口。这种模式在需要对单例进行某些初始化,同时又需要维护其私有变量时是非常有用的。例如:
var application = function(){
var components = new Array(); //私有变量
components.push(new BaseComponent()); //私有变量初始化
//公共方法
return {
getComponentCount : function(){
return components.length;
},
registerComponent : function(component){
if(typeof component == "object"){
components.push(component);
}
}
};
}();
对于必须创建一个对象并以某些数据对其进行初始化,同时还要公开一些能够访问这些私有数据的方法,适合模块模式。