混入(Mixin)是一个对象,我们可以使用它来向另一个对象或类添加可重用的功能,而无需使用继承。我们无法独立使用混入,它的唯一目的是向对象或类添加功能,而不使用继承。
比如说,在我们的应用程序中,我们需要创建多个狗。然而,我们创建的狗的基本类型没有任何属性,只有一个名字属性。
class Dog {constructor(name) {this.name = name;}
}
狗不仅仅应该有一个名字,它还应该能够叫、摇尾巴和玩耍!我们不必直接在Dog类中添加这些行为,而是可以创建一个混入(mixin)来为我们提供叫、摇尾巴和玩耍的属性。
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!
Mixins 让我们可以轻松地在不使用继承的情况下向类或对象添加自定义功能。
虽然我们可以在不继承的情况下使用 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 混合中实现其许多属性,这使我们能够访问 setTimeout
和 setInterval
、indexedDB
和 isSecureContext
等属性。
由于它是一个 mixin,因此仅用于向对象添加功能,因此您将无法创建 WindowOrWorkerGlobalScope
类型的对象。
window.indexedDB.open("toDoList");window.addEventListener("beforeunload", event => {event.preventDefault();event.returnValue = "";
});window.onbeforeunload = function() {console.log("Unloading!");
};console.log("From WindowEventHandlers mixin: onbeforeunload",window.onbeforeunload
);console.log("From WindowOrWorkerGlobalScope mixin: isSecureContext",window.isSecureContext
);console.log("WindowEventHandlers itself is undefined",window.WindowEventHandlers
);console.log("WindowOrWorkerGlobalScope itself is undefined",window.WindowOrWorkerGlobalScope
);
React(ES6 之前)
在引入 ES6 类之前,Mixin 通常用于为 React 组件添加功能。React 团队不鼓励使用 mixin,因为它很容易给组件增加不必要的复杂性,使其难以维护和重用。React 团队鼓励使用高阶组件,这些组件现在通常可以被 Hooks 取代。
Mixins 允许我们通过将功能注入对象的原型来轻松地向对象添加功能,而无需继承。但修改对象的原型被视为不好的做法,因为它可能导致原型污染和我们功能来源的一定程度的不确定性。