mixins设计模式以及在js中的使用

4,495 阅读4分钟

Mixins

在《Javascript设计模式》一书中介绍了一种设计模式-Mixin 模式。在一些著名的组件库中,Mixins也是十分常见的关键字。但,菜狗的我在日常的开发中好像没怎么用过mixin。 看来我和大佬之间的第一个差距就是mixins的使用了(笑) 那到底什么是mixins ,以及怎么使用呢?

初识Mixins

在《Learning JavaScript Design Patterns》一书中,Mixins模式定义如下:

In traditional programming languages such as C++ and Lisp, Mixins are classes which offer functionality that can be easily inherited by a sub-class or group of sub-classes for the purpose of function re-use.

在js中, 我们能通过Mixins 扩展已知类。js中所有的对象都有原型,原型可以继承其他对象的原型从而得到更多的属性。通过Mixins可以使对象以最小的复杂性继承其他对象的功能,而这个和JS中对象的原型完美契合,也许这就为什么mixins这种设计模式在js被广泛应用的原因。

我们可以mixin对象或者mixin构造函数。【1】

mixins 对象

举个例子, 我们定义一个mixins对象包含一系列的功能

var myMixins = {
 
  moveUp: function(){
    console.log( "move up" );
  },
 
  moveDown: function(){
    console.log( "move down" );
  },
 
  stop: function(){
    console.log( "stop! in the name of love!" );
  }
 
};

然后我们从其他的构造器函数中继承原型,例如Underscore.js中的_.extend()方法。

function CarAnimator() {
    this.moveLeft = function() {
        console.log('move left');
    }
}

_.extend(CarAnimator.prototype, myMixins);

var myAnimator = new CarAnimator();
myAnimator.moveLeft(); // 输出: move left

mixins构造函数

对于某个构造函数中的特性方法也是可以混入的。

var Car = function ( settings ) {
 
    this.model = settings.model || "no model provided";
    this.color = settings.color || "no colour provided";
 
};
var Mixin = function () {};
 
Mixin.prototype = {
 
    driveForward: function () {
        console.log( "drive forward" );
    },
 
    driveBackward: function () {
        console.log( "drive backward" );
    },
 
    driveSideways: function () {
        console.log( "drive sideways" );
    }
 
};
// 定义一个继承方法,从已知对象继承某个特定的方法

function augment( receivingClass, givingClass ) {
 
    // only provide certain methods
    if ( arguments[2] ) {
        for ( var i = 2, len = arguments.length; i < len; i++ ) {
            receivingClass.prototype[arguments[i]] = givingClass.prototype[arguments[i]];
        }
    }
    // provide all methods
    else {
        for ( var methodName in givingClass.prototype ) {
 
            // check to make sure the receiving class doesn't
            // have a method of the same name as the one currently
            // being processed
            if ( !Object.hasOwnProperty.call(receivingClass.prototype, methodName) ) {
                receivingClass.prototype[methodName] = givingClass.prototype[methodName];
            }
 
            // Alternatively (check prototype chain as well):
            // if ( !receivingClass.prototype[methodName] ) {
            // receivingClass.prototype[methodName] = givingClass.prototype[methodName];
            // }
        }
    }
}

// 将driveForward 和 driveBackward 混入 Car
augment( Car, Mixin, "driveForward", "driveBackward" );
 
// 创建一个实例
var myCar = new Car({
    model: "Ford Escort",
    color: "blue"
});

myCar.driveForward();
myCar.driveBackward();
 
// Outputs:
// drive forward
// drive backward

Mixins 的优缺点

Mixins 设计模式能在保证最大复用的情况下 减少功能的重复定义。当需要跨对象共享属性的的时候我们可以考虑使用Mixin,mixin可以维护共享,还能避免重复的定义。

但同时,在大型项目中,将功能注入原型会造成原型的污染从而导致功能的不确定性。如果开发者在文档中能够明确的写出,就可能减少这种混乱,但在需要mixin的时候,还是需要认真的考虑一下这个缺点,是便捷性更重要还是安全性更重要。

Mixin的实际应用

在高阶组件中,我们可能希望将多个类进行混合,我们有很多的设计模式,但菜狗如我,一定不会考虑mixin 因为难。

mixin为对象提供存在在其他属性的附加组件,但这些单独的属性和这个对象之间并不是继承关系。实现的时候回传入目标对象和源对象,将目标附加到源对象中并返回一个新对象。

通俗的说,mixin可以像工厂模式一样,返回一个新子类对象,但在过程中并没有定义任何子类。Mixin允许我们修改创建好的类,并且应用于一个现有的超类,然后生成一个新的子类。

如果在es6中使用:

  1. 使用Object.assign
let walk = {
    position() {
        console.log(`walk to ${direction} in ${speed}`);
    }
}

let Pedestrain = function(speed, direction) {
    this.speed = speed;
    this.direction = direction;
}

let pedestrain1 = new Pedestrain('20 m/s''south');

pedestrain1 = Object.assign(pedestrain1, walk);
pedestrain1.position(); // output : walk to south in 20m/s
  1. 使用Class关键字
lass  Pedestrain {
    constructor(name) {
        this.name = name;
    }
}
let walk = {
    position(speed, direction) {
        console.log(`walk to ${direction} in ${speed}`);
    }
}
Object.assign(Pedestrain.prototype , walk);
let pedestrain1 = new Pedestrain('xiaoming');
pedestrain1.position('20 m/s''south');

例子中我们可以看到使用object.assign可以灵活的拓展对象。而且mixin构造出的类层次结构是水平的,可以想象成为姐妹类,而不是增长继承链的父子类。

在Vue中使用mixin

我们在定义组件类的时候也可以使用mixin的方式。

假设我们有个组件

const cardMixins = {
    data() {
        return{
            title:'title'
        }
    }
}

export default cardMixins

在另一个组件中我们引入这个mixin

import cardMixins from 'card'
export default {
    name: 'smallCard',
    mixins: [cardMixins],
    created() {
        console.log(this.$data)
    }
}

我们通过mixins 我们可以使用cardMixins 中的data,计算属性等,省去了重复的定义。

总结

mixin算是比较高阶的用法, 在大型组件库或者框架中都能看到mixin和工厂模式的混用, 关于mixin的使用还是要多看多用,而且尽可能提供详细的文档。

如果文章中有任何不当的地方欢迎在评论区指正。