2013-05-14 85 views
0

看起來上下文$(this)在類選擇器的某些情況下會發生更改。我花了好幾個小時試圖調試以下的JavaScript的,對此我沒有任何好處在:

$.fn.specialbutton = function(callback) { 
    $(this).bind('click', function() { // $(this) is the a.button, as expected 
     $.ajax({ 
      url: 'ajax.php', 
      data: callback(); // context pitfall 
     }).done(function(response) { 
      $(this).html(response); // context changed to something unknown 
     }) 
    }); 
} 

$('a.button').specialbutton(function() { 
    // context pitfall 
    return { 
     id: $(this).data('id'); // $(this) is not what you expect 
    }; 
}); 

最後我想解決的辦法是,保存環境和使用回調的明確調用:

$.fn.specialbutton = function(callback) { 
    $(this).bind('click', function() { // $(this) is the a.button, as expected 

     var $this = $(this); // save the context; 

     $.ajax({ 
      url: 'ajax.php', 
      data: callback.call($this); // explicitly specify context 
     }).done(function(response) { 
      $this.html(response); // reuse the saved context 
     }) 
    }); 
} 

$('a.button').specialbutton(function() { 
    // context pitfall 
    return { 
     id: $(this).data('id'); // $(this) is what was specified in prototype 
    }; 
}); 

上下文變化的規則和原因是什麼?這是設計功能還是限制?這似乎不會影響選擇器是否爲HTML ID選擇器,即$('a#button')。對上述代碼進行編碼將會是更好還是更常見的方式?

+0

規則是,上下文會因地而異。這是一個與jQuery無關的javascript事物,它取決於每個函數的執行方式和/或定義的位置。例如,直接執行'callback'會使用它定義的上下文(它將是'window')來執行它,但是使用'.call()'當然會將上下文更改爲您傳遞給第一個參數的內容, .CALL()'。在ajax成功處理程序中,'this'引用了ajax調用的'context'屬性,默認情況下它是傳入'$ .ajax'的選項。這發生在使用'.call()'的jQuery內部。 – 2013-05-14 17:13:08

+0

這不是jQuery如何工作的問題,這是* JavaScript *如何工作的問題。每個函數都有自己的上下文('this')。如果您瞭解如何在JavaScript中處理上下文,那麼在jQuery中使用上下文也會更有意義。 – zzzzBov 2013-05-14 17:18:22

+0

好文章http://alistapart.com/article/getoutbindingsituations – shakib 2013-05-14 17:44:48

回答

1

由於提到article幫助你與你的答案,生病嘗試從文章中提及的關鍵點,

在JavaScript中,結合始終是明確的,並且很容易丟失,因此採用這種方法將不參考適當的Ø在所有情況下都會對抗,除非你強迫。總的來說,JavaScript中的綁定並不是一個困難的概念,但它經常被JavaScripters忽略或掩蓋,這導致了混淆。

例如:

var john = { 
    name: 'John', 
    greet: function(person) { 
    alert("Hi " + person + ", my name is " + this.name); 
    } 
}; 
john.greet("Mark"); 

//=> "Hi Mark, my name is John" 

話又說回來,

var john = { 
    name: 'John', 
    greet: function(person) { 
    alert("Hi " + person + ", my name is " + this.name); 
    } 
}; 
var fx = john.greet; 
fx("Mark"); 
// => "Hi Mark, my name is " 

這是用JavaScript 綁定東西,我會稱之爲「綁定丟失的一個最重要的問題。「只要通過引用而不是 直接通過它的所有者對象訪問方法,就會發生 。該方法失去了其隱含的 綁定,並停止引用它的所有者對象,並返回到 其默認值,在這種情況下,它是窗口(所以如果窗口名稱屬性爲 ,那麼它將被使用)。

認識結合敏感的代碼圖案

綁定敏感代碼模式涉及傳遞方法的引用, 這通常通過兩種可能的手段發生:或者您是 分配方法作爲一個值,或者你傳遞一種作爲 參數的方法(當你考慮 時,它本質上是一樣的)。

明確約束

那麼我們該如何解決?我們明確地綁定 - 也就是說,我們明確指出 它在被調用時指向該方法內的內容。和 我們該怎麼做? JavaScript爲我們提供了兩種選擇:申請和撥打 。

var fx = john.greet; 
fx.apply(john, ["Mark"]); 
// => "Hi Mark, my name is John" 

