2009-11-27 40 views
42

我看了a talk by Douglas Crockford on the good parts in Javascript,我的眼睛 被打開。他說,有一點像「Javascript是唯一的語言,好的程序員相信他們可以有效地使用它,而不需要學習它。」然後我意識到,我是那個人。OO Javascript的構造模式:新古典vs原型

在那次演講中,他發表了一些對我來說非常驚人和有見地的發言。例如,JavaScript是這個星球上最重要的編程語言。或者它是這個星球上最流行的語言。而且,它以許多嚴肅的方式被打破。

他對我所做的最令人驚訝的表述是「新的危險」。他不再使用它了。他也沒有使用this

他爲Javascript中的構造函數提出了一個有趣的模式,它允許私有和公共成員變量,並且既不依賴於new也不依賴於this。它看起來像這樣:

// neo-classical constructor 
var container = function(initialParam) { 
    var instance = {}; // empty object 

    // private members 
    var privateField_Value = 0; 
    var privateField_Name = "default"; 

    var privateMethod_M1 = function (a,b,c) { 
     // arbitrary 
    }; 

    // initialParam is optional 
    if (typeof initialParam !== "undefined") { 
     privateField_Name= initialParam; 
    } 

    // public members 
    instance.publicMethod = function(a, b, c) { 
     // because of closures, 
     // can call private methods or 
     // access private fields here. 
    }; 

    instance.setValue = function(v) { 
     privateField_Value = v; 
    }; 

    instance.toString = function(){ 
     return "container(v='" + privateField_Value + "', n='" + privateField_Name + "')"; 
    }; 

    return instance; 
} 


// usage 
var a = container("Wallaby"); 
WScript.echo(a.toString()); 
a.setValue(42); 
WScript.echo(a.toString()); 

var b = container(); 
WScript.echo(b.toString()); 

編輯:代碼更新,切換到小寫類名。

此模式已從Crockford's earlier usage models發展而來。

問題:你使用這種類型的構造函數嗎?你覺得這可以理解嗎?你有更好的嗎?

+2

天才......不像構造函數,更像工廠! – Zoidberg 2009-11-27 17:42:20

+2

確保你防止使用像var c = new Container();這可能涉及檢查這個值。 – 2009-11-27 18:13:14

+0

我同意@Kevin Hakanson,道格拉斯克羅克福德實際上建議在這種方法中使用小寫字母的初始值,以及大寫字母的初始值用於與新的方法一起使用的構造函數方法。 – 2009-11-27 18:21:35

回答

16

這看起來像module pattern的非單例版本,通過利用JavaScript的「閉包」可以模擬私有變量。

我喜歡它(有點...)。但是我並沒有真正看到以這種方式完成的私有變量的優點,特別是當它意味着添加(初始化後)任何新方法都無法訪問私有變量時。

另外,它沒有利用JavaScript的原型模型。所有的方法和屬性都必須在構造函數被調用時進行初始化 - 如果存儲在構造函數原型中的方法不會發生。事實是,使用傳統的構造函數/原型模式要快得多!你真的認爲私有變量使性能命中值得嗎?

這種模型對模塊模式有意義,因爲它只被初始化一次(創建一個僞單身),但我不太確定它在這裏是否有意義。

你使用這種類型的構造函數嗎?

沒有,雖然我不使用它的單變型,模塊模式...

你覺得它理解?

是的,它的可讀性和非常清晰,但我不喜歡這樣的構造函數中的所有東西的想法。

你有更好的嗎?

如果你真的需要私有變量,那麼堅持下去吧。否則,僅僅使用傳統的構造函數/原型模式(除非你分享Crockford的恐懼new/this組合的):

+0

什麼是傳統的構造函數模式?就像我說的,*我是那個在學習之前使用lamguage的人。另外,什麼是perf命中?每次調用ctor/factory都不要初始化函數......我認爲完全消除了私有變量。這就是你所說的「這是否值得」? 在我看來,Crockford的一個主要關注點是缺乏模塊化,以及不同的js庫可能出乎意料地踐踏其他變量。他的模塊模式,他拒絕使用新的或這個 - 他們都打算直接解決這個問題。 – Cheeso 2009-11-27 18:32:36

