关于 mixin-pattern
一项介绍 JavaScript mixin 的技能,可在不建立继承链的情况下为对象或类添加可复用行为。内容说明基于原型的应用方式、适用场景,以及命名冲突和原型污染等风险。
主要功能
- 01无需继承即可添加行为
- 02使用 Object.assign() 应用属性
- 03组合多个 mixin 的行为
- 04留意原型污染风险
- 05在 React 中优先使用 Hooks
适用场景
- 01在多个类间复用方法
- 02组合来自不同来源的行为
- 03评估 React mixin 的替代方案
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ patternsdev/ skills --skill mixin-pattern安装后在 Claude Code 中输入/mixin-pattern确认已加载。
详细介绍
Mixin 模式
mixin 是一种对象,我们可以用它为另一个对象或类添加可复用的功能,而无需使用继承。我们不能单独使用 mixin:它唯一的用途就是在不使用继承的情况下为对象或类_添加功能_。
假设我们的应用需要创建多只狗。不过,我们创建的基本狗对象除了 name 属性之外没有任何属性。
何时使用
- 当你需要为多个类添加可复用的功能,但不想创建继承链时,可以使用它
- 当你想组合来自多个来源的行为时,它会很有用
何时不使用
- 当使用钩子(React)或组合式函数(Vue)进行组合能达到相同效果且更易追踪时
- 当存在原型污染风险时——mixin 会修改共享原型,可能导致命名冲突
- 当要添加的功能足够简单、只需使用工具函数或导入模块即可时
说明
- 使用
Object.assign()将 mixin 属性添加到类原型 - 谨慎处理原型污染——修改原型可能导致意外行为
- 在 React 中,优先使用 Hooks,而非 mixin(React 团队不建议使用 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。由于我们刚刚将 dogFunctionality mixin 添加到了 Dog 的原型中,Daisy 应该能够行走、摇尾巴和玩耍!
const pet1 = new Dog("Daisy");
pet1.name; // Daisy
pet1.bark(); // Woof!
pet1.play(); // Playing!
太好了!使用 mixin 可以轻松地为类或对象添加自定义功能,而无需使用继承。
虽然我们可以在不使用继承的情况下通过 mixin 添加功能,但 mixin 本身也可以使用继承!
大多数哺乳动物也会行走和睡觉。狗是哺乳动物,因此应该能够行走和睡觉!
我们来创建一个 animalFunctionality mixin,添加 walk 和 sleep 属性。
const animalFunctionality = {
walk: () => console.log("Walking!"),
sleep: () => console.log("Sleeping!"),
};
我们可以使用 Object.assign 将这些属性添加到 dogFunctionality 原型中。在本例中,目标对象是 dogFunctionality。
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 方法了。
浏览器环境中的 Window 接口就是一个现实世界中的 mixin 示例。Window 对象通过 WindowOrWorkerGlobalScope 和 WindowEventHandlers mixin 实现了许多属性,因此我们能够访问 setTimeout 和 setInterval、indexedDB 以及 isSecureContext 等属性。
由于它是一个 mixin,只用于为对象_添加功能_,因此你无法创建 WindowOrWorkerGlobalScope 类型的对象。
React(ES6 之前)
在引入 ES6 类之前,人们常用 Mixins 为 React 组件添加功能。React 团队不建议使用 Mixins,因为它很容易给组件增加不必要的复杂性,使其难以维护和复用。React 团队建议改用高阶组件,而如今,高阶组件通常可以用 Hooks 替代。
Mixins 通过将功能注入对象的原型,让我们无需继承就能轻松为对象添加功能。修改对象的原型被视为不良实践,因为这可能导致原型污染,并使函数的来源变得难以确定。
来源
参考资料
- Functional Mixins - Eric Elliott
- Mixins - JavaScript Info