2013-05-14 180 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 
    }; 
}); 

注意,锚标签没有提交事件,让你的代码没有多大意义......

相关问题