+0

「傳統的構造函數模式」,我的意思是有一個構造函數,它使用隱式創建的自身實例(通過'new'創建),它可以稱爲'this'。然後,可以將任何方法添加到構造函數的原型中 - 添加到此原型的任何方法都將立即可用於「類」的所有當前和未來實例。看看我添加到我的答案的代碼示例。 perf的擊中比你想象的要多。在這裏比較它們:http://gist.github.com/244166 – James 2009-11-27 18:42:37

+0

我比傳統與克羅克福德的建議相比,我的速度有了10倍的差異。 – Cheeso 2009-11-27 18:56:15

6

在他的書中,它被稱爲功能繼承(第52頁)。我還沒有使用它。如果你學習道格拉斯的javascript,這是可以理解的。我不認爲有更好的方法。這一個是好的,因爲它:

  • 使程序員能夠創建私有成員

  • 確保私有成員和消除不是假裝隱私(私有成員開始_)

  • 使繼承順利,沒有不必要的代碼

Howeve r,它有一些缺點。您可以在這裏閱讀更多關於它的信息:http://www.bolinfest.com/javascript/inheritance.php

9

我避免這種模式,因爲大多數人發現閱讀起來比較困難。我一般遵循兩個途徑:

  1. 如果我只對某件事有一個,然後我用匿名對象:

    var MyObject = { 
        myMethod: function() { 
         // do something 
        } 
    }; 
    
  2. 對於更多的東西比一個我用的標準JavaScript原型繼承

    var MyClass = function() { 
    }; 
    MyClass.prototype.myMethod = function() { 
        // do something 
    }; 
    
    var myObject = new MyClass(); 
    

(1)更容易閱讀,理解和書寫。 (2)在有多個對象時更有效。 Crockford的代碼每次都會在構造函數中創建一個新的函數副本。封閉也有更難調試的缺點。

儘管您失去了真正的私人變量,但您將_作爲慣例添加了前綴應爲私人成員。

this在javascript中是一個公認的難題,但可以通過使用.call.apply來正確設置它。我還經常使用var self = this;創建一個閉包變量,用作在成員函數中定義的函數內的this

MyClass.prototype.myMethod = function() { 
    var self = this; 

    // Either 
    function inner1() { 
     this.member(); 
    } 
    inner1.call(this); 

    // Or 
    function inner2() { 
     self.member(); 
    } 
    inner2(); 
}; 
+4

匿名對象?你的意思是對象文字嗎? – 2009-11-27 23:11:10

1

你使用這種構造格局?

當我第一次學習JavaScript並偶然發現Douglas Crockford的文獻時,我已經使用過這個模式。

你覺得它可以理解嗎?

只要你理解閉包,這個方法很清楚。

你有更好的嗎?

這取決於你想要完成什麼。如果你試圖編寫一個儘可能白癡的庫,那麼我可以完全理解私有變量的使用。這可能有助於防止用戶無意中干擾對象的完整性。是的,時間成本可能會稍微大一點(大約3倍),但時間成本是一個有爭議的爭論,直到它實際上影響您的應用程序。我注意到前一篇文章中提到的測試(test)沒有考慮訪問對象函數需要多長時間。

我發現原型/構造函數方法通常會導致更快的構建時間,是的,但它並不一定節省檢索時間。使用原型鏈來查找函數與直接附加到您正在使用的對象的函數相比,還有額外的成本。所以如果你調用了很多原型函數而不是構建很多對象,那麼使用Crockford的模式可能更有意義。

我傾向於不喜歡使用私有變量,但是,如果我沒有爲大量讀者編寫我的代碼。如果我與一羣人都是有能力的編碼人員在一起,我通常可以相信他們會知道如何使用我的代碼,如果我自己編寫和評論。然後我使用類似Crockford的模式sans私人成員/方法的東西。

7

你使用這種構造函數模式嗎?

都能跟得上

你覺得它理解?

是的,它非常直接。

你有更好的嗎?

我還沒有看過談話,但我很快就會談到。 在那之前,我沒有看到使用newthis的危險,和這裏的原因:

