2011-02-18 91 views
15

我正在嘗試將函數範圍傳遞給回調方法。我遇到的問題是我正在獲取對象範圍,它不能提供我訪問原始函數中的參數和局部變量的權限。我對「this」的理解是指當前的上下文(不管是窗口還是某個對象),以及本地聲明的變量和參數。 [引用Richard Cornford在http://jibbering.com/faq/notes/closures/關於「執行上下文」部分的出色工作]。我也明白,JavaScript中的變量具有函數作用域(如果它們是在函數內部聲明的,它們只能從該函數中訪問)。將範圍傳遞給回調函數/綁定

有了這樣的理解,在一個新的環境中,我試圖編寫一個模式,我爲我以前的僱主做了很多,調用異步方法,指定一個回調處理程序並傳遞當前作用域,期望它可用在回調方法中。我目前的環境中並沒有發現這種情況。 (披露:我在以前的環境中使用ExtJS ......現在讓我覺得我對這個框架感到非常舒適,並對發生的事情做出了假設)。

我的簡單測試代碼演示了我正在嘗試做什麼以及什麼不起作用。

function myHandler(data, ctx) { 
    console.log('myHandler(): bar: ' + bar); // <- prob: bar undefined 
    console.log(JSON.stringify(data)); 
} 
MySvcWrap = { 
    doWork: function(p1, callback, scope) { 
     var result = {colors: ['red', 'green'], name:'Jones', what: p1}; 
     if (callback) { 
      callback.call(scope||this,result, scope); 
     } 
    } 
} 
function lookup() { 
    var bar = 'food'; // local var 
    MySvcWrap.doWork('thang', myHandler, this); // scope object is this 
} 

lookup(); 

這裏的問題是傳遞給MySvcWrap.doWork的'this'在這種情況下是Window全局對象。我的意圖是將函數的執行上下文傳遞給myHandler。

我試過了。如果,而不是「這個」,我傳遞一個對象,這樣的作品,如:

function myHandler(data, ctx) { 
    console.log('myHandler(): this.bar: ' + this.bar); // <- no prob: this.bar 
    console.log(JSON.stringify(data)); 
} 

function lookup() { 
    var bar = 'food'; // local var 
    MySvcWrap.doWork('thang', myHandler, {bar: bar}); // scope object is just object 
} 

我需要有人來俱樂部我在這裏頭......路過的時候「這」在我的第一種情況下,當然這是全局範圍(我在一個全局定義的函數中)......我的問題是,當我傳遞範圍時,我想過我可以訪問本地定義的變量和參數w /在這種情況下...我是否在搖擺我之前的理解JS的?如何完成這項任務?

回答

16

順便說一句,你的代碼中範圍幾句話:

function lookup() { 
    var bar = 'food'; // local var 
    MySvcWrap.doWork('thang', myHandler, this); // this here points to window object and not to the function scope 
} 

所以它是一樣的文字:

function lookup() { 
    var bar = 'food'; // local var 
    MySvcWrap.doWork('thang', myHandler, window); 
} 

這是因爲你在全球範圍定義查找功能。裏面doWork功能,當你寫this它指向MySvcWrap對象(因爲該功能是在該對象內定義)。

如果你的回調函數必須看到bar變量,應在同一範圍內定義,這樣的

MySvcWrap = { 
    doWork: function(p1, callback, scope) { 
     var result = {colors: ['red', 'green'], name:'Jones', what: p1}; 
     if (callback) { 
      callback.call(scope||this,result, scope); 
     } 
    } 
} 
function lookup() { 
    var bar = 'food'; // local var 
    MySvcWrap.doWork('thang', 
     function (data, ctx) { 
      console.log('myHandler(): bar: ' + bar); 
      console.log(JSON.stringify(data)); 
     }, 
     this); // scope object is this 
} 

lookup(); 
在這種情況下

您發送匿名函數回調,則查找函數中定義,因此它可以訪問其局部變量;我的控制檯顯示我在這CAE:

myHandler(): bar: food 
{"colors":["red","green"],"name":"Jones","what":"thang"} 

,使其更易於支持,您可以定義查找函數內將myHandler:

function lookup() { 
    var bar = 'food'; // local var 
    var myHandler = function(data, ctx) { 
     console.log('myHandler(): bar: ' + bar); 
     console.log(JSON.stringify(data)); 
    }; 
    MySvcWrap.doWork('thang', myHandler, this); // scope object is this 
} 

在另一方面,爲什麼一個功能應該可以訪問本地另一個函數的變量?也許它可以重新設計...

還有一個方法,使你的代碼的工作就是使用匿名函數,而不是查找(將工作,如果你只需要聲明,一旦執行該功能):

(function() { 
    var bar = 'food'; 

    function myHandler(data, ctx) { 
     console.log('myHandler(): bar: ' + bar); 
     console.log(JSON.stringify(data)); 
    } 
    MySvcWrap = { 
     doWork: function(p1, callback, scope) { 
      var result = {colors: ['red', 'green'], name:'Jones', what: p1}; 
      if (callback) { 
       callback.call(scope||this,result, scope); 
      } 
     } 
    } 
    MySvcWrap.doWork('thang', myHandler, this); 
    } 
)(); 

結果是一樣的,但沒有查找函數了...

還有一個想法使它工作......其實你需要在同一個範圍內定義bar變量的地方定義回調處理程序,所以它可以做得有點棘手, :

function myHandler(bar) { // actually in this case better to call it createHandler 
    return function(data, ctx) { 
     console.log('myHandler(): bar: ' + bar); 
     console.log(JSON.stringify(data)); 
    } 
} 
MySvcWrap = { 
    doWork: function(p1, callback, scope) { 
     var result = {colors: ['red', 'green'], name:'Jones', what: p1}; 
     if (callback) { 
      callback.call(scope||this,result, scope); 
     } 
    } 
} 
function lookup() { 
    var bar = 'food'; // local var 
    MySvcWrap.doWork('thang', myHandler(bar), this); // scope object is this 
} 

很少的資源來了解JavaScript的作用域和關閉:

  1. Explaining JavaScript scope and closures
  2. Picking up Javascript - Closures and lexical scoping
  3. JavaScript: Advanced Scoping & Other Puzzles - 的話題非常漂亮的演示
+0

我喜歡的參數處理器方法。 – 2015-06-03 04:38:11

2

,如果它是結構類似的代碼會工作這個。

MySvcWrap = { 
    doWork: function(p1, callback, scope) { 
     var result = {colors: ['red', 'green'], name:'Jones', what: p1}; 
     if (callback) { 
      callback.call(scope||this,result, scope); 
     } 
    } 
} 
function lookup() { 
    var bar = 'food'; // local var 

    function myHandler(data, ctx) { 
     console.log('myHandler(): bar: ' + bar); // <- prob: bar undefined 
     console.log(JSON.stringify(data)); 
    } 

    MySvcWrap.doWork('thang', myHandler, this); // scope object is this 
} 

lookup(); 

myHandler函數可以訪問查找的局部變量,因爲它由它包含,因此是閉包。

我可能會嘗試通過構建這樣的代碼來達到相同的結果。

function myHandler(data, bar, ctx) { 
    console.log('myHandler(): bar: ' + bar); // <- prob: bar undefined 
    console.log(JSON.stringify(data)); 
} 
MySvcWrap = { 
    doWork: function(p1, callback) { 
     var result = {colors: ['red', 'green'], name:'Jones', what: p1}; 
     if (callback) { 
      callback(result); 
     } 
    } 
} 
function lookup() { 
    var bar = 'food'; // local var 
    MySvcWrap.doWork('thang', myHandler.bind(this, bar)); // callback function is bound to the scope 
} 

lookup(); 

而不是通過範圍,我使用查找方法內的綁定。使用綁定我也可以添加我希望從該範圍傳遞的局部變量。

當然,作爲綁定在舊版瀏覽器中不可用,您需要通過框架添加它,或者使用添加該方法的許多小代碼片段之一(如果該方法不存在)。

0

我對我的第一個答案有點敏捷,並留下了一些間隙,如Maxym在下面的評論中所述,謝謝你通知我:)這就是我在返回工作之前快速發帖的原因:P

