比如我們現在的需求如下。
有一個函數MyObj,支持如下功能:
- 可以通過MyObj()的方式返回一個對象,這個對象和new MyObj()是等價的
-
MyObj本身是一個對象,可以通過MyObj.doit()的方式調用其上的方法或屬性
為了實現需求,第一反應是:var MyObj=function(){ return new MyObj(); };然後在MyObj上掛載靜態方法,在MyObj.prototype上掛載對象方法。
看起來穩的很,其實這明顯是一個死循環:
// VM160:2 Uncaught RangeError: Maximum call stack size exceeded
MyObj();
為了解決這個問題,我們在MyObj的原型上定義了一個方法:
MyObj.prototype.init=function(){
return this;
};
執行下面的方法:
var temp=MyObj.prototype.init();
上面返回的temp很明顯就是MyObj.prototype,其實就是MyObj對象(例如:new A(),其實就是取A.prototype,這樣對比就很好理解了)。
因此可以改造代碼如下:
var MyObj = function (param) {
return MyObj.prototype.init();
};
這樣MyObj和new MyObj()就分別表示類和對象。
問:看起來是不是實現了?
答:是的,實現了。
問:可是總感覺有點不好,説不出為什麼。
答:是不是感覺MyObj()打印出來的東西有點多?
問:是的。
事實上,因為直接取MyObj.prototype作為new MyObj(),理論上説,使用上區別不大,唯一不足的是,掛載在MyObj.prototype上的方法會在打印MyObj對象的時候看見,不舒服。
為了看起來好看些,代碼再次改造:
var MyObj = function () {
return new MyObj.prototype.init();
};
// 為了讓MyObj()返回的是MyObj對象,需要修改MyObj.prototype.init的原型
MyObj.prototype.init.prototype = MyObj.prototype;
此刻的原型關係變成了:
MyObj() ==
return new MyObj.prototype.init() ==
MyObj.prototype.init.prototype ==
MyObj.prototype ==
new MyObj()
此時需求就實現了,而且打印MyObj()的時候,對象上的方法都在原型上,看起來就比較舒服了。