FrankKai / FrankKai.github.io

FE blog
https://frankkai.github.io/
363 stars 39 forks source link

DOM进阶之EventTarget #188

Open FrankKai opened 4 years ago

FrankKai commented 4 years ago

之所以学EventTarget,是因为Element对象是最终继承自这个interface的。 这个interface在前端事件方面扮演着非常重要的作用,addEventListener,removeEventListener和dispatchEvent都是在这个interface内部实现的。

image

EventTarget概览

EventTarget Constructor

最简单的例子

var myEventTarget = new EventTarget();

image 由上图可以看出,EventTarget interface上有addEventListener,dispatchEvent和removeEventListener三个方法,而且Symbol(Symbol.toStringTag)是"EventTarget"。

自定义EventTarget实例继承EventTarget interface

class MyEventTarget extends EventTarget {
  constructor(mySecret) {
    super();
    this._secret = mySecret;
  }
  get secret() { return this._secret; }
};

let myEventTarget = new MyEventTarget(5);
let value = myEventTarget.secret;  // == 5
myEventTarget.addEventListener("foo", function(e) {
  this._secret = e.detail;
});

let event = new CustomEvent("foo", { detail: 7 });
myEventTarget.dispatchEvent(event);
let newValue = myEventTarget.secret; // == 7

拓展思考

Symbol.toStringTag是什么?

一目了然的demo, 可以学会如何自定义一个object 类型([object Number], [object FrankKaiClass])

class FrankKaiClass {
    get [Symbol.toStringTag]() {
        return 'FrankKaiClass';
    }
}
console.log(Object.prototype.toString.call(new FrankKaiClass()));
// [object FrankKaiClass]

EventTarget Methods

EventTarget 简单实现

var EventTarget = function() {
  this.listeners = {};
};

EventTarget.prototype.listeners = null;
EventTarget.prototype.addEventListener = function(type, callback) {
  if (!(type in this.listeners)) {
    this.listeners[type] = [];
  }
  this.listeners[type].push(callback);
};

EventTarget.prototype.removeEventListener = function(type, callback) {
  if (!(type in this.listeners)) {
    return;
  }
  var stack = this.listeners[type];
  for (var i = 0, l = stack.length; i < l; i++) {
    if (stack[i] === callback){
      stack.splice(i, 1);
      return;
    }
  }
};

EventTarget.prototype.dispatchEvent = function(event) {
  if (!(event.type in this.listeners)) {
    return true;
  }
  var stack = this.listeners[event.type].slice();

  for (var i = 0, l = stack.length; i < l; i++) {
    stack[i].call(this, event);
  }
  return !event.defaultPrevented;
};

参考资料:https://developer.mozilla.org/en-US/docs/Web/API/EventTarget