2013-02-02 101 views
6

第一對夫婦段落描述了我試圖達到的目標,實際問題在最後。謝謝對象創建功能

以前,我只是使用new關鍵字來創建對象,以及原型來分配方法和處理繼承。然而,最近(部分由CoffeeScript中產生的JS啓發),我決定用一個創建對象的功能的發揮,將是這個樣子:

var Test = function(a) { 
    function Test(a) { 
     this.a = a; 
    } 
    var numCalls = 0; 
    Test.prototype.output = function() { 
     alert('I was initialized with ' + this.a); 
     numCalls++; 
    }; 
    Test.prototype.called = function() { 
     alert('You called the output ' + numCalls + ' times'); 
    }; 
    return new Test(a); 
}; 

然後,我會創建一個新的對象是這樣的:

var obj = Test('string'); 

與每個實例使用new的典型方法相比,此方法有兩個優點。首先,我不太可能忘記使用單詞new(我知道還有其他避免new的方法,但是從我所看到的他們有類似的問題,我將在下面描述),其次,我可以輕鬆地請參閱構造函數在現在屬於該類的任何函數中看到的「私有」變量。我在測試它時遇到了一個警告。 instanceof不再有效,因爲它看不到內部作用域對象Test。要解決這個問題,我試圖用constructor屬性來代替:

var a = Test('one'); 
var b = Test('two'); 
a.constructor == b.constructor    // false 
a.constructor.name == b.constructor.name // true 

而這正是讓我困惑。不使用對象創建函數創建相同的對象會使它們的構造函數相同,但在這種情況下它們是不同的。我的猜測是,發生的事情是每次函數運行時都會生成一個全新的對象類型(對象結構相同,但原型實例不同)。

如果我對問題的理解是正確的,那麼這是否也意味着代碼將爲每個對象實例分配額外的內存給我的JavaScript,以便通過欺騙它來爲實例共享以創建每個實例的相同對象原型的實例(擊敗使用原型的全部目的)?如果是這樣,是否有避免這種情況的好方法,同時仍然保持這種模式的優點(能夠在內部函數之間共享私有變量,而不必使用關鍵字new)?

如果我誤解了這個問題,有人能告訴我實際發生了什麼嗎?

+0

爲什麼你忘記關鍵字new?我的意思是,每次創建一個新對象時,都使用「新」,這非常簡單。這個解決方案在許多OO語言中存在了幾十年,這是一件好事,因爲每個人都可以閱讀和理解你的代碼。雖然沒有人理解var obj = Test('string'),但你的代碼可讀性較差。但我知道,這是一些JS程序員的最終目標。 ;) – Marcus

+0

@Marcus,如上所述,'new'關鍵字不是這種模式的唯一優勢。另外,我不是團隊中唯一的程序員。假設每個人都會記得使用'new'關鍵字,同時在Python中編寫後端(它不會爲類使用'new')是不現實的。在類聲明結束時發現丟失的'new'要比創建每個實例更容易。 –

+0

啊,我現在明白了:沒有編譯器告訴程序員他忘記了「新」,所以這就是爲什麼你不想使用它。 – Marcus

回答

3

如果是這樣,是否有避免這種情況,同時仍然保持這種模式的好處 一個好辦法...

嘗試使用模塊的辦法來代替:

var Test = (function TestModule() { 

    function Test(a) { 
    this.a = a; 
    } 

    Test.prototype = { 

    }; 

    return function(a) { 
    return new Test(a); 
    }; 

}()); 

var a = Test('foo'); 
var b = Test('baz'); 

a.constructor == b.constructor; // true 
a.constructor.name == b.constructor.name; // true 
+0

這是什麼首先'('在行'var的意思是=(函數TestModule(){'我真的不明白那種代碼... – Marcus

+0

@Marcus:這是一個IIFE。 ://benalman.com/news/2010/11/immediately-invoked-function-expression/ – elclanrs

0

現在我努力完成這項工作:完全封裝並且不需要實例化「新」的完美課程。搜索過了一會兒,我想出了這個:

function Test(x){ 

    var innerFunction = function(y){ 
     var variable = y; 

     this.getA = function(){ 
      return variable; 
     } 

     this.setA = function(x){ 
      variable = x; 
     } 
    } 
    return new innerFunction(x); 
} 

