jQuery回调函数$(this)上下文的规则是什么

时间:2013-05-14 17:08:06

标签: jquery

似乎上下文$(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'),这似乎不会影响。什么是更好或更常见的代码编码方式?

3 个答案:

答案 0 :(得分:2)

简单的答案是它不会改变上下文。或者更确切地说,它与它改变其背景的方式一致。当您使用done方法时,该方法特定于您正在进行的ajax通话。因此,this在这种情况下绝对意味着与this处理程序内的submit不同。

如果您在提交处理程序的之外调用了ajax,那么您期望this会是什么?

总结:this根据定义是上下文的。当您更改上下文时(例如进行ajax调用以及该对象的方法/回调内部),this的含义肯定会发生变化。如果您需要以前的this上下文值,则需要跟踪更高版本闭包中的先前this值,这正是您在设置$this时所做的。

答案 1 :(得分: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
    };
});

请注意,锚标记没有提交事件,因此您的代码没有多大意义......

答案 2 :(得分: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为我们提供了两个选项:apply和   调用

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   对象和方法(可能属于所述对象)将会   产生一个全新的功能(我们正在返回的匿名功能)。

     

你甚至可以绑定吗?

     

现在我们已经完成了绑定的细节,这是公平的   压力,有时,绑定是矫枉过正。具体来说,有一个   可以替换绑定的代码模式,具有重要意义   通过使用词汇封闭来提高绩效。 (如果你不清楚的话   关于什么是关闭,不要惊慌。)

     

这是模式:方法中的某些代码依赖于匿名   函数通过引用传递给工作。那个匿名函数需要   访问周围方法的关键字。例如,假设   一分钟我们有数组中的每个迭代器,考虑一下   再次关注代码:

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

匿名方法可以围绕createBoundedWrapper,内部this将指向外部范围this

  

然而,这样的代码并不像看起来那么好。我们看到了   实现这样一个“约束参考”需要我们包装原件   匿名函数中的方法,表示调用绑定   方法引用导致两个方法调用:我们的匿名包装器,   和原始方法。如果有一件事情是真实的   任何语言,这种方法调用都很昂贵。

     

在这种情况下,我们可以访问原始版本,希望如此   关键字在我们定义和调用故障的相同代码位置   function(我们作为参数传递给每个人的匿名方法)。   我们可以简单地将这个引用保存在局部变量中,并且   在我们的迭代函数中使用它:

// ...
  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.

希望这会有所帮助。