2012-03-10 44 views
5

From Secrets of the Javascript Ninja(大演練BTW):維護構造函數丟失「新」是否是一個好習慣?

// We need to make sure that the new operator is always used 
function User(first, last){ 
    if (!(this instanceof User)) 
    return new User(first, last); 

    this.name = first + " " + last; 
} 

var name = "Resig"; 
var user = User("John", name); 

assert(user, "This was defined correctly, even if it was by mistake."); 
assert(name == "Resig", "The right name was maintained."); 

做任何真實世界的代碼風格指南使所有構造函數做這種維護(前兩行)呢?我相信棉絨短襪會接到像User("John", "Resig")這樣的電話,並警告有關失蹤的new,不是嗎?

+3

我認爲如果你正在開發一個公共圖書館,人們可能會錯誤地調用'User()'而不是'new User()''''''' – 2012-03-10 08:59:11

回答

7

我建議您在支持它的瀏覽器中使用嚴格模式並進行測試。例如,

function User(first, last) { 
    "use strict"; 
    this.name = first + " " + last; 
} 

如果您忽略了new,將會採用嚴格模式。例如,

User("a", "b") 

會在Firefox中拋出TypeError。這樣您就可以在無需支付instanceof支票的罰款的情況下發現錯誤。即使您將其稱爲嚴格模式以外,它也會引發類型錯誤。這是因爲直接調用嚴格模式函數this綁定到undefined而不是全局。在undefined上設置屬性會導致TypeError異常。

+0

似乎有點像黑客。我想知道這個代碼是多麼便攜? – 2012-03-10 10:15:10

+0

其實它是相反的。 chuckj提出的是「正確」的做法(ES5),而OP建議的方式就是「黑客」。看看這裏:https://developer.mozilla.org/en/JavaScript/Strict_mode – Saebekassebil 2012-03-10 10:24:17

+0

@Saebekassebil,chuckj的解決方案可能是正確的方法,但根據鏈接文章,嚴格模式在瀏覽器中不一致得到支持。他們說得很清楚 - 「瀏覽器還沒有可靠地實施嚴格模式,所以不要盲目依賴它。」; 「不支持嚴格模式的瀏覽器將運行嚴格的模式代碼,其瀏覽器的行爲不同,」等等。如果OP真的想要實現這個功能,我認爲他應該使用John Resig的解決方案,該解決方案應該適用於所有主要的JavaScript實現。 – 2012-03-11 04:48:32

2

雖然在JSHint.com檢查你的代碼,它並沒有告訴我,我錯過了new關鍵字:

function User(first, last){ 
    this.name = first + " " + last; 
} 

var name = "Resig"; 
var user = User("John", name); 

結果:

Line 2: this.name = first + " " + last; 
Missing "use strict" statement. 

Line 5: var name = "Resig"; 
Redefinition of 'name'. 

但棉短絨ISN」這裏重點。

至於是不是良好的做法,是的,這是一個很好的做法,因爲人類不是短絨,他們可以忘記把new關鍵字。即使一條短信提醒,我仍然會繼續保護我的代碼。

把簡單放在一起沒有錯;你可以說額外條件,可以幫助你避免這個問題。

有個約定,第一個字母的一個構造函數應該是大寫的;這是出於同樣的原因,所以JS開發人員從不會忘記關鍵字new,他說一個函數首字母大寫,但並不是所有的開發人員都可能知道它(直到最近我纔開始認真研究JS),如果你沒有通過外部世界指定,那麼你可以通過自己的代碼來保護你的代碼。

+0

當代碼沒有被作爲構造函數調用時,代碼確實發出對'new'的調用,所以如果你執行'new User(x,y)'或'User(x,y) '。 – Lucero 2012-03-10 09:06:32

+0

我很確定我看到某處確實考慮過首字母大寫規則來推斷構造函數的用法,並檢查是否缺少'new'。也許我在幻覺。 – ripper234 2012-03-10 09:10:26

+1

@ ripper234:如果你總是使用linter,或者你總是使用它,並且你期望其他人使用你的代碼也有linters,那麼根本沒有問題,一切都會好的:) – Sarfraz 2012-03-10 09:11:59

1

該代碼不會警告,如果首先不使用它,它會調用new,因此致電User(x, y)最終會在內部執行new User(x, y)

