似乎上下文$(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 :(得分: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.
希望这会有所帮助。