之前写过一篇文章《关于underscore.js源码的一些理解与笔记》,里面涉及到的自调用构造函数,代码如下:
var _ = function(obj) {
if (obj instanceof _) return obj;
if (!(this instanceof _)) return new _(obj);
this._wrapped = obj;
};
这段代码不管以函数的方式或者是以构造函数的方式调用,都会返回一个继承 _.prototype 的对象。但这种模式有一个缺点,使用函数调用时,需要额外的函数调用,代价比较高。可以通过以下代码来验证:
var _ = function(obj) {
console.log('create_');
if (obj instanceof _) return obj;
if (!(this instanceof _)) {
console.log('new_');
return new _(obj);
}
this._wrapped = obj;
console.log('wrapped_');
},
a = _(), //log: create_ new_ create_ wrapped_
b = new _(); //log: create_ wrapped_
那么,我们该如何来优化这种方式呢?
利用ES5的Object.create函数来创建对象。Object.create() 方法创建一个拥有指定原型和若干个指定属性的对象。修改后的代码如下:
var _ = function(obj) {
if (obj instanceof _) return obj;
var self = this instanceof _ ? this : Object.create(_.prototype);
self._wrapped = obj;
};
采用如下代码做验证:
var _ = function(obj) {
console.log('create_');
if (obj instanceof _) return obj;
var self = this instanceof _ ? this : Object.create(_.prototype);
self._wrapped = obj;
console.log('wrapped_');
},
a = _(), //log: create_ wrapped_
b = new _(); //log: create_ wrapped_
这时候,不管采不采用new实例化对象,都只会调用一次,同时将构造函数的参数解耦出来,可以适用于可变参数函数。但Object.create只有在ES5环境中才有效,在旧环境需要创建一个局部的构造函数并使用new初始化的函数来替代Object.create。
简单起见,实现一个单参数版本,代码如下:
if (typeof Object.create === 'undefined') {
Object.create = (function(prototype) {
function C(){}
return function(prototype) {
C.prototype = prototype;
return new C();
}
}());
}