比如我們現在的需求如下。
有一個函式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()的時候,物件上的方法都在原型上,看起來就比較舒服了。