正确的绑定方式“;这个“;在JavaScript事件回调中
Correct way to bind "this" in JavaScript event callbacks?
我创建了一个名为SearchBox的类来处理搜索交互(延迟触发、按回车键进行搜索、在活动时阻止搜索、在搜索完成且文本发生更改时同步结果等)
所有的类方法都是原型方法,可以通过this
访问。在下面的代码中,假设p
是类的原型。
p.registerListeners = function () {
$(this.element).on('keypress', this.searchKeyPressed);
};
p.unregisterListeners = function () {
$(this.element).off('keypress', this.searchKeyPressed);
};
这不起作用,因为当按键事件调用searchKeyPressed
处理程序时,它在this
的上下文中不会这样做。我能想到的唯一解决方案是只有现代浏览器支持的解决方案,即将回调绑定到this
,这实际上创建了一个新函数。由于它创建了一个新函数,我必须缓存它,以便稍后删除它,因为我必须传递与传递给on
相同的对off
的引用。
有比这更好的方法吗?或者这可以吗?
var boundKeyPressed;
p.registerListeners = function () {
boundKeyPressed = this.searchKeyPressed.bind(this);
$(this.element).on('keypress', boundKeyPressed);
};
p.unregisterListeners = function () {
$(this.element).off('keypress', boundKeyPressed);
};
我认为jQuery.on
可能会提供一种自动进行事件绑定的方法,但它似乎会根据调用方式将this
绑定到不同的东西。例如,使用on('eventname',instance.func)
时,this
是"currentTarget"(不一定是冒泡术语中的"target"),而使用on('eventname','selector',instance.func)
时,this
是指与选择器匹配的元素。在任何一种情况下,func
都会像与instance
没有关系一样运行。
如果向事件添加命名空间,则可以绑定事件并轻松地一次解除所有事件的绑定。
绑定:
$(this.element).on('keypress.mynamespace', this.searchKeyPressed.bind(this));
解除绑定:
$(this.element).off('.mynamespace');
First,除非您希望您的页面使用寿命很长(或者您正在听带有数百种内容的按键,这是一个非常大的架构问题),否则这不会成为内存问题,即使手机有"键"。
第二,.bind
对所有不到五年历史的浏览器都有很好的支持,而且polyfill非常简单。
第三:你是100%正确的,必须缓存函数以便稍后处理它是不酷的,所以让我们对此做点什么。
addEventListener
(和attachEvent
)有一个鲜为人知的技巧,它很乐意支持对象,并在对象上使用handleEvent
方法。
我并不是什么都用它,因为有时它真的不值得,但对于游戏制作,我用它来输入,有点像:
class Input {
constructor (events) {
this.events = events || [];
}
handleEvent (e) {
var input = this;
var method = e.type;
if (typeof input[method] === "function") {
input.dispatchEvent(method, e);
}
}
dispatchEvent (method, content) {
var input = this;
input[method](content);
}
listen (el, events) {
var input = this;
events = events || input.events;
events.forEach(event => el.addEventListener(event, input));
return this;
}
ignore (el, events) {
var input = this;
events = events || input.events;
events.forEach(event => el.removeEventListener(event, input));
return this;
}
}
class Keyboard extends Input {
constructor () {
super(["keydown", "keyup"]);
var keyboard = this;
keyboard.keys = new Set();
}
press (key) { this.keys.add(key); }
release (key) { this.keys.delete(key); }
isPressed (key) { return this.keys.has(key); }
keydown (e) {
var key = e.keyCode;
this.press(key);
}
keyup (e) {
var key = e.keyCode;
this.release(key);
}
}
我可以:
var gameplayEvents = ["keyup", "keydown"];
var keyboard = new Keyboard();
keyboard.listen(canvas, gameplayEvents);
// ongameover
keyboard.ignore(canvas, gameplayEvents);
如果你注意到的话,这都是100%纯JS。没有jQuery、extJS等。实际上,它也没有太多的代码。如果我只需要一个实例来处理mouseup和mousedown,我就可以将它变成一个对象文字;实际上,我所需要的只是一个带有handleEvent的对象,以便在handleEvent回调中成为this
。
只有一个例子值得担心。如果需要注销,我不会缓存任何额外的内容。
jQuery(和其他人)实际上在内部使用了这一点,以优化他们通常被滥用来生成的糟糕代码。
是的,也许我用ES6作弊。。。但这根本没有必要。
它比我过去做的事情更令人愉快:
function Parent (args) { }
extend(Parent.prototype, { /*public-methods*/ });
function Child (args) {
Parent.call(this);
// construct
}
extend(
Child.prototype,
Parent.prototype,
{ /*public-override-methods*/ },
{ constructor: Child }
);
同样,很多时候绑定是100%有效的
现在有一个关于绑定的ES7版本的建议,每次调用它时(如果通过这种方式),它都可能产生相同的值。
此外,语法还允许将各种很棒的东西链接在一起。
您可以不使用bind
,而是使用jQuery.proxy
函数来保留上下文,这将创建一个包装函数,您可以取消绑定:
jQuery.proxy
有多种变体,但目前您必须使用jQuery.proxy(function, context)
:
p.registerListeners = function () {
$(this.element).on('keypress', $.proxy(this.searchKeyPressed, this));
};
p.unregisterListeners = function () {
$(this.element).off('keypress', $.proxy(this.searchKeyPressed, this));
};
在构造函数中添加bind(this)
,这样就不会在每次调用.bind
时都创建新函数。bind
在每次调用它时都会创建新函数,因此如果使用以下内容附加它:$el.on("click", this.handler.bind(this))
,则无法使用$el.off("click", this.handler.bind(this))
分离它,因为处理程序不相同。(this.handler.bind(this) !== this.handler.bind(this)
)如果保存对绑定函数的引用(就像在构造函数this.handler = this.handler.bind(this)
中一样),则可以保存$el.on("click", this.handler)
和$el.off("click", this.handler)
,因为处理程序是相同的。
使用此方法,实质上就是覆盖该实例的函数。它将不再调用原型上的类函数,而是调用该指令上的类功能。
function MyObject($el) {
this.testValue = "I am really this!";
this.$el = $el;
this.onClick = this.onClick.bind(this);
this.render();
}
MyObject.prototype.onClick = function(evt) {
console.log(this.testValue); // logs "I am really this!"
}
MyObject.prototype.render = function() {
var $a = $("<a>", {"text": "Click on me!"}).appendTo($el.empty());
$a.on("click", this.onClick);
}
您可以使用闭包。
p.registerListeners = function() {
var me = this;
$(me.element).on('keypress', function() {
me.searchKeyPressed.apply(me, arguments);
});
};
使用apply传入参数。
- 如何将javascript事件从web浏览器wpf控件发送到wpf的c#代码
- 在同一个javascript事件处理程序中调用不同的函数
- Javascript事件;在新选项卡中打开”;
- 存在每个时间元素的javascript事件
- Javascript事件.锚的目标问题
- 带有参数的Javascript事件处理程序
- 如何从另一个处理程序内部取消JavaScript事件处理程序函数的执行
- firefox中的Javascript事件范围问题
- Firefox中的JavaScript事件参数
- 在动态加载的PHP表单上放置JavaScript事件
- 如何在Javascript事件上从JSNI设置GWTClass字段
- Javascript事件发射器一次处理多个事件
- 在下拉式javascript事件监听器中选择时触发事件
- 在javascript中导入xlsx文件时,如何手动强制javascript事件
- 显示触发的JavaScript事件
- 正在取消IE11中的JavaScript事件
- 页面卸载期间的JavaScript事件循环
- 存在其他参数时访问Javascript事件
- 阻止Javascript事件影响子元素
- 如何在事件处理程序中获取 javascript 事件对象