8

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

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

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

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

这不起作用,因为当 keypress 事件调用searchKeyPressed处理程序时,它不会在this. 我能想到的唯一解决方案是只有现代浏览器支持的解决方案,即将回调绑定到this,这实际上创建了一个新函数。由于它创建了一个新函数,因此我必须缓存它以便稍后将其删除,因为我必须将相同的引用off传递给我传递给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)this是“currentTarget”(在冒泡术语中不一定是“目标”),而当使用时on('eventname','selector',instance.func)this是指匹配选择器的元素。在任何一种情况下,func运行都好像与 没有关系instance

4

5 回答 5

8

如果您向事件添加命名空间,您可以绑定事件并轻松地一次性解除绑定。

绑定:

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

解除绑定:

$(this.element).off('.mynamespace');
于 2015-12-01T23:42:37.427 回答
3

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

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));
};
于 2015-12-03T09:47:02.567 回答
3

首先,除非您希望您的页面寿命很长(或者您正在聆听数百种内容的按键,这是一个非常大的架构问题),否则这不会是一个很大的问题,涉及内存,即使手机有“钥匙”。

其次.bind对所有不到五年的浏览器都有很好的支持,并且很容易填充。

第三:你是 100% 正确的,必须缓存函数以便以后处理它并不酷,所以让我们做点什么

addEventListener(and )有一个鲜为人知的技巧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 的对象,以成为thishandleEvent 回调的内部。

只有一个实例需要担心。如果我需要注销,我不会缓存任何额外的东西。

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

语法还允许将各种很棒的东西链接在一起的额外好处。

于 2015-12-02T00:25:18.290 回答
1

添加bind(this)构造函数 - 这样您就不会在每次调用时都创建新函数.bindbind每次调用它时都会创建新函数,因此如果使用 this: 附加它$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)and $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);
}
于 2017-11-16T12:56:17.757 回答
0

您可以使用闭包。

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

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

使用 apply 传递参数。

于 2015-12-01T23:24:51.590 回答