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)

同样的,这个装饰器可以用于任何类,并且很久传入的参数不同,类中被装饰的属性值不同,通过这种技术,我们可以将一些通用的功能抽象到一个装饰器函数中,对需要使用该功能类使用装饰器注入功能,从而避免了反复的在不同的类中实现相同的代码,从而然代码更加简洁可读。

posted @ 2020-10-15 11:17  没有想象力  阅读(364)  评论(0编辑  收藏  举报