var fx = christophe.greet; 
fx.call(christophe, "Mark"); 
// => "Hi Mark, my name is John" 

所以,我們要的是要堅持結合的方法,使我們得到 綁定方法引用,所以說話的方式。實現這個 的唯一方法是要求我們將原始方法封裝在另一個方法中, 將執行應用調用。這裏有一個刺在它:

function createBoundedWrapper(object, method) { 
    return function() { 
    return method.apply(object, arguments); 
    }; 
} 

var john = { 
    name: 'John', 
    greet: function(person) { 
    alert("Hi " + person + ", my name is " + this.name); 
    } 
}; 
var fx = createBoundedWrapper(john, john.greet); 

fx("Mark"); 
// => "Hi Mark, my name is John" 

如果你不是JavaScript的過於熱衷,上面的代碼可能會混淆你 位。這裏的想法是,用給定的 對象和方法(其大概屬於所述對象)調用createBoundedWrapper將產生一個全新的函數(我們返回的匿名函數)。

你應該甚至綁定?

現在我們已經通過綁定的細節,它只是公平的壓力,有時,綁定是矯枉過正。具體來說,有一個 代碼模式,其中綁定可以被替換,並且通過使用詞法閉包而具有顯着的性能收益。 (如果你沒有一個明確的 封閉是什麼,不要驚慌。)

這裏的模式:一種方法中的一些代碼依賴於一個匿名 功能通過引用傳遞工作。該匿名函數需要 訪問周圍方法的this關鍵字。例如,假設 一分鐘,我們有陣列內的每個迭代器,再次考慮 以下代碼:

// ... 
    processItems: function() { 
    this.items.each(function(item) { 
     // Process item… 
     this.markItemAsProcessed(item); 
    }); 
    }, 
// ... 

匿名方法可以圍繞createBoundedWrapper被包裹,然後將內this將指向外範圍爲this

但是,這樣的代碼並不像看起來那麼好。我們看到 實現這樣的「綁定參考」要求我們將原始的 方法包裝在一個匿名函數中,這意味着調用綁定的 方法引用會導致兩個方法調用:我們的匿名包裝, 和原始方法。如果任何語言都有一件事情是真的,那就是方法調用是昂貴的。

在這種情況下,我們有訪問原始的,期望在我們定義和調用故障 函數(我們傳遞作爲參數傳遞給每個匿名方法)相同的代碼位置這 關鍵字。 我們可以簡單地保存適當的在一個局部變量此引用,並 使用我們的迭代函數內部:

// ... 
    processItems: function() { 
    var that = this; 
    this.items.each(function(item) { 
     // Process item 
     that.markItemAsProcessed(item); 
    }); 
    }, 
// ... 

外賣點

總結一下:

Any member access must be qualified with the object it pertains to, even when it is this. 
Any sort of function reference (assigning as a value, passing as an argument) loses the function’s original binding. 
JavaScript provides two equivalent ways of explicitly specifying a function’s binding when calling it: apply and call. 
Creating a 「bound method reference」 requires an anonymous wrapper function, and a calling cost. In specific situations, leveraging closures may be a better alternative. 

希望這有助於。

2

簡單的答案是它不會改變上下文。或者說,它與它的環境如何改變是一致的。當您處於done方法中時,該方法特定於您正在進行的ajax調用。所以在這種情況下,this絕對意味着與不同的是submit處理程序。

如果你所做的Ajax調用外提交處理的,你會期望this是?

總結:根據定義this是上下文。當你改變上下文(例如調用ajax調用以及該對象的內部方法/回調)時,this的含義肯定會改變。如果您需要先前的this上下文值,則需要跟蹤上一次關閉的this值,這正是您在設置$this時所做的。

1

上下文(this)將根據函數的定義和/或執行方式而改變。

一種更好的方式來寫這將是:

$.fn.specialbutton = function(callback) { 
    this.bind('submit', function() { // $(this) is the a.button, as expected 
     $.ajax({ 
      url: 'ajax.php', 
      data: callback.call(this), // explicitly specify context 
      context: this, // explicitly specify context 
     }).done(function(response) { 
      this.html(response); // the context is now correct 
     }) 
    }); 
} 

$('a.button').specialbutton(function() { 
    // context pitfall 
    return { 
     id: this.data('id'); // this is the element that was submitted 
    }; 
}); 

注意,錨標籤沒有提交事件,讓你的代碼沒有多大意義......

相關問題