混合模式(X)

mixin 是一个对象,可以使用它来向另一个对象或类添加可重用的功能,而无需使用继承。

不能单独使用 mixin:它们的唯一目的是在没有继承的情况下向对象或类添加功能。


创建一个 mixin

假设对于我们的应用程序,需要创建多个狗。 然而,创建的基本狗没有任何属性,只有一个名称属性。

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

一只狗应该能够做的不仅仅是有一个名字。 它应该会吠叫、摇尾巴和玩耍!

可以创建一个 mixin,为我们提供 bark、wagTail 和 play 属性,而不是直接将其添加到 Dog 中。

const dogFunctionality = {
  bark: () => console.log("Woof!"),
  wagTail: () => console.log("Wagging my tail!"),
  play: () => console.log("Playing!")
};

我们可以使用 Object.assign 方法将 dogFunctionality mixin 添加到 Dog 原型中。

此方法向目标对象添加属性:在本例中为 Dog.prototype

Dog 的每个新实例都可以访问 dogFunctionality 的属性,因为它们已添加到 Dog 的原型中!

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

const dogFunctionality = {
  bark: () => console.log("Woof!"),
  wagTail: () => console.log("Wagging my tail!"),
  play: () => console.log("Playing!")
};

Object.assign(Dog.prototype, dogFunctionality);

创建第一个宠物 pet1,称为 Daisy。

由于刚刚在 Dog 的原型中添加了 dogFunctionality mixin,Daisy 应该能够走路、睡觉和玩耍!

1
2
3
4
5
6

const pet1 = new Dog("Daisy");

pet1.name; // Daisy
pet1.bark(); // Woof!
pet1.play(); // Playing!

Perfect! Mixins 使我们可以轻松地向类或对象添加自定义功能,而无需使用继承。


从 mixin 继承

虽然可以在没有继承的情况下使用 mixin 添加功能,但 mixin 本身可以使用继承!

大多数哺乳动物(除了海豚……也许还有更多)也可以走路和睡觉。 狗是哺乳动物,应该会走路和睡觉!

让我们创建一个添加步行和睡眠属性的 animalFunctionality mixin。

可以使用 Object.assign 将这些属性添加到 dogFunctionality 原型中。 在这种情况下,目标对象是 dogFunctionality。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

const animalFunctionality = {
  walk: () => console.log("Walking!"),
  sleep: () => console.log("Sleeping!")
};

const dogFunctionality = {
  bark: () => console.log("Woof!"),
  wagTail: () => console.log("Wagging my tail!"),
  play: () => console.log("Playing!"),
  walk() {
    super.walk();
  },
  sleep() {
    super.sleep();
  }
};
Object.assign(dogFunctionality, animalFunctionality);
//或者
<!-- const dogFunctionality = {
  __proto__: animalFunctionality,
  bark: () => console.log("Woof!"),
  wagTail: () => console.log("Wagging my tail!"),
  play: () => console.log("Playing!"),
  walk() {
    super.walk();
  },
  sleep() {
    super.sleep();
  }
}; -->

Object.assign(Dog.prototype, dogFunctionality);
1
2
3
4
5
6
7
8

const pet1 = new Dog("Daisy");

console.log(pet1.name); //Daisy 
pet1.bark(); //Woof!
pet1.play(); //Playing! 
pet1.walk(); //Walking!
pet1.sleep(); //Sleeping! 

Perfect! Dog 的任何新实例现在也可以访问 walk 和 sleep 方法。


浏览器中的 mixin

现实世界中的 mixin 示例在浏览器环境中的 Window 界面上可见。

Window 对象从 WindowOrWorkerGlobalScopeWindowEventHandlers 混合中实现了它的许多属性,
这允许我们访问诸如 setTimeout 和 setInterval、indexedDB 和 isSecureContext 等属性。

由于它是一个 mixin,因此仅用于向对象添加功能,无法创建 WindowOrWorkerGlobalScope 类型的对象。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

window.indexedDB.open("toDoList");

window.addEventListener("卸载前", event => {
  event.preventDefault();
  event.returnValue = "";
});

window.onbeforeunload = function() {
  console.log("卸载!");
};

console.log(
  "来自 WindowEventHandlers mixin:卸载前",
  window.onbeforeunload
);

console.log(
  "来自 WindowEventHandlers mixin:是安全上下文",
  window.isSecureContext
);

console.log(
  "WindowEventHandlers 本身未定义",
  window.WindowEventHandlers
);

console.log(
  "WindowOrWorkerGlobalScope 本身未定义",
  window.WindowOrWorkerGlobalScope
);

//来自 WindowEventHandlers mixin:卸载前 f(){}
//来自 WindowEventHandlers mixin:是安全上下文 true
//WindowEventHandlers 本身未定义 undefined
//WindowOrWorkerGlobalScope 本身未定义 undefined

React (ES6之前)

在引入 ES6 类之前,通常使用 Mixin 向 React 组件添加功能。

React 团队不鼓励使用 mixin,因为它很容易给组件增加不必要的复杂性,使其难以维护和重用。

React 团队鼓励使用高阶组件,现在通常可以用 Hooks 代替。


缺点

Mixins 允许我们通过将功能注入对象的原型,轻松地向对象添加功能而无需继承。

修改一个对象的原型被视为不好的做法,因为它会导致原型污染和函数来源的一定程度的不确定性。


知识点

  • Object.assign 用于将所有可枚举属性的值从一个或多个源对象分配到目标对象。它将返回目标对象。