在JavaScript中有许多解决特定问题的编码思维(设计模式),例如工厂模式,订阅通知模式,装饰模式,单例模式等。其中单例模式是我们在实践中最常用的模式之一。而它的实现,与闭包息息相关。
所谓单例模式,就是只有一个实例。
最简单的单例模式
对象字面量的方法就是最简单的单例模式,我们可以将属性与方法依次放在字面量里。
var per = {
name: 'Jake',
age: 20
getName: function() {
return this.name;
},
getAge: function() {
return this.age;
}
}
但是这样的单例模式有一个严重的问题就是他的属性可以被外部修改。因此在许多场景这样的写法并不符合我们的需求。我们期望对象能够有自己的私有方法与属性。
有私有方法/属性的单例模式
通过前面所学的知识我们很容易就能想到,想要一个对象拥有私有的方法属性,那么只需要创建一个单独的作用域将对象与外界隔离起来就行了。这里我们借助匿名函数自执行的方式即可。
var per = (function() {
var name = 'Jake';
var age = 20;
return {
getName: function() {
return name;
},
getAge: function() {
return age;
}
}
})();
// 访问私有变量
per.getName();
有私有变量的好处在于,外界对于私有变量能够进行什么样的操作,是可以控制的。我们可以提供一个getName方法让外界可以访问到名字,也可以额外提供一个setName方法,来修改它的名字。对外提供什么样的能力,完全由我们自己决定。
不知不觉中,我们已经利用了闭包来解决我们的问题。匿名函数(称为A)中,当name被getName访问时,闭包就已经产生。因此A中的两个属性都会被保留下来。
这个时候离我们最终的模块化思维已经比较接近了,在模块化的开发中,每一个模块都是一个与此类似的单例模式。如果在后面学习模块化开发时遇到了困难,不妨回到这里来理一下思路。
调用时才初始化的单例模式
有的时候(使用频次较少)我们希望自己的实例仅仅只是在我们调用的时候才会初始化,而不是如上面两个例子那样,即使没有调用per,per的实例在函数自执行的时候就返回了。
那么我们就需要在上面例子的基础上,做一个简单的判断。
var per = (function() {
// 定义一个变量,用来保存实例
var instance = null;
var name = 'Jake';
var age = 20;
// 初始化方法
function initial() {
return {
getName: function() { return name; },
getAge: function() { return name; }
}
}
return {
getInstance: function() {
if (!instance) {
instance = initial();
}
return instance;
}
}
})();
// 只在使用时获得实例
var p1 = per.getInstance();
var p2 = per.getInstance();
console.log(p1 === p2); // true
这个例子,我们在匿名函数中定义了一个instance变量用来保存实例。在getInstance方法中判断了是否对他进行重新赋值。由于这个判断的存在,其实变量instance仅仅只在第一次调用getInstance方法时赋值了。所以这样写完美符合了单例模式的思路。
这个例子中利用闭包的思路其实与上一个例子一样,这里就不多做分析,留给大家自己思考以进一步熟悉闭包。