JS中的Minix和装饰器实现
Minix和装饰器
Mixin
Mixin 是面向对象语言中一种常用的设计模式,通常称之为组合,在js中通常使用一个中间的继承对象实现。
A对象有功能a,C对象需要继承自A对象并添加x功能,
解决办法1:我们可以使用C继承自A,再在C上定义x功能。但是如果D类型需要继承自B类型,同样也需要该x功能,我们就只能重写一遍功能。
解决方法2:将x功能进行封装到一个Mixin函数中,Mixin函数接受类型A作为参数,然后在这个Mixin函数中创建一个新的类型Temp,Temp继承A并添加该x功能。这样C只需要继承这个新建的Temp就可以同时拥有A和x功能了。这种方式将类型A作为参数。同样的,如果B类型的子类想添加这个x功能,只需要将B作为参数传入这个Mixin函数,然后继承这个函数的返回值。
代码简单实现
class A { // 普通类型A,他只能打印它的x属性,无法打印y和z属性
constructor(x, y, z){
this.x = x
this.y = y
this.z = z
this.showX = () => {console.log(this.x)}
}
}
function MixinShowY (SupCls){ // 可以打印y属性功能的函数,接受一个类型为参数
class Tempcls extends SupCls{
constructor(...args){
super(...args)
this.showY = () => {console.log(this.y)}
}
}
return Tempcls // 返回值为一个类
}
class C extends MixinShowY(A){}
// MixinShowB(A) 的返回值为一个类,可以被C继承,继承后同时拥有MixinShowB和A的功能
在MixinShowB函数中,接受一个类作为参数来创建一个新的类型并添加功能showB,任何一个类型都可以被传入,然后返回新的类型并添加功能。便实现了这个函数功能的复用。
Mixin叠加
上面中z属性无法被打印,我们可以继续定义Minix函数添加这个功能
function MixinShowZ (SupCls){ // 增加可以打印y属性功能的函数,接受一个类型为参数
class Tempcls extends SupCls{
constructor(...args){
super(...args)
this.showZ = () => {console.log(this.Z)}
}
}
return Tempcls // 返回值为一个类
}
class D extends MixinShowY(C){}
// 或者
class D extends MixinShowZ(MixinShowZ(A)){}
obj = new D(1,2,3)
obj.showX()
obj.showY()
上面的两种写法效果相同,第二种在A的基础上连续使用两次包装Mixin。获得了ShowY和ShowZ两个功能;
此时如果想定义一个类B,他需要在A的基础上增加ShowZ方法,而不需要ShowY,只需要
class B extends MixinShowZ(A){}
需要哪种功能,就使用该功能对应的Mixin函数混入即可。
装饰器
装饰器语法在ES7中的定义,它是一个语法糖,依赖于ES5的Object.defineProperty方法,由于是ES7中的语法,需要使用babel将代码转译为ES5以前的,可以在babel官网在线转译少量代码,也可以在本机上搭建离线的转译环境。可以查看博主的JS模块化管理及babel离线环境搭建
装饰器,顾名思义就是可以为一个对象添加某种功能的包装器,在目前JS中只有使用class定义的类才能使用装饰器
继续上面的示例,我们为A类添加一个ShowY的功能
function addShowY(cls){ // 装饰器函数
class Temp extends cls{
constructor(...args){
super(...args)
this.showY = () => {console.log(this.y)}
}
}
return Temp
}
@addShowY // 等价于 A = addshowY(A),装饰后A标识符已经指向了addShowY的返回值,一个新的类
class A {
constructor(x, y, z){
this.x = x
this.y = y
this.z = z
this.showX = () => {console.log(this.x)}
}
}
a = new A(1,2,3)
a.showY() // 调用成功
任何需要这个功能类,都可以使用这个装饰器进行装饰
@addShowY
class B{
constructor(a, b, c, d){
this.a = a
this.b = b
this.c = c
this.d = d
}
}
带参装饰器
我们还可以使用带参数的装饰器,这需要对象装饰器函数进行些许的改变,想象一下这个带参装饰器函数未来的使用方式应该是下面这样。
@addname("root") // 为类添加一个family属性,这个family可以自己指定。等价于C = addname(root)(C)
class C(){} // 这样C 类将会拥有一个指定的famliy属性
从用法乐意看出,这个addname函数将会被调用,调用后的返回值是一个接受类的函数,然后返回值为一个类,即
function addname(family){
function func(cls){ //
class Temp extends cls{
constructor(...args){
super(...args)
this.family = family
}
}
return Temp // func()函数调用,返回新的类
}
return func // addname调用后返回一个函数,这个函数执行 func(cls)
}
@addname("root")
class C(){
constructor(x,y){
this.x = x
this.y = y
}
}
let obj = new C(1,2)
console.log(obj.family)
同样的,这个装饰器可以用于任何类,并且很久传入的参数不同,类中被装饰的属性值不同,通过这种技术,我们可以将一些通用的功能抽象到一个装饰器函数中,对需要使用该功能类使用装饰器注入功能,从而避免了反复的在不同的类中实现相同的代码,从而然代码更加简洁可读。