mixin-pattern

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Mixin Pattern

Mixin模式

A mixin is an object that we can use in order to add reusable functionality to another object or class, without using inheritance. We can't use mixins on their own: their sole purpose is to add functionality to objects or classes without inheritance.
Let's say that for our application, we need to create multiple dogs. However, the basic dog that we create doesn't have any properties but a
name
property.
Mixin是一种对象,我们可以用它在不使用继承的情况下,为其他对象或类添加可复用的功能。Mixin本身无法单独使用:它们的唯一作用就是在不借助继承的前提下,为对象或类添加功能
假设我们的应用需要创建多个狗的实例。不过我们创建的基础
Dog
类只有一个
name
属性,没有其他特性。

When to Use

使用场景

  • Use this when you need to add reusable functionality to multiple classes without creating an inheritance chain
  • This is helpful when you want to compose behavior from multiple sources
  • 当你需要为多个类添加可复用功能,同时又不想创建继承链时使用
  • 当你希望从多个来源组合行为时,这种方式很有帮助

Instructions

实现说明

  • Use
    Object.assign()
    to add mixin properties to a class prototype
  • Be cautious with prototype pollution — modifying prototypes can lead to unexpected behavior
  • In React, prefer Hooks over mixins (mixins are discouraged by the React team)
  • Consider composition over inheritance when designing reusable behavior
  • 使用
    Object.assign()
    将Mixin的属性添加到类的原型上
  • 要小心原型污染——修改原型可能会导致意外行为
  • 在React中,优先使用Hooks而非Mixin(React团队不推荐使用Mixin)
  • 在设计可复用行为时,考虑使用组合而非继承

Details

详细示例

js
class Dog {
  constructor(name) {
    this.name = name;
  }
}
A dog should be able to do more than just have a name. It should be able to bark, wag its tail, and play! Instead of adding this directly to the
Dog
, we can create a mixin that provides the
bark
,
wagTail
and
play
property for us.
js
const dogFunctionality = {
  bark: () => console.log("Woof!"),
  wagTail: () => console.log("Wagging my tail!"),
  play: () => console.log("Playing!"),
};
We can add the
dogFunctionality
mixin to the
Dog
prototype with the
Object.assign
method. This method lets us add properties to the target object:
Dog.prototype
in this case. Each new instance of
Dog
will have access to the properties of
dogFunctionality
, as they're added to the
Dog
's prototype!
js
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);
Let's create our first pet,
pet1
, called Daisy. As we just added the
dogFunctionality
mixin to the
Dog
's prototype, Daisy should be able to walk, wag her tail, and play!
js
const pet1 = new Dog("Daisy");

pet1.name; // Daisy
pet1.bark(); // Woof!
pet1.play(); // Playing!
Perfect! Mixins make it easy for us to add custom functionality to classes or objects without using inheritance.
Although we can add functionality with mixins without inheritance, mixins themselves can use inheritance!
Most mammals can walk and sleep as well. A dog is a mammal, and should be able to walk and sleep!
Let's create a
animalFunctionality
mixin that adds the
walk
and
sleep
properties.
js
const animalFunctionality = {
  walk: () => console.log("Walking!"),
  sleep: () => console.log("Sleeping!"),
};
We can add these properties to the
dogFunctionality
prototype, using
Object.assign
. In this case, the target object is
dogFunctionality
.
js
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);
Object.assign(Dog.prototype, dogFunctionality);
Perfect! Any new instance of
Dog
can now access the
walk
and
sleep
methods as well.
An example of a mixin in the real world is visible on the
Window
interface in a browser environment. The
Window
object implements many of its properties from the
WindowOrWorkerGlobalScope
and
WindowEventHandlers
mixins, which allow us to have access to properties such as
setTimeout
and
setInterval
,
indexedDB
, and
isSecureContext
.
Since it's a mixin, thus is only used to add functionality to objects, you won't be able to create objects of type
WindowOrWorkerGlobalScope
.
js
class Dog {
  constructor(name) {
    this.name = name;
  }
}
狗应该能做更多事情,而不只是拥有名字。它应该会吠叫、摇尾巴和玩耍!我们不用直接在
Dog
类中添加这些功能,而是可以创建一个Mixin来提供
bark
wagTail
play
方法。
js
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
的原型上!
js
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。由于我们刚刚把
dogFunctionality
Mixin添加到了
Dog
的原型上,Daisy应该能够吠叫、摇尾巴和玩耍!
js
const pet1 = new Dog("Daisy");

pet1.name; // Daisy
pet1.bark(); // Woof!
pet1.play(); // Playing!
完美!Mixin让我们不用继承就能轻松为类或对象添加自定义功能。
虽然我们可以通过Mixin在不使用继承的情况下添加功能,但Mixin本身也可以使用继承!
大多数哺乳动物都会走路和睡觉。狗是哺乳动物,也应该具备这些能力!
让我们创建一个
animalFunctionality
Mixin,添加
walk
sleep
方法。
js
const animalFunctionality = {
  walk: () => console.log("Walking!"),
  sleep: () => console.log("Sleeping!"),
};
我们可以用
Object.assign
把这些属性添加到
dogFunctionality
的原型上,这里的目标对象是
dogFunctionality
js
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);
Object.assign(Dog.prototype, dogFunctionality);
完美!现在任何新的
Dog
实例都能访问
walk
sleep
方法了。
现实世界中Mixin的一个例子是浏览器环境中的
Window
接口。
Window
对象的许多属性都是从
WindowOrWorkerGlobalScope
WindowEventHandlers
这两个Mixin实现的,这让我们可以访问
setTimeout
setInterval
indexedDB
isSecureContext
等属性。
由于它们是Mixin,因此只能用来为对象添加功能,你无法创建
WindowOrWorkerGlobalScope
类型的对象。

React (pre ES6)

React(ES6之前)

Mixins were often used to add functionality to React components before the introduction of ES6 classes. The React team discourages the use of mixins as it easily adds unnecessary complexity to a component, making it hard to maintain and reuse. The React team encouraged the use of higher order components instead, which can now often be replaced by Hooks.
Mixins allow us to easily add functionality to objects without inheritance by injecting functionality into an object's prototype. Modifying an object's prototype is seen as bad practice, as it can lead to prototype pollution and a level of uncertainty regarding the origin of our functions.
在ES6类引入之前,Mixin常被用于为React组件添加功能。React团队不推荐使用Mixin,因为它很容易给组件添加不必要的复杂度,使其难以维护和复用。React团队当时鼓励使用高阶组件,而现在高阶组件通常可以被Hooks替代。
Mixin允许我们通过向对象原型注入功能,在不使用继承的情况下轻松为对象添加功能。不过修改对象原型被视为不良实践,因为这可能导致原型污染,并且会让我们对函数的来源产生不确定性。

Source

来源

References

参考资料