從JQuery出發總結的關於原型使用上的一些淺薄理解

zxl20070701發表於2023-03-12

比如我們現在的需求如下。

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

相關文章