2010-06-09 36 views
1

我在回調函數工作時遇到了一些麻煩。這裏是我的代碼:使用Javascript回調的範圍界定問題

SomeObject.prototype.refreshData = function() 
{ 
    var read_obj = new SomeAjaxCall("read_some_data", { }, this.readSuccess, this.readFail); 
} 

SomeObject.prototype.readSuccess = function(response) 
{ 
    this.data = response; 
    this.someList = []; 
    for (var i = 0; i < this.data.length; i++) 
    { 
     var systemData = this.data[i]; 
     var system = new SomeSystem(systemData); 
     this.someList.push(system); 
    } 
    this.refreshList(); 
} 

基本上SomeAjaxCall正在做一個數據的ajax請求。如果它有效,我們使用回調'this.readSuccess',如果失敗'this.readFail'。

我已經知道SomeObject.readSuccess中的'this'是全局的(又稱窗口對象),因爲我的回調被稱爲函數而不是成員方法。我的理解是,我需要使用閉包來保持'這個',但是,我還沒有能夠得到這個工作。

如果有人能夠告訴我我應該做什麼,我會非常感激。我仍然圍繞着關閉如何工作,特別是他們如何在這種情況下工作。

謝謝!

回答

2

那麼最簡單的事情做的是隻是包裝「this.readSuccess」在另一個功能:

SomeObject.prototype.refreshData = function() 
{ 
    var obj = this; 
    var read_obj = new SomeAjaxCall("read_some_data", { }, 
    function() { obj.readSuccess(); }, function() { obj.readFail(); }); 
} 

一些JavaScript框架提供了一個實用程序來「綁定」一個對象的功能,它只是手段它爲你創造了這些小功能之一。請注意,變量「obj」將被這些小函數「記住」,所以當您的處理程序被調用時,「this」引用將用於調用「refreshData」的對象。

+0

的確,這裏發生的是'obj'被鎖定在'function(){obj.readSuccess(); ''被評估。除非你使用像Prototype或jQuery這樣的庫,否則沒有其他辦法可以做到這一點;它們有一個'.bind()'函數,可以透明地完成閉包創建。 – MvanGeest 2010-06-09 13:09:27

+0

不要挑剔,但爲了StackOverflow後代的緣故,jQuery版本被稱爲「代理」,而不是「綁定」。 – Pointy 2010-06-09 13:15:52

+0

非常有意義。 jQuery代理函數的工作原理很好。 但是,爲了完整性,我仍然有點困惑,如果我不使用jQuery,我會如何處理這個問題。在我的例子中,readSuccess有1個參數respnse,它在以後填充。所以說我沒有意義說function(){obj.readSuccess(wah ??); }因爲哇?應該在晚些時候通過。 我試過function(){obj.readSuccess; }沒有運氣。有什麼想法嗎? – nazbot 2010-06-09 16:12:05

0

下面的網站似乎表明,問題可能在您的「班級」建築中比在使用中更多。如果你讀到「使用原型屬性重寫」,他們寫道,「類」結構化的特定方法將使方法保持全局而不是基於實例。也許另一種創作方法?

http://devedge-temp.mozilla.org/viewsource/2001/oop-javascript/

2

你這裏的問題不是正是封閉或範圍界定問題。問題在於,當您將this.readSuccess分配給變量時,您可以指定函數本身,而不包含它原來所屬的對象的任何概念。

以同樣的方式,你可以採取常規的「獨立」的功能,並把它作爲一個對象的方法:

function hello() { 
    alert("Hello "+this.planet); 
} 
var planet = "Earth"; 
hello(); // -> 'Hello Earth' 

var Venus = { 
    planet: "Venus" 
}; 
hello.apply(Venus); // -> 'Hello Venus' 
Venus.hello = hello; 
Venus.hello(); // -> 'Hello Venus' 

而且您的問題可以在這個例子中複製

var helloVenus = Venus.hello; 
helloVenus(); // -> 'Hello Earth' 

所以你的問題是將this.readSuccess分配給一些變量,並將其稱爲此的方法。這可以通過Pointy展示的封閉來完成。由於我不知道「SomeAjaxCall」實際上做了什麼,因此很難知道this的值是否實際上丟失,並且實際上是否需要var obj = this。機會是,它不是,所以你可以罰款這種代碼:

var helloVenus = function() { Venus.hello() } 
helloVenus(); // -> 'Hello Venus' 

在你的情況,這將是(編輯:將傳遞給處理程序的參數):

SomeObject.prototype.refreshData = function() 
{ 
    var read_obj = new SomeAjaxCall(
    "read_some_data", 
    { }, 
    function() { this.readSuccess.apply(this, arguments) }, 
    function() { this.readFail.apply(this, arguments) } 
); 
} 

如前所述,幾個js框架提供bind函數來簡化這類問題。但你並不需要只爲這一個完整的框架:這裏是一個完全正常的Function#bind方法工作的一個普通的JavaScript:

Function.prototype.bind = function(obj) { 
    var __method = this; 
    var args = []; 
    for(var i=1; i<arguments.length; i++) 
     args.push(arguments[i]); 
    return function() { 
     var args2 = []; 
     for(var i=0; i<arguments.length; i++) 
      args2.push(arguments[i]); 
     return __method.apply(obj, args.concat(args2)); 
    }; 
} 

隨着Function#bind的幫助下,你可以這樣寫:

SomeObject.prototype.refreshData = function() 
{ 
    var read_obj = new SomeAjaxCall(
    "read_some_data", 
    { }, 
    this.readSuccess.bind(this), 
    this.readFail.bind(this) 
); 
} 
+0

這是一個很好的答案。非常感謝這些信息。 – nazbot 2010-06-09 17:30:15

+0

並且bind現在是ECMAScript 5的一部分:https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/bind – w00t 2012-02-10 09:35:30