我一直想把你在我的第一個答案是:如果你想使用綁定從功能myHandler(這是lookup範圍之外)訪問功能lookup變量,你必須不使用varthis代替。

使用var將使它只lookup's私人範圍和嵌套其中的任何功能,這是何等的其他答案已經證明(並可能是最好的路線)

下面是如何更新的例子訪問將查找範圍傳遞給myHandler,使myHandler完全脫離查找的範圍。希望這將有助於闡明:

「我遇到的問題是我得到的對象範圍,它不能提供我訪問原始函數中的參數和局部變量。「

如同在我以前的答案this評論說可能很麻煩,你可以結束了,如果不小心,就像我在我的第一個例子一樣的東西添加到global範圍。:(所以我添加了一個一個hack'ish校驗位,看看有什麼範圍this是確保它不是window用於演示目的...

Live Example

function myHandler(data, ctx) { 
    console.log('myHandler(): bar: ' + this.bar); // <- must use `this` 
    console.log('myHandler(): privateBar: ' + this.privateBar); // <- undefined because it's privately scoped 
    console.log(JSON.stringify(data)); 
} 
MySvcWrap = { 
    doWork: function(p1, callback, scope) { 
     var result = { 
      colors: ['red', 'green'], 
      name: 'Jones', 
      what: p1 
     }; 
     if (callback) { 
      callback.call(scope || this, result, scope); 
     } 
    } 
} 

function lookup() { 
    if(this == window) return lookup.call(lookup); // <- my hack'ish check 

    var privateBar = 'private food'; // private local var 
    this.bar = 'food'; // public local var 
    MySvcWrap.doWork('thang', myHandler, this); // scope object is this 
} 

lookup(); 

console.log('global space - bar: ' + this.bar); 
+1

`var bar = this.bar ='food';` - 在這種情況下,你在窗口對象中創建`bar`變量,所以它和在lookup函數外面聲明這個變量一樣,並且只是在這裏定義它, bar ='food'`,我的意思是不帶`var` ...現在這段代碼非常複雜,隱藏了變量`bar`現在也是全局的事實 – Maxym 2011-02-18 19:06:45