2013-04-26 51 views
9

下面是一個簡單的Javascript類與公共和私人方法(小提琴:小提琴:http://jsfiddle.net/gY4mh/)的示例。在Javascript中確保「this」上下文的最佳做法是什麼?

從公共調用私有函數導致「this」是窗口對象。我應該如何確保我的私有方法是用類上下文而不是窗口來調用的?這會不受歡迎嗎?

+2

管理'this'範圍一個小技巧可以在這裏找到:http://stackoverflow.com/q/4886632 – 2013-04-26 00:14:29

+1

另一種解決方案:['Function.bind '](https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Function/bind) – 2013-04-26 00:16:29

+0

與'this'問題無關,你不能刪除立即調用的匿名函數包裝器來自內部'函數的例子()'?在我看來,如果你直接聲明'function Example()',你會得到相同的結果。 http://jsfiddle.net/gY4mh/1/ – nnnnnn 2013-04-26 00:44:17

回答

8

使用閉包。基本上在函數中聲明的任何變量,仍適用於該函數內部功能:

var Example = (function() { 
    function Example() { 
     var self = this; // variable in function Example 
     function privateFunction() {     
      // The variable self is available to this function even after Example returns. 
      console.log(self); 
     } 

     self.publicFunction = function() { 
      privateFunction(); 
     } 
    } 

    return Example; 
})(); 

ex = new Example; 
ex.publicFunction(); 
2

我想最常用的方式來完成這件事是通過簡單地緩存(存儲)的this在本地環境變量的值

function Example() { 
    var that = this; 
    // ... 
    function privateFunction() { 
     console.log(that); 
    } 

    this.publicFunction = function() { 
     privateFunction(); 
    } 
} 

更方便的方法是調用Function.prototype.bind結合上下文到一個函數(永遠)。但是,這裏唯一的限制就是這需要一個支持ES5的瀏覽器,並且綁定的功能稍微慢一些。

var privateFunction = function() { 
    console.log(this); 
}.bind(this); 
+1

最後一個不會運行。 – 0x499602D2 2013-04-26 00:19:17

+0

@ 0x499602D2您是否在ES5-ready瀏覽器中運行它? – ErikE 2013-04-26 00:21:56

+0

@ErikE問題是對'函數聲明'的調用,這是有問題的。我將代碼修改爲一個函數表達式的函數表達式 – jAndy 2013-04-26 00:22:40

0

如果你不使用EcmaScript5,我推薦使用下劃線(或LoDash的)bind功能。

0

除了這裏給出的,如果你沒有一個ES5就緒瀏覽器,你可以創建自己的「永久綁定功能」很簡單的代碼,像這樣其他的答案:

function boundFn(thisobj, fn) { 
    return function() { 
     fn.apply(thisobj, arguments); 
    }; 
} 

然後使用它是這樣的:

var Example = (function() { 
    function Example() { 
     var privateFunction = boundFn(this, function() { 
      // "this" inside here is the same "this" that was passed to boundFn. 
      console.log(this); 
     }); 

     this.publicFunction = function() { 
      privateFunction(); 
     } 
    } 

    return Example; 
}()); // I prefer this order of parentheses 

瞧 - this是神奇外上下文的的this而不是內部之一!

你甚至可以得到ES5一樣的功能,如果它缺少在瀏覽器中像這樣(這樣做什麼,如果你已經擁有它):

if (!Function.prototype.bind) { 
    Function.prototype.bind = function (thisobj) { 
     var that = this; 
     return function() { 
     that.apply(thisobj, arguments); 
     }; 
    }: 
} 

然後使用var yourFunction = function() {}.bind(thisobj);完全相同的方式。

完全符合(儘可能)完全符合ES5的代碼,檢查參數類型等可在mozilla Function.prototype.bind找到。如果你正在做一些與函數不同的高級事物,有些差異可能會讓你感到沮喪,所以如果你想要走這條路線,請在鏈接上閱讀它。

3

另一種方法是使用「apply」來明確設置「this」應綁定的方法。

function Test() { 
    this.name = 'test'; 
    this.logName = function() { 
     console.log(this.name); 
    } 
} 

var foo = {name: 'foo'}; 

var test = new Test(); 
test.logName() 
// => test 
test.logName.apply(foo, null); 
// => foo 

另一種方法是使用「呼」:

function Test() { 
    this.name = 'test'; 
    this.logName = function() { 
     console.log(this.name); 
    } 
} 

var foo = {name: 'foo'}; 

var test = new Test(); 
test.logName() 
// => test 
test.logName.call(foo, null); 
// => foo 

兩個「應用」和「叫」走,你要綁定「本」,以作爲第一個參數的對象和作爲第二個參數傳入您要調用的方法的參數數組。

3

值得理解的是,除了有人告訴你一個代碼修復之外,如何確定javascript中this的值。在JavaScript中,this確定以下方式:

  1. 如果你通過一個對象的屬性調用一個函數在object.method(),然後this將被設置爲在方法內部的對象。

  2. 如果調用直接函數而沒有任何對象的引用,例如function(),然後this將(在瀏覽器中window)或在嚴格模式設置爲全局對象,它會被設置爲undefined

  3. 如果使用new運算符創建一個新對象,則該對象的構造函數將被調用,並將值this設置爲新創建的對象實例。你可以認爲這與上面的第1項相同,該對象被創建,然後調用它的構造方法。

  4. 如果調用.call().apply()的功能,如function.call(xxx),那麼你就可以決定你傳遞給.call().apply()什麼論據正是this設置爲通過。您可以在MDN上閱讀關於.call() here.apply() here的更多信息。

  5. 如果您使用function.bind(xxx),則會創建一個小的存根函數,以確保您的函數被調用的期望值爲this。在內部,這可能只是使用.apply(),但它是您希望單個回調函數在被調用時(當您不是該函數的直接調用方時)具有正確值this的快捷方式。

  6. 在回調函數中,回調函數的調用者負責確定期望值this。例如,在事件處理函數回調函數中,瀏覽器通常將this設置爲處理事件的DOM對象。

對這些不同的方法here on MDN有一個很好的總結。

因此,在您的情況下,當您致電privateFunction()時,您正在進行正常的函數調用。所以,如預期的那樣,this的值如上面的選項2中設置。

如果你想顯式地將它設置爲this在方法的當前值,那麼你可以這樣做是這樣的:

var Example = (function() { 
    function Example() { 
     function privateFunction() { 
      // "this" is window when called. 
      console.log(this); 
     } 

     this.publicFunction = function() { 
      privateFunction.call(this); 
     } 
    } 

    return Example; 
})(); 

ex = new Example; 
ex.publicFunction(); 

其他方法,如使用封閉和定義var that = this最好用於在不是函數調用者的情況下回調函數的情況下,因此不能使用1-4。沒有理由在你的特定情況下這樣做。我會說使用.call()是一個更好的做法。然後,你的函數實際上可以使用this,並且可以表現得像一個私有方法,這似乎是你所尋求的行爲。

+0

+1。 – jthomas 2013-04-26 02:00:56

+0

我會爭辯說,在OP的情況下,使用一個包含別名的閉包(根據公認的答案)是_better_,因爲內部就是'.bind'所做的,它避免了必須記住使用'.call '語法而不是普通的函數調用語法。 – Alnitak 2015-07-01 07:09:04

0

我想說分配selfthis是一種常見的技術:

function Example() { 
    var self = this; 

    function privateFunction() { 
     console.log(self); 
    } 

    self.publicFunction = function() { 
     privateFunction(); 
    }; 
} 

使用apply(如其他人所說)也適用,但它在我看來更復雜一點。

這可能超出了這個問題的範圍,但我也建議考慮一種不同的JavaScript方法,你根本不使用關鍵字this。我在ThoughtWorks的一位前同事Pete Hodgson寫了a really helpful article, Class-less JavaScript,解釋了一種做法。

1

我會說正確的方法是使用原型,因爲畢竟Javascript是如何設計的。所以:

var Example = function(){ 
    this.prop = 'whatever'; 
} 

Example.prototype.fn_1 = function(){ 
    console.log(this.prop); 
    return this 
} 

Example.prototype.fn_2 = function(){ 
    this.prop = 'not whatever'; 
    return this 
} 

var e = new Example(); 

e.fn_1() //whatever 
e.fn_2().fn_1() //not whatever 

這裏有一個小提琴http://jsfiddle.net/BFm2V/

相關問題