正确的约束方式"这个"在JavaScript事件回调中?

时间:2015-12-01 23:15:29

标签: javascript jquery jquery-callback function-binding

我创建了一个名为SearchBox的类来处理搜索交互(延迟触发,搜索输入按键,防止搜索活动,搜索完成时同步结果,文本更改等)。

所有类方法都是原型方法,意味着可以通过this访问。在下面的代码中,假设p是类的原型。

p.registerListeners = function () {
    $(this.element).on('keypress', this.searchKeyPressed);
};

p.unregisterListeners = function () {
    $(this.element).off('keypress', this.searchKeyPressed);
};

这不起作用,因为当按键事件调用searchKeyPressed处理程序时,它不会在this的上下文中执行此操作。我能想出的唯一解决方案是只有现代浏览器支持的解决方案,即将回调绑定到this,实际上创建了一个新函数。由于它创建了一个新函数,我必须将其缓存以便以后删除它,因为我必须将相同的引用传递给我传递给off的{​​{1}}。

有没有比这更好的方法,或者这样可以吗?

on

我认为也许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)是" currentTarget" (不一定"目标"以冒泡的方式),而当使用this时,on('eventname','selector',instance.func)指的是与选择器匹配的元素。在任何一种情况下,this都会运行,就好像它与func无关。

5 个答案:

答案 0 :(得分:3)

如果您为事件添加命名空间,则可以绑定事件并轻松地将它们全部解除绑定。

要绑定:

$(this.element).on('keypress.mynamespace', this.searchKeyPressed.bind(this));

取消绑定:

$(this.element).off('.mynamespace');

答案 1 :(得分:2)

首先,除非您希望自己的页面存在很长时间(或者您正在使用数百种内容来监听按键,这是一个非常大的架构问题),这不是即使手机有“钥匙”,也要成为一个存在问题的问题。

第二次.bind非常支持所有不到五年的浏览器,并且很容易被填充。

第三:你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,每次调用它时都会产生相同的值(如果它通过这种方式)。

还有一个额外的好处,即语法允许将各种可怕的东西链接在一起。

答案 2 :(得分:2)

您可以使用jQuery.proxy function来保留上下文,而不是使用bind,这会创建一个可以取消绑定的包装函数:

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));
};

答案 3 :(得分:0)

您可以使用闭包。

p.registerListeners = function() {
    var me = this;

    $(me.element).on('keypress', function() {
        me.searchKeyPressed.apply(me, arguments);
    });
};

使用apply传递参数。

答案 4 :(得分:0)

在构造函数中添加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);
}