2014-12-30 31 views
3

我正在研究一些JavaScript代碼,這些代碼將用於爲我們的企業級軟件構建「類」(特別是「查看模型」)的構造函數。我正在做的一件事是建立一個代碼強制設計模式,在這個模式中,實現者將明確定義他們希望由類的實例公開哪些函數,代碼將它們添加到構造函數的prototype中,如反對該類的每個實例的屬性。這當然有一個好處,那就是每個類型只有一個這樣的函數的實例,而不是每個實例的實例。使用綁定來強制原型函數的上下文

Here's a CodePen of most of my examples.

的問題是,在一定條件下,我處理binding loss問題。例如,對於此構造:

function Foo(myName) { 
    this.firstName = myName; 
    } 

    Foo.prototype.greet = function(yourName) { 
    alert("Hello, " + yourName + ". I am " + this.firstName + "."); 
    } 

...這將工作:

var sam = new Foo("Sam"); 
// Alerts "Hello, Denny. I am Sam." 
sam.greet("Denny"); 

...但這不會:

var sad = new Foo("Sad"); 
// This changes the context of greet. :(
var sadGreet = sad.greet; 
// Alerts "Hello, Denny. I am undefined." 
sadGreet("Denny"); 

這是因爲當我們做var sadGreet = sad.greet我們正在將greet函數的上下文更改爲window,因此在調用sadGreet("Denny")this.firstName不存在。

所以,我想出了一個解決方案是覆蓋原型與調用原型的屬性,在Function.prototype.bind()包裹它:

function Bar(myName) { 
    this.firstName = myName; 
    // Here's where the magic happens. 
    this.greet = Bar.prototype.greet.bind(this); 
} 

Bar.prototype.greet = function(yourName) { 
    alert("Hello, " + yourName + ". I am " + this.firstName + "."); 
} 

var happy = new Bar("Happy"); 
// Since each instance of Bar overwrites the context for greet, this will work. :) 
var happyGreet = happy.greet; 
// Alerts "Hello, Denny. I am Happy." 
happyGreet("Denny"); 

我的問題是這樣的:我認爲Bar是效率不如Foo,但是這樣做是否會將greet作爲prototype方法聲明爲完全無效?在封面之下,它只是複製greet作爲一個屬性,無論如何,或者是我的電話bind只需添加一個「包裝」爲Bar.prototype.greet?換句話說,這是否與Bar的上述定義完全相同?

function Bar(myName) { 
    this.firstName = myName; 
    // Here's where the magic happens. 
    this.greet = function(yourName) { 
    alert("Hello, " + yourName + ". I am " + this.firstName + "."); 
    } 
} 

如果您不僅可以回答問題,而且可以告訴我如何測試,那麼可以獲得積分獎勵!

+0

關於效率問題通常是艱難對於SO而言,因爲它很難得到確鑿的回答。也就是說,我的主要反應是,我認爲'happyGreet(...)'通常是一個錯誤。如果一個函數在一個實例上,你可能不應該把它分配給一個變量,如果你這樣做,你應該''。bind'它在那裏的分配位置,或做'happyGreet.call(快樂,「丹尼」)' – loganfsmyth

+0

@loganfsmyth感謝您的答覆,我同意。不幸的是,這只是「綁定損失」可能發生的幾個可能的例子之一 - 而且我不一定要控制這些構造函數的每個實現。 – Grinn

+0

函數的執行上下文包含一個* this *參數,該參數通過調用函數**或使用* bind *來設置**。如果你改變你如何調用一個函數,他們可能會改變它* this *。或不。 – RobG

回答

1

Bar的效率肯定比Foo低,但不是時間複雜度。相反,它在內存消耗方面效率較低,因爲Bar的每個實例現在都有一個UNIQUE和NEW'greet'函數對象('bind'函數創建一個新的函數對象)。

看着你的代碼,它實際上變得稍微複雜一些,即使在Bar.prototype上有'迎接'功能。取而代之的是,這個工程還有:

function Bar(myName) { 
    this.firstName = myName; 
    this.greet = greet.bind(this); 
} 

var greet = function(yourName) { 
    alert("Hello, " + yourName + ". I am " + this.firstName + "."); 
}; 

var happy = new Bar("Happy"); 
var happyGreet = happy.greet; 
happyGreet("Denny"); 

可以測試了「迎接」功能,通過簡單的嘗試,這是不相同的每個實例的「打招呼」功能:

console.log(greet === happyGreet); 
+0

在你的例子中,不會'happy.greet'也是一個UNIQUE和NEW'greet'函數嗎? – Grinn

+0

不,happy.greet只是對屬於Bar的每個實例的「問候」函數對象的引用。基本上'var greet'函數是一個函數,然後Bar的每個實例基於'var greet'函數創建一個新的'greet'函數。 – wmock

+0

編輯我的迴應顯示,問候函數與happyGreet函數不同。這在你以前的例子中也是一樣的:happyGreet!== Bar.prototype.greet – wmock

相關問題