但測試結果證明是錯誤的:

var a = Test("foo"); 
var b = Test("baz"); 

alert(a.constructor == b.constructor);    //false, not good! 
alert(a.constructor.name == b.constructor.name); //true 

所以似乎有錯誤的範圍,所以我用一個公開內部函數

function Test(x){ 

    function innerFunction(y){ 
     var variable = y; 

     this.getA = function(){ 
      return variable; 
     } 

     this.setA = function(x){ 
      variable = x; 
     } 
    } 
    return new innerFunction(x); 
} 

並運行一些廣泛的測試,證明它是正確的:

var a = Test("foo"); 
var b = Test("baz"); 

alert(a.constructor == b.constructor);    //true, made it! 
alert(a.constructor.name == b.constructor.name); //true 

alert(a.getA());      //"foo" as expected 
alert(a.getA() == b.getA());   //false as expected 

a.variable = "whatever"; 
alert(a.getA());      //"foo" as expected 
alert(a.variable);      //"whatever", doesn't seem preventable 

a.setA("somewhere"); 
alert(a.getA());      //"somewhere", as expected 
alert(a.variable);      //"whatever", doesn't seem preventable 

但是,我們可以用這種方式使用幾個函數嗎?這是我的第一種方法:

function Test(x){ 

    function innerFunction(y){ 
     var variable = y; 

     this.getA = function(){ 
      return variable; 
     } 

     this.setA = function(x){ 
      variable = x; 
     } 
    } 
    return new innerFunction(x); 
} 

function TestToo(x){ 

    function innerFunction(y){ 
     var variable = y; 

     this.getA = function(){ 
      return variable; 
     } 

     this.setA = function(x){ 
      variable = x; 
     } 
    } 
    return new innerFunction(x); 
} 

var a = Test("foo"); 
var b = Test("baz"); 

var c = TestToo("foo"); 
var d = TestToo("baz"); 

alert(a.constructor == b.constructor);    //true, as expected 
alert(a.constructor.name == b.constructor.name); //true, as expected 

alert(c.constructor == d.constructor);    //true, as expected 
alert(c.constructor.name == d.constructor.name); //true, as expected 

alert(a.constructor == c.constructor);    //false, as expected 
alert(a.constructor.name == c.constructor.name); //true, as NOT expected 

這就是它嗎?我們是否真的總是需要知道使用字符串來比較a.constructor.name的內部類結構?真是沒有,因爲在Javascript中你可以從字面上做的一切(你只需要知道如何,不爲什麼),我發現這個最終的解決方案:

function Test(x){ 

    function Test(y){ 
     var variable = y; 

     this.getA = function(){ 
      return variable; 
     } 

     this.setA = function(x){ 
      variable = x; 
     } 
    } 
    return new Test(x); 
} 

function TestToo(x){ 

    function TestToo(y){ 
     var variable = y; 

     this.getA = function(){ 
      return variable; 
     } 

     this.setA = function(x){ 
      variable = x; 
     } 
    } 
    return new TestToo(x); 
} 

var a = Test("foo"); 
var b = Test("baz"); 

var c = TestToo("foo"); 
var d = TestToo("baz"); 

alert(a.constructor == b.constructor);    //true, as expected 
alert(a.constructor.name == b.constructor.name); //true, as expected 

alert(c.constructor == d.constructor);    //true, as expected 
alert(c.constructor.name == d.constructor.name); //true, as expected 

alert(a.constructor == c.constructor);    //false, as expected 
alert(a.constructor.name == c.constructor.name); //false, q.e.d.! 

我是認真的,我不知道爲什麼會這樣作品。但它肯定有效,100%對象封裝,與Java類1:1相等。 ;-)

+0

這就像使用「Goto」:轉到「Test」,然後創建一個名爲「Test」的類並實例化該類。證明Javascript函數本身沒有類,只有當你調用「new」時它纔會變成類! – Marcus

+0

問題:我在IE中沒有得到相同的結果!結果是:'a.constructor == b.constructor// false「和'a.constructor.name == b.constructor.name // true',但是函數本身仍然可以正常工作。%D – Marcus