2012-04-26 95 views
10

我知道這不是推薦的方式,但是如果我聲明以下函數,然後將它們作爲構造函數調用,那麼結果對象之間會有什麼不同(如果有的話)?在JavaScript中,構造函數和函數返回的對象之間有什麼不同?

function Something() { 
    this.foo = "bar"; 
} 

function something2() { 
    var that = {}; 
    that.foo = "bar"; 
    return that; 
} 

var x = new Something(); 
var y = new something2(); 
var z = something2(); 

I.e. x,yz這裏有什麼不同?

不會something2是一個更好的寫構造函數的方法,因爲不管你使用new還是不會影響函數的結果?

順便說一句應該something2在這裏大寫? (我認爲不是因爲Crockford在大寫字母上如此堅決,因爲函數會打破全局名字空間......)

+2

有點警惕先生克羅克福德。雖然他有很多好話要說,但他確實有不同意見。 – staticsan 2012-04-26 06:50:37

回答

14

簡而言之:

new something2() instanceof something2 === false 

與此相關的,如果你擴展你的榜樣使用原型屬性

Something.prototype.method = function() { }; 
something2.prototype.method = function() { }; 

你會發現,原型是不是在後一種情況下繼承:

typeof (new Something()).method === "function" 
type (new something2()).method === "undefined" 

該rea l答案是你正在攻擊完全不同的基礎機器。調用new調用[[Construct]]機制,該機制涉及根據構造函數的.prototype屬性設置[[Prototype]]屬性。

但是一個有趣的事情發生在[[Construct]]算法的步驟8--10:設置一個新的空對象,然後附加其[[Prototype]]後,它會執行[[Call] ]到實際的構造函數,使用這個新的空加原型對象作爲this。然後,在第9步中,如果事實證明該構造函數返回了一些東西 - 它拋棄了原型綁定,傳遞爲this的對象,它一直花費在設置上!

注意:您可以訪問對象的[[原型](這是由不同構造的.prototype)與Object.getPrototypeOf

Object.getPrototypeOf(new Something()) === Something.prototype // steps 5 & 6 
Object.getPrototypeOf(new something2()) === Object.prototype // default 

回答一些元問題:

  • 不,請不要大寫something2,因爲它是工廠函數而不是構造函數。如果某些東西是大寫的,則預計會有構造函數語義,例如new A() instanceof A
  • 如果您擔心全局命名空間遭到破壞,您應該開始使用strict mode,將"use strict";放在文件的頂部。嚴格模式的很多很好的清理之一是this默認爲undefined,而不是全局對象,例如,在構造函數嘗試將屬性附加到undefined時調用構造函數而不使用new將導致錯誤。
  • 工廠函數(又稱「閉包模式」)通常是構造函數和類的合理替代,只要您(a)不使用繼承; (b)不構造太多的對象實例。後者是因爲在閉包模式中,每個方法的新實例附加到每個新創建的對象,這對內存使用來說並不好。國際海事組織(IMO)最大的回報就是使用"private" variables(這是一個good thing,並且別讓別人告訴你:P)。
+0

第二種方法中的'new'也是可選的(或者確實不需要),但是在第一種情況下忽略掉這些東西(這個===窗口) – Matt 2012-04-26 06:52:38

+0

@ Matt-你可以從內部防禦一點點通過看看這個'this'是否是全局對象並相應地開始構造函數。 – RobG 2012-04-26 06:58:12

+1

@Matt,@RobG ---或者,只需在文件頂部加上'「use strict」;'!那麼'this'將會是'undefined'。 – Domenic 2012-04-26 07:01:31

2

在第二種情況下,返回的對象不會從構造函數繼承任何東西,所以沒有什麼意義像這樣使用它。

> var x = new Something(); 
> var y = new something2(); 
> var z = something2(); 

即這裏的x,y和z會有什麼不同?從Something,何在既不y或從something2z繼承

x繼承。

豈不something2是編寫構造, 因爲你是否使用新的更好的方式還是不不會影響 函數的結果?

有呼籲something2的構造,因爲它返回的對象不是分配給其this新構造的對象,從something2.prototype繼承,這是另一些可能是調用new something2()時候能得到沒有意義的。

順便說一句,這裏應該用大寫嗎? (我認爲不是,因爲 克羅克福德是在資本如此堅決,對於功能將 痛毆全局命名空間......)

沒有,因爲調用它的構造是有點意義的,所以表徵它作爲一個會誤導人。

1

調用一個函數作爲構造(即與新的keyword)運行以下步驟:

  1. prototype屬性創建新對象
  2. 該對象的原型設置爲對象的功能
  3. 在該對象的上下文中執行構造函數(即this是新對象)
  4. 返回該對象(如果構造函數或沒有return聲明)

因此,您的第二個解決方案將返回一個普通的對象與屬性「foo」。但是yz都不是instanceof Something2,並且不從該原型繼承。有這樣的功能,是的,但他們不應該被稱爲構造函數(沒有大寫的命名,沒有調用new)。它們屬於工廠模式。

如果你想可以在不新執行的構造,使用該代碼:

function Something(params) { 
    if (! this instanceof Something) 
     return new Something(params); 
    // else use "this" as usual 
    this.foo = "bar"; 
    ... 
} 
1

我想說的最重要的事情是返回的對象的原型。

function Something() { 
     this.foo = "bar"; 
    } 

    Something.prototype = { 
    // Something prototype code 
    hello: function(){ 
    //... 
    } 
    } 

    function something2() { 
    var that = {}; 
    that.foo = "bar"; 
    return that; 
    } 

    something2.prototype = { 
     // something2 prototype code 
     greetings : function() { 
     //... 
     } 
    } 

    var x = new Something(); 
    var y = new something2(); 
    var z = something2(); 

    typeof x.hello === function // should be true 
    typeof y.greetings === undefined // should be true 
    typeof z.greetings === undefined // should be true 

換句話說,我會說你沒有用something2實例化對象,你正在創建純粹的從Object繼承的新對象。當您使用關鍵字

  1. 東西()會給你「東西」類型的新對象。
  2. something2()會給你「對象」類型的新對象,它將立即返回一個新的空對象。
  3. 新something2是低效的,因爲你正在創建一個空白的範圍,從中你創建一個新的對象

    var that = {}; 
    

    這相當於

    var that = new Object(); 
    
相關問題