JS设想形式之Mixin(混入)形式

观点

Mixin形式就是一些供应能够被一个或许一组子类简朴继续功用的类,意在重用其功用。在面向对象的语言中,我们会经由过程接口继续的体式格局来完成功用的复用。然则在javascript中,我们没办法经由过程接口继续的体式格局,然则我们能够经由过程javascript特有的原型链属性,将功用援用复制到原型链上,到达功用的注入。

示例

下面经由过程一个简朴的例子来演示这个形式

var Car = function(settings) {
  this.model = settings.model || "no model provided"
  this.color = settings.color || "no color 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 Merge(recClass, giveClass) {
  if(arguments.length > 2) {
    for(var i = 2, lenth = arguments.length; i < lenth ; ++ i) {
      var methodName = arguments[i]
      recClass.prototype[methodName] = giveClass.prototype[methodName]
    }
  }else {
    for(var methodName in giveClass.prototype) {
      if(!recClass.prototype[methodName]) {
        recClass.prototype[methodName] = giveClass.prototype[methodName]
      }
    }
  }
}

Merge(Car, Mixin, "driveForward", "driveBackward")

var myCar = new Car({
  model: "BMW",
  color: "blue"
})

myCar.driveForward()    //drive forward
myCar.driveBackward()   //drive backward

//不指定特定要领名的时刻,将后者一切的要领都添加到前者里
Merge(Car, Mixin)

var mySportsCar = new Car({
  model: "Porsche",
  color: "red"
})

mySportsCar.driveForward()  //drive forward

优瑕玷

长处
有助于削减体系中的反复功用及增添函数复用。当一个应用程序能够须要在种种对象实例中同享行为时,我们能够经由过程在Mixin中保持这类同享功用并专注于仅完成体系中真正差别的功用,来轻松防止任何反复。

瑕玷
有些人以为将功用注入对象原型中会致使原型污染和函数劈头方面的不确定性。

Vue混入功用研讨

vue中关于混入的代码目次/src/core/global-api/mixin.js
能够看到vue源码中是经由过程mergeOptions来兼并设置到options上

export function initMixin (Vue: GlobalAPI) {
  Vue.mixin = function (mixin: Object) {
    this.options = mergeOptions(this.options, mixin)
    return this
  }
}

下面我们来看兼并设置的相干代码/src/core/instance/init.js
当实行new Vue的时刻options._isComponent为false,会走else的分支

export function initMixin (Vue: Class<Component>) {
  Vue.prototype._init = function (options?: Object) {
    const vm: Component = this
    // a uid
    vm._uid = uid++

    let startTag, endTag
    /* istanbul ignore if */
    if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
      startTag = `vue-perf-start:${vm._uid}`
      endTag = `vue-perf-end:${vm._uid}`
      mark(startTag)
    }

    // a flag to avoid this being observed
    vm._isVue = true
    // merge options
    if (options && options._isComponent) {
      // optimize internal component instantiation
      // since dynamic options merging is pretty slow, and none of the
      // internal component options needs special treatment.
      initInternalComponent(vm, options)
    } else {
      vm.$options = mergeOptions(  //兼并设置
        resolveConstructorOptions(vm.constructor),
        options || {},
        vm
      )
    }
    /* istanbul ignore else */
    if (process.env.NODE_ENV !== 'production') {
      initProxy(vm)
    } else {
      vm._renderProxy = vm
    }
    // expose real self
    vm._self = vm
    initLifecycle(vm)
    initEvents(vm)
    initRender(vm)
    callHook(vm, 'beforeCreate')
    initInjections(vm) // resolve injections before data/props
    initState(vm)
    initProvide(vm) // resolve provide after data/props
    callHook(vm, 'created')

    /* istanbul ignore if */
    if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
      vm._name = formatComponentName(vm, false)
      mark(endTag)
      measure(`vue ${vm._name} init`, startTag, endTag)
    }

    if (vm.$options.el) {
      vm.$mount(vm.$options.el)
    }
  }
}

下面来看mergeOptions的完成src/core/util/options.js
mergeField将this.options(parent)和须要混入的对象mixin(child)兼并在this.options上面

/**
 * Merge two option objects into a new one.
 * Core utility used in both instantiation and inheritance.
 */
export function mergeOptions (
  parent: Object,
  child: Object,
  vm?: Component
): Object {
  if (process.env.NODE_ENV !== 'production') {
    checkComponents(child)
  }

  if (typeof child === 'function') {
    child = child.options
  }

  normalizeProps(child, vm)
  normalizeInject(child, vm)
  normalizeDirectives(child)
  const extendsFrom = child.extends
  if (extendsFrom) {
    parent = mergeOptions(parent, extendsFrom, vm)
  }
  if (child.mixins) {
    for (let i = 0, l = child.mixins.length; i < l; i++) {
      parent = mergeOptions(parent, child.mixins[i], vm)
    }
  }
  const options = {}
  let key
  for (key in parent) {
    mergeField(key)
  }
  for (key in child) {
    if (!hasOwn(parent, key)) {
      mergeField(key)
    }
  }
  function mergeField (key) {
    const strat = strats[key] || defaultStrat
    options[key] = strat(parent[key], child[key], vm, key)
  }
  return options
}

参考

《JavaScript设想形式》

JS设想形式系列文章

JS设想形式之Obeserver(观察者)形式、Publish/Subscribe(宣布/定阅)形式
JS设想形式之Factory(工场)形式
JS设想形式之Singleton(单例)形式
JS设想形式之Facade(表面)形式
JS设想形式之Module(模块)形式、Revealing Module(展现模块)形式
JS设想形式之Mixin(混入)形式

    原文作者:大海怪
    原文地址: https://segmentfault.com/a/1190000019002233
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