沒有聽過他的觀點,我只能假設,他建議從因的this性質這樣的事情避而遠之,以及它如何根據執行特定方法的上下文(直接在原始對象上或作爲回調等)而發生變化。作爲一名教育工作者,他可能會教會迴避這些關鍵詞,因爲大部分人並沒有意識到和缺乏經驗的開發人員在不考慮該語言的本質的情況下擅長JavaScript。對於熟悉該語言的經驗豐富的開發人員,我不相信有必要避免使用該語言的這種特性,從而賦予其極大的靈活性(與避免諸如with之類的事情完全不同)。所有這一切說,我現在會看。無論如何,當不使用某種支持自動繼承的框架(如dojo.declare),或者在編寫獨立於框架的對象時,我現在採取以下方法。

定義:

var SomeObject = function() { 
    /* Private access */ 
    var privateMember = "I am a private member"; 
    var privateMethod = bindScope(this, function() { 
     console.log(privateMember, this.publicMember); 
    }); 

    /* Public access */ 
    this.publicMember = "I am a public member"; 

    this.publicMethod = function() { 
     console.log(privateMember, this.publicMember); 
    }; 
    this.privateMethodWrapper = function() { 
     privateMethod(); 
    } 
}; 

使用

var o = new SomeObject(); 
o.privateMethodWrapper(); 

哪裏bindScope類似於Dojo的dojo.hitch或原型的Function.prototype.bind一個效用函數。

0

認爲可以幫助討論在這裏報告道格拉斯克羅克福德在他的「高級JavaScript」演示視頻中所解釋的這種模式的主要觀點。

重點是避免使用新的操作符。 因爲當某個人在調用對象構造函數時忘記使用new運算符,則在構造函數中添加的對象成員都將最終進入全局名稱空間。 請注意,在這種情況下,沒有運行時警告或錯誤通知錯誤,全局名稱空間只會被污染。 這已被證明具有嚴重的安全隱患,併成爲許多診斷錯誤的難題。

這就是Powerconstructor模式的主要觀點。

私有/特權部分是次要的。以不同的方式做到這一點是可以的。 所以是原型成員不使用。

HTH盧卡

+1

我第一次提出這個問題已經有一段時間了,隨着時間的推移,我有了一些觀點。在我看來,克羅克福德主張大量工作來保護一小部分錯誤 - 省略「新」操作符。爲什麼要去所有的麻煩?我們對其他常見的錯誤情況沒有類似的保護:例如,省略'if'語句或'return'語句。你說這是難以診斷的錯誤的來源。真? – Cheeso 2011-10-08 12:56:47

+1

我還沒有看到這方面的證據,我懷疑「省略新的」會導致Javascript開發中很大一部分的痛苦。當然最初的單元測試會很快顯示開發者,*嘿!你忘了在這裏叫新!*然後問題就解決了。靜態分析(JSLINT)也可以用於輔助。爲了解決這個問題,到處實施克羅克福德會議對我來說似乎是一種過度反應。 – Cheeso 2011-10-08 12:56:59

+0

爲了避免遺忘新事物,只需用'if(!(this instanceof ConstructorName)){throw new Error(「構造函數的無效調用」)開始構造函數。 }' – Kulvar 2017-08-16 20:59:10

1

如果我們需要始終創建同一類型的對象,我寧願用原型構造模式,因爲它允許共享的方法和屬性通過原型鏈通過自動代表團

另外我們可以保留私人對象;看看這個方法:

var ConstructorName = (function() { //IIFE 
    'use strict'; 

    function privateMethod (args) {} 

    function ConstructorName(args) { 
     // enforces new (prevent 'this' be the global scope) 
     if (!(this instanceof ConstructorName)) { 
      return new ConstructorName(args); 
     } 
     // constructor body 
    } 

    // shared members (automatic delegation) 
    ConstructorName.prototype.methodName = function(args) { 
     // use private objects 
    }; 

    return ConstructorName; 
}()); 

我建議您查看這個答案,我們可以發現一個有趣的方法來創建一個構造函數:Different way of creating a Javascript Object

這是一個例子,你可以使用原型的方法構造函數:How do I create a custom Error in JavaScript?