JavaScript中并没有类,因此对单体咬文嚼字的定义严格说来并没有意义。但是JavaSctipt中具有new语法可使用构造函数来创建对象,而且有时可能需要使用这种语法的单体实现。这种思想在于当使用同一个构造函数以new操作符来创建多个对象时,应该仅获得指向完全相同的对象的新指针。
下面的代码片段显示了其预期行为:
var one = new Universe();
var two = new Universe();
one === two; // 结果为true
在JavaScript中要实现上述模式,需要Universe构造函数缓存该对象实例this,以便当第二次调用该构造函数时能够创建并返回同一个对象。有多种选择可以实现这一目标:
- 可以使用全局变量来存储该实例。
- 可以在构造函数的静态属性中缓存该实例。
- 可以将该实例包装在闭包中。
但是全局变量容易被覆盖,因此不推荐此种方法。在静态属性中缓存该实例的缺点在于此实例属性可被公开访问,在外部代码中可能会修改此属性。而在闭包中的实例,可以保证实例的私有性,并且保证该实例不会被构造函数之外的代码所修改。其代价就是带来了额外的闭包开销。
下面,让我们来看看第二种和第三种方法的实现实例。
静态属性中的实例
function Universe() {
if (typeof Universe.instance === 'object') {
return Universe.instance;
}
this.start_time = 0;
this.bang = "Big";
Universe.instance = this;
// 隐式返回
// return this;
}
测试用例如下:
var one = new Universe();
var two = new Universe();
one === two; // 结果为true
闭包中的实例
function Universe() {
// 缓存实例
var instance;
Universe = function Universe() {
return instance;
}
// 保留原型属性
Universe.prototype = this;
// 实例
instance = new Universe();
// 重置构造函数指针
instance.constructor = Universe;
// 所有功能
instance.start_time = 0;
instance.bang = "Big";
return instance;
}
测试用例如下:
Universe.prototype.something = "some thing";
var one = new Universe();
Universe.prototype.some1thing = "some 1 thing";
var two = new Universe();
console.log(one === two); // true
console.log(one.something, two.something); // some thing some thing
console.log(one.some1thing, two.some1thing);// some 1 thing some 1 thing
console.log(one.bang, two.bang); // Big Big
console.log(one.constructor === Universe); // true
另外一种解决方案是将构造函数和实例包装在即时函数中,代码如下:
var Universe;
(function () {
var instance;
Universe = function Universe() {
if (instance) {
return instance;
}
instance = this;
//所有功能
instance.start_time = 0;
instance.bang = "Big";
}
}());
网友评论