Mixin 的特性一直廣泛存在于各種面向?qū)ο笳Z言屈雄,它的實(shí)質(zhì)上是利用語言特性(比如 Ruby 的 include 語法、Python 的多重繼承跪另、?ES6 的 Decorator)來更簡(jiǎn)潔地實(shí)現(xiàn)組合模式益眉。
封裝一個(gè) Mixin 方法
// 用賦值的方式將 mixins 對(duì)象里的方法都掛載到原對(duì)象上,就實(shí)現(xiàn)了對(duì)對(duì)象的混入质欲。
const mixin = function(obj, mixins) {
const newObj = obj;
newObj.prototype = Object.create(obj.prototype);
for (let prop in mixins) {
if (mixins.hasOwnProperty(prop)) {
newObj.prototype[prop] = mixins[prop];
}
}
return newObj;
}
const BigMixin = {
fly: () => {
console.log('I can fly');
}
};
const Big = function() {
console.log('new big');
};
const FlyBig = mixin(Big, BigMixin);
const flyBig = new FlyBig(); // 'new big'
flyBig.fly(); // 'I can fly'
underscore 中的 extend 或 lodash 中的 assign 方法,或者說在 ES6 中一個(gè)方法 Object.assign() 可以簡(jiǎn)單實(shí)現(xiàn)mixin
React Mixins 混入使用
import React from 'react';
import PureRenderMixin from 'react-addons-pure-render-mixin';
React.createClass({
mixins: [PureRenderMixin],
render() {
return <div>foo</div>;
}
});
以官方封裝的 PureRenderMixin 來舉例糠馆,在 createClass 對(duì)象參數(shù)中傳入一個(gè) mixins 的數(shù)組嘶伟,里面封裝了我們所需要的模塊。mixins 也可以增加多個(gè)重用模塊又碌,使用多個(gè)模塊九昧,方法之間的有重合會(huì)對(duì)普通方法和生命周期方法有所區(qū)分绊袋。
Vue.js Mixins 混入使用
// 定義一個(gè)混入對(duì)象
var myMixin = {
created: function () {
this.hello()
},
methods: {
hello: function () {
console.log('hello from mixin!')
}
}
}
// 定義一個(gè)使用混入對(duì)象的組件
var Component = Vue.extend({
mixins: [myMixin]
})
var component = new Component() // => "hello from mixin!"
更多見 vue.js - 混入
在 ECMAScript6 class 語法上 修飾器(Decorator)實(shí)現(xiàn)混入
將mixin寫成一個(gè)修飾器,放在通用腳本 mixins.js
export function mixins(...list) {
return function (target) {
Object.assign(target.prototype, ...list);
};
}
然后铸鹰,就可以使用上面這個(gè)修飾器癌别,為類“混入”各種方法。
import { mixins } from './mixins';
const Foo = {
foo() { console.log('foo') }
};
@mixins(Foo)
class MyClass {}
let obj = new MyClass();
obj.foo() // "foo"
es6的修飾器(Decorator)語法
修飾器(Decorator)是一個(gè)函數(shù)(上面為mixins函數(shù))蹋笼,用來修改類的行為展姐。修飾器對(duì)類的行為的改變,是代碼編譯時(shí)發(fā)生的剖毯,而不是在運(yùn)行時(shí)圾笨。基本上逊谋,修飾器的行為就是下面這樣擂达。
@decorator
class A {}
// 等同于
class A {}
A = decorator(A) || A;
修飾器函數(shù)的第一個(gè)參數(shù),就是所要修飾的目標(biāo)類涣狗。
function testable(target) {
// ...
}
如果覺得一個(gè)參數(shù)不夠用谍婉,可以在修飾器外面再封裝一層函數(shù)。
function testable(isTestable) {
return function(target) {
target.isTestable = isTestable;
}
}
@testable(true)
class MyTestableClass {}
MyTestableClass.isTestable // true
@testable(false)
class MyClass {}
MyClass.isTestable // false
上面的例子是為類添加一個(gè)靜態(tài)屬性镀钓,如果想添加實(shí)例屬性穗熬,可以通過目標(biāo)類的 prototype
對(duì)象操作。
修飾器不僅可以修飾類丁溅,還可以修飾類的屬性唤蔗。
class Person {
@readonly
name() { return `${this.first} ${this.last}` }
}
上面代碼中,修飾器readonly用來修飾“類”的name方法窟赏。
此時(shí)妓柜,修飾器函數(shù)一共可以接受三個(gè)參數(shù),第一個(gè)參數(shù)是所要修飾的目標(biāo)對(duì)象涯穷,第二個(gè)參數(shù)是所要修飾的屬性名棍掐,第三個(gè)參數(shù)是該屬性的描述對(duì)象。
function readonly(target, name, descriptor){
// descriptor對(duì)象原來的值如下
// {
// value: specifiedFunction,
// enumerable: false,
// configurable: true,
// writable: true
// };
descriptor.writable = false;
return descriptor;
}
readonly(Person.prototype, 'name', descriptor);
// 類似于
Object.defineProperty(Person.prototype, 'name', descriptor);