這就是說,我不喜歡這種透明修復。防禦性編碼是可以的(例如拋出一個錯誤),但是我不喜歡代碼在做我想做的事情的假設(或者我可能做過的事情,比如當我想調用一個被調用的函數時錯誤地使用大寫字母user)。


我們(和我們公司一樣)雖然在內部使用組件和模板系統,但仍使用JavaScript的相同「特性」。

我們的「組件」(具有附加到特定DOM對象的行爲的JS對象)以這種樣式創建:new MyComponent(domObject) - 創建組件並將其附加到DOM元素(它可以偵聽事件等)。

我們還使用了一個模板化引擎來編譯JS函數,以便您可以像這樣調用模板:myTemplate(someData) - 將給定的模板呈現爲HTML字符串。這些模板也可以調用partials,這顯然只是返回HTML字符串的正常函數。

爲了直接生成組件的HTML,組件構造函數將使用「檢測」,無論它是通過new調用還是不作爲構造函數或模板。當它作爲模板工作時,它會爲其根元素添加一個特殊屬性(在我們的例子中爲data-component),以便稍後可以完成修復,其中生成的HTML轉換爲DOM樹,然後所有具有此屬性的DOM元素都會自動獲取爲他們生成的組件(這次使用new)。

這只是您如何利用此類語言結構的一個示例。

1

只是爲了好玩:這也可能是,以防止忘記new關鍵字的方式:

function User(first,last,id){ 
    User.Instance = User.Instance|| function(firstname,lastname,id){ 
     this.firstname = firstname || 'nofirstname'; 
     this.lastname = lastname || 'nolastname'; 
     this.id = id || 0; 
     if (!User.Instance.prototype.nameUpper){ 
     User.Instance.prototype.nameUpper = function(){ 
        return this.name.toUpperCase(); 
     }; 
     User.Instance.prototype.resetId = function(){ 
        this.id = 0; return this; 
     }; 
     User.Instance.prototype.toString = function(){ 
      return [this.id,': ', 
        this.firstname[0].toUpperCase(), 
        this.firstname.slice(1), 
        ' ', 
        this.lastname[0].toUpperCase(), 
        this.lastname.slice(1)].join(''); 
     }; 
     } 
    } 
    return new User.Instance(first,last,id); 
} 
//usage 
var pete = User('pete','johanssen',1), 
    jean = User('jean','harlowe',2), 
    mary = new User('mary','wsnovsky',3); 
console.log(pete); //=> 1: Pete Johanssen' 
console.log(mary); //=> 3: Mary Wsnovsky' 

關於你的問題:我會考慮「良好實踐」相當規範/評判。每天吃奶牛眼是不錯的做法嗎?如果我要回答這個問題,我會說不,但我肯定不是所有人都會同意的。使用您演示的模式來規避被遺忘的new關鍵字本身並不好或不好。這取決於您設定的標準:您是否想強制執行某種嚴格規定,或者您是否想要防範自己或同事的sl?行爲?該模式的性能會降低嗎?請問chukj的答案在所有情況下都適用,如果不是,那對你來說足夠了嗎?

我喜歡的東西之一討厭javascript是它的靈活性。 JSLint的,據我從道格拉斯克羅克福德的講座瞭解,旨在迫使你一個統一的編程風格 - 用他自己的話說:使用JSLint的

將傷害你

但另一方面,jslint提供了很多可能性來使用諸如'容忍錯誤定義','容忍非大寫構造函數'等選項來減輕這種力量。換句話說,即使使用jslint,'好習慣'也不是明確界定。

有時JavaScript社區的行爲就像'javascript教派的教會'。其中我們發現非常嚴格的信徒,對他們來說法律是法律,對它的每一個失常都是一個基本罪行(對他們來說可能是「使用嚴格的」是專門發明的)。其他人則遵循更加務實的道路。有經常上教堂的人和從不出現的人。分裂現象出現。有些人計劃離開這個教堂,並找到一個新的教堂,例如其中classes被允許並被認爲是好的和神聖的。

現在可以清楚了:我認爲沒有'好習慣'這樣的東西。或者用更僞的宗教術語來表述它:好的做法在旁觀者眼中是

+0

雖然不是一個真正的答案......但是,我不會暈倒。 – ripper234 2012-03-10 11:45:11

+0

就像我說的,只是爲了好玩。但我編輯了我的答案,也回答你的問題。 – KooiInc 2012-03-10 13:01:38

相關問題