2010-05-06 66 views
23

時間一般不超過原型的功能,我見過的類定義之外聲明原型的功能,如:設置JavaScript對象類聲明

function Container(param) { 
    this.member = param; 
} 
Container.prototype.stamp = function (string) { 
    return this.member + string; 
} 

var container1 = new Container('A'); 
alert(container1.member); 
alert(container1.stamp('X')); 

此代碼將產生兩個警報與價值觀「A」和「AX」 。

我想定義類定義的原型函數INSIDE。做這樣的事情有什麼不妥嗎?

function Container(param) { 
    this.member = param; 
    if (!Container.prototype.stamp) { 
     Container.prototype.stamp = function() { 
      return this.member + string; 
     } 
    } 
} 

我試圖讓我可以訪問類中的私有變量。但我發現,如果我的原型函數引用私人變種,私營var值始終是當函數原型最初創建,而不是在對象實例的值被使用的值:

Container = function(param) { 
    this.member = param; 
    var privateVar = param; 
    if (!Container.prototype.stamp) { 
     Container.prototype.stamp = function(string) { 
      return privateVar + this.member + string; 
     } 
    } 
} 
var container1 = new Container('A'); 
var container2 = new Container('B'); 
alert(container1.stamp('X')); 
alert(container2.stamp('X')); 

此代碼會生成兩個值爲「AAX」和「ABX」的警報。我希望輸出將是「AAX」和「BBX」。我很好奇爲什麼這不起作用,如果有其他的模式我可以使用。

編輯:請注意,我完全理解,對於這個簡單的例子,最好只使用像this.stamp = function() {}這樣的閉包,而不要使用原型。我就是這麼做的。但我使用的原型,以瞭解更多關於它的實驗和想知道的幾件事情:

  • 當它是有意義的使用原型的功能,而不是關閉?我只需要使用它們來擴展現有的對象,如Date。我讀過closures are faster
  • 如果我需要使用原型函數出於某種原因,是否可以在類的內部定義它,比如在我的示例中,還是應該在外部定義?
  • 我想了解爲什麼每個實例的privateVar值不能被原型函數訪問,只有第一個實例的值。
+0

瓶蓋再刪除線... – Dormilich 2010-05-06 22:09:14

+2

