• 欢迎访问天天编码网站,Java技术、技术书单、开发工具,欢迎加入天天编码
  • 如果您觉得本站非常有看点,那么赶紧使用Ctrl+D 收藏天天编码吧
  • 我们的淘宝店铺已经开张了哦,传送门:https://shop145764801.taobao.com/

7.11 Mixins(混入)

JS 教程 tiantian 279次浏览 0个评论 扫描二维码

在 JavaScript 中,我们仅可以继承单一的对象。一个对象只有一个[[Prototype]]。而且,一个类只可以继承另一个其他类。

但是,某些情况下那将是很不方便的。举例,我具有一个类StreetSweeper和另一个类Bicycle,然后我们想创建一个StreetSweepingBicycle类。

或者,我们具有一个Renderer类实现了模板,另一个类EventEmitter实现了事件处理,然后希望合并那些函数,创建一个类Page。使得该Page类的对象可以使用模板和发送事件。

业界有一个概念,称为“mixins(混入)”,可以帮组我们。

正如在 Wikipedia 中所述,一个mixin是一个类,该类包含的方法可以被其他类使用,而不需要该类成为其他类的父类。

换句话说,一个 mixin 提供方法,其实现了某些特定的行为,但是我们不会单独使用它,我们利用它来增加其他类的行为。

一个 mixin 实例

在 JavaScript中,创建mixin的最简单方式就是创建一个具有多个实用方法的对象,这样我们就可以轻易地合并它们到另一个类的原型中。

举例,此处的mixinsayHiMixin用来给用户添加某些speech

// mixin
let sayHiMixin = {
  sayHi() {
    alert(`Hello ${this.name}`);
  },
  sayBye() {
    alert(`Bye ${this.name}`);
  }
};

// usage:
class User {
  constructor(name) {
    this.name = name;
  }
}

// copy the methods
Object.assign(User.prototype, sayHiMixin);

// now User can say hi
new User("Dude").sayHi(); // Hello Dude!

此处不存在继承,而是一个简单的方法复制。所以User可以继承某些其他的类,或者包含其他的mixin来混合其他的方法,比如:

class User extends Person {
  // ...
}

Object.assign(User.prototype, sayHiMixin);

Mixin 可以在不影响继承的情况下扩展功能。

举例,此处的sayHiMixin继承自sayMixin

let sayMixin = {
  say(phrase) {
    alert(phrase);
  }
};

let sayHiMixin = {
  __proto__: sayMixin, // (or we could use Object.create to set the prototype here)

  sayHi() {
    // call parent method
    super.say(`Hello ${this.name}`);
  },
  sayBye() {
    super.say(`Bye ${this.name}`);
  }
};

class User {
  constructor(name) {
    this.name = name;
  }
}

// copy the methods
Object.assign(User.prototype, sayHiMixin);

// now User can say hi
new User("Dude").sayHi(); // Hello Dude!

请注意,上述的那个位于sayHiMixin的调用super.say(),对于父方法的调用是在那个mixin的原型中进行查找,而不是该类中。

7.11 Mixins(混入)

这是因为sayHiMixin的方法具有[[HomeObject]]被设置。所以super实际上指向sayHiMixin.__proto__,而不是User.__proto__

EventMixin

现在,让我们来看看实际编程中的 mixin。

在将来,很多对象都需要与事件协作。

这意味着:一个对象在某些重要事情发生的情况下应该具有方法来“产生一个事件”,然后其他的对象应该可以监听到这样的事件。

一个事件应该具有一个名称,和可选的,绑定的其他数据。

举例,一个对象user可以在用户登录时产生一个事件"login"。然后,另一个对象calendar可以希望接受这样的事件,并为那个登录的用户加载日历。

或者,一个menu可以在某个 menu item 被选择时产生一个select事件,然后其他对象可能希望去获取那些信息并产生相应的行为。

事件是一种对象与其他感兴趣的对象共享信息的方式。它们在很多类中都具有十分重要的用处,所有,让我们来创建一个 mixin 吧:

let eventMixin = {
  /**
   * Subscribe to event, usage:
   *  menu.on('select', function(item) { ... }
  */
  on(eventName, handler) {
    if (!this._eventHandlers) this._eventHandlers = {};
    if (!this._eventHandlers[eventName]) {
      this._eventHandlers[eventName] = [];
    }
    this._eventHandlers[eventName].push(handler);
  },

  /**
   * Cancel the subscription, usage:
   *  menu.off('select', handler)
   */
  off(eventName, handler) {
    let handlers = this._eventHandlers && this._eventHandlers[eventName];
    if (!handlers) return;
    for (let i = 0; i < handlers.length; i++) {
      if (handlers[i] === handler) {
        handlers.splice(i--, 1);
      }
    }
  },

  /**
   * Generate the event and attach the data to it
   *  this.trigger('select', data1, data2);
   */
  trigger(eventName, ...args) {
    if (!this._eventHandlers || !this._eventHandlers[eventName]) {
      return; // no handlers for that event name
    }

    // call the handlers
    this._eventHandlers[eventName].forEach(handler => handler.apply(this, args));
  }
};

此处存在三个方法:

  1. .on(eventName, handler) —— 当那个给定名称的事件发生时,应该发生的函数handler保存起来。那些处理器保存在_eventhandlers属性上。
  2. .off(eventName, handler) —— 从那个处理器列表中移除掉指定的处理器。
  3. .trigger(eventName, ...args) —— 产生事件:所有已保存的处理器被调用,并且args作为参数传递给它们。

用法:

// Make a class
class Menu {
  choose(value) {
    this.trigger("select", value);
  }
}
// Add the mixin
Object.assign(Menu.prototype, eventMixin);

let menu = new Menu();

// call the handler on selection:
menu.on("select", value => alert(`Value selected: ${value}`));

// triggers the event => shows Value selected: 123
menu.choose("123"); // value selected

现在,如果我们的代码对于用户的选择感兴趣,我们就可以使用menu.on(...)来绑定处理器。

而且,那个eventMixin可以给任意数量的类添加如上的行为,只要我们愿意,而且不需要与原型链打交道。

总结

Mixin —— 是一个面向对象编程的通用概念:一个具有多个实用方法的类,方便其他类的操作。

其他的某些语言,比如 python,允许通过多重继承的方式来实现混合。JavaScript 不支持多重继承,但是可以通过将方法复制到它们的原型链的方式来实现混合。

我们可以使用 mixin 的方式,在多个方面来增强类的功能,比如我们上述示例中的事件处理。

Mixin 可以演变成为一个容易混淆的点,如果它们不经意间覆写了原生的类方法。所以,通常应该仔细考虑混合类的名称,来最小化上述的混淆情况。


天天编码 , 版权所有丨本文标题:7.11 Mixins(混入)
转载请保留页面地址:http://www.tiantianbianma.com/mixins.html/
喜欢 (1)
支付宝[多谢打赏]
分享 (0)
发表我的评论
取消评论

表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址