JavaScript: 将对象的方法绑定到事件处理程序

3
据John Resig的《学习高级JavaScript》(http://ejohn.org/apps/learn/#83)所述,绑定对象的方法到事件处理程序时如果没有传递原始对象作为上下文是不正确的,但我发现该示例有缺陷。它声称clicked属性是意外设置的。以下是一个反例。
var Button = {
  click: function(){
    this.clicked = true;
    console.log( elem.clicked );
  }
};

var elem = document.createElement("li");
elem.innerHTML = "Click me!";
elem.onclick = Button.click;
document.children[0].appendChild(elem);

console.log( !elem.clicked );

不做这件事肯定还有其他原因。是什么呢?


你的意思是什么?那段代码确实会设置<li>元素的“clicked”属性。 - Pointy
你还跳过了那个示例中非常重要的代码行,即实际触发"click"事件的代码。在你上面的代码中,"click"函数甚至永远不会被调用。 - Pointy
如果代码能够正常运行,那么它并不是“错误”的,但可能会让其他的程序员感到困惑。 - dandavis
4个回答

9

在任何对象方法中,this 总是指向调用该方法的对象。对于所有 JavaScript 来说,this 提供了 调用上下文,而不是 方法所有者(*)

var sample = {
    foo: function () {
        this.clicked = true;
    }
}

sample.foo();         // 'this' refers to 'sample' 
alert(sample.clicked) // true

在事件处理程序中,this指的是触发事件的元素。这意味着当你将一个对象方法传递给click事件时...
var div = document.getElementById("test");
div.onclick = sample.foo;

那么,foo()将在DOM元素上被调用,即使它已在其他地方定义。

/* ... click the div ... */ 

alert(sample.clicked); // false
alert(div.clicked);    // true

这将导致意外的结果。


(*) 这是因为在JavaScript中技术上不存在方法所有者。 方法是独立的函数,恰好被对象属性引用。 在不同的对象中存储对相同功能的引用(div.onclick = sample.foo 就是这样做的)是很容易的。

因此,obj.method() 是语法糖。

function func() { /* ... */ }
var obj = {
    method: func
};

obj.method();
func.call(obj);  // same thing

3
你也可以使用 Function.prototype.bind 来解决这个问题。
elem.onclick = Button.click.bind(Button);

注意:.bind 方法需要 ECMAScript 版本 >= 5


2
Resig先生所讨论的是在JavaScript中,this的取值方式与其他一些语言中的this有着明显的不同。我认为他的观点是,在这个例子中,“Button”对象上“click”函数中对this的引用并不一定指向该对象本身,而是由调用该函数的情况决定其值。
因此,当您将该函数用作事件处理程序时,函数中this的值将是对被单击的元素的引用。

0
这是一个没有事件的简单示例。
var foo = {
  method: function() {
    this.prop = true;
  }
};

var bar = {};

bar.method = foo.method;
bar.method();

console.log( foo.prop ); // => undefined
console.log( bar.prop ); // => true

foo.method();

console.log( foo.prop ); // => true

在你的例子中,似乎是在点击元素之前检查了elem.clicked的状态。点击它,属性将被设置(但Button.clicked不会被设置!)。正确的做法是:

elem.onclick = function() { Button.click(); };

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接