閱讀上封(http://www.jibbering.com/faq/faq_notes/closures.html)的詳細原因,你的代碼的行爲它的方式。 – outis 2010-05-06 22:09:28

回答

21

何時使用原型函數而不是閉包?

嗯,這是最輕便的路要走,讓我們假設你有某些構造的prototype的方法,與您共創1000個對象實例,所有這些對象將在其原型鏈的方法,和所有其中只會涉及一個功能對象

如果您在構造函數中初始化該方法,例如(this.method = function() {};),所有1000個對象實例都將有一個函數對象作爲自己的屬性。

如果我需要使用原型函數出於某種原因,它是「OK」來定義它內部的類,就像在我的例子中,還是應該在外面定義?

定義構造函數原型的成員本身並沒有什麼意義,我會更詳細地解釋它,以及爲什麼代碼不起作用。

我想了解爲什麼每個實例的privateVar值不能被原型函數訪問,只有第一個實例的值。

讓我們來看看你的代碼:

var Container = function(param) { 
    this.member = param; 
    var privateVar = param; 
    if (!Container.prototype.stamp) { // <-- executed on the first call only 
     Container.prototype.stamp = function(string) { 
      return privateVar + this.member + string; 
     } 
    } 
} 

你的代碼的行爲,關鍵的一點是,Container.prototype.stamp功能是在第一個方法調用創建

當前您創建一個函數對象,它將當前的封閉範圍存儲在名爲[[Scope]]的內部屬性中。

當您調用該函數時,通過使用var或函數聲明在其中聲明的標識符(變量),此範圍稍後會增大。

[[Scope]]的屬性列表形成範圍鏈,並且當訪問的標識符(例如您privateVar變量),被檢查的那些對象。

由於您的函數是在第一個方法調用(new Container('A'))上創建的,因此privateVar被綁定到第一個函數調用的作用域,並且無論您如何調用該方法,它都將保持綁定狀態。

看看這個answer,第一部分是關於with聲明,但在第二部分中,我將討論範圍鏈如何適用於函數。

+0

@CMS:很好的解釋,非常感謝!所以如果我的原型函數只訪問'this'而不是私人變量,那麼它會正常工作,即使它被定義在類內部,對吧?有沒有這樣做的缺點,而不是像我通常看到的那樣在類之後聲明原型函數? – Tauren 2010-05-07 01:38:39

+1

@Tauren,是的,有一個缺點,例如在代碼中會出現內存泄漏,在構造函數中的第一次調用中聲明的所有變量都不會被垃圾回收,因爲如你所知,封閉範圍即使在構造函數結束其執行之後(創建閉包),Container.prototype.stamp函數的創建位置仍然可以訪問。出於這個原因,一些圖書館像[Google's Closure](http://code.google.com/closure/library/)避免了* private *成員的關閉,他們只是簡單地遵守命名約定。 'OBJ .__ privateMember'。 – CMS 2010-05-07 02:12:24

+0

非常有意義,謝謝! – Tauren 2010-05-07 05:16:36

1

你需要把功能上的每一個具體的實例,而不是原型的,像這樣的:

Container = function(param) { 
    this.member = param; 
    var privateVar = param; 

    this.stamp = function(string) { 
     return privateVar + this.member + string; 
    } 
} 
+0

@Slaks:我已經修改了這個問題,以便更清楚我究竟在問什麼。 – Tauren 2010-05-06 22:22:16

1

爲了得到你想要的行爲,你需要在每個單獨的對象分配不同stamp()功能與獨特關閉:

Container = function(param) { 
    this.member = param; 
    var privateVar = param; 
    this.stamp = function(string) { 
     return privateVar + this.member + string; 
    } 
} 

當您在原型創建一個單一功能的每個對象使用相同的功能,與功能收盤在第一個集裝箱的privateVar

通過每次調用構造函數時分配this.stamp = ...,每個對象都將獲得自己的stamp()函數。這是必要的,因爲每個stamp()需要關閉一個不同的privateVar變量。

+0

@約翰:謝謝你的回答。我想我的問題還不夠清楚,所以我修改了它。請看看編輯。 – Tauren 2010-05-06 22:23:33

0

這是因爲privateVar不是該對象的私有成員,而是圖章封閉的一部分。您可以通過創建始終函數得到的效果:

Container = function(param) { 
    this.member = param; 
    var privateVar = param; 
    this.stamp = function(string) { 
     return privateVar + this.member + string; 
    } 
} 

的功能是建立時的privateVar的值設置,所以你必須在每次創建它。

編輯:修改不設置原型。

+0

@凱茜:謝謝,我同意封閉是要走的路。請參閱我編輯的問題,因爲我添加了更多關於我正在嘗試提問的詳細信息。 – Tauren 2010-05-06 22:21:26

+0

@Tauren變量只被設置爲第一個privateVar的原因是該函數只創建一次,所以閉包只創建一次。 – 2010-05-06 22:59:17

+0

感謝您的澄清,我明白現在發生了什麼。 – Tauren 2010-05-07 01:30:12

11

對不起,復活一個老問題,但我想補充一點,就是我最近發現其他地方在這裏SO (尋找鏈接,將編輯/添加它,一旦我找到它) found it

我個人喜歡下面的方法,因爲我可以直觀地將我的所有原型和'實例'定義與函數定義一起分組,同時避免多次評估它們。它還提供了一個機會來關閉你的原型方法,這對於創建由不同原型方法共享的「私有」變量非常有用。

var MyObject = (function() { 
    // Note that this variable can be closured with the 'instance' and prototype methods below 
    var outerScope = function(){}; 

    // This function will ultimately be the "constructor" for your object 
    function MyObject() { 
     var privateVariable = 1; // both of these private vars are really closures specific to each instance 
     var privateFunction = function(){}; 
     this.PublicProtectedFunction = function(){ }; 
    } 

    // "Static" like properties/functions, not specific to each instance but not a prototype either 
    MyObject.Count = 0; 

    // Prototype declarations 
    MyObject.prototype.someFunction = function() { }; 
    MyObject.prototype.someValue = 1; 

    return MyObject; 
})(); 

// note we do automatic evalution of this function, which means the 'instance' and prototype definitions 
// will only be evaluated/defined once. Now, everytime we do the following, we get a new instance 
// as defined by the 'function MyObject' definition inside 

var test = new MyObject(); 
+0

這是一個非常酷的想法,感謝分享它! – Tauren 2010-11-20 12:59:24

+0

這正是我需要的,非常感謝你! – seahorsepip 2016-01-13 15:55:05