zoukankan      html  css  js  c++  java
  • Mixin模式

    Mixin是JavaScript中用的最普遍的模式,几乎所有流行类库都会有Mixin的实现。

    Mixin是掺合,混合,糅合的意思,即可以就任意一个对象的全部或部分属性拷贝到另一个对象上。

    从提供的接口来看,有的是对对象的操作,有的是对类的操作。对类的操作又称为掺元类(Mixin classes)

    一、掺合对象 (Mixin object)

    先看最简单的mixin实现

    1 function mixin(dest, src) {
    2     for (var key in src) {
    3         dest[key] = src[key]
    4     }
    5 }

    使用下

    1 var person = {name: 'John', age: 29}
    2 var obj = {}
    3 mixin(obj, person)
    4 console.log(obj) // {name: 'John', age: 29}

    可看到,已经将person的所有属性拷贝到obj上了。 有个缺点,如果obj上已经有了name: 'Jack',那么会被person覆盖。因此需要加个判断,如果dest上已经存在了,就不拷贝。

     1 function mixin(dest, src) {
     2     for (var key in src) {
     3         if (!dest[key]) {
     4             dest[key] = src[key]
     5         }
     6     }
     7 }
     8 var person = {name: 'John', age: 29}
     9 var obj = {name: 'Jack'}
    10 mixin(obj, person)
    11 console.log(obj) // Object { name="Jack", age=29}
    当然,你可以提供更强大,灵活的Mixin,比如可以将任意多个对象掺合到目标对象
     1 function mixin(dest /*, Any number of objects */) {
     2     var sources = Array.prototype.slice.call(arguments, 1)
     3     for (var i=0; i<sources.length; i++) {
     4         var src = sources[i]
     5         for (var key in src) {
     6             if (!dest[key]) {
     7                 dest[key] = src[key]
     8             }
     9         }  
    10     }
    11 }
    12 var person = {name: 'John', age: 29, toString: function(){return 'aa'}}
    13 var permission = {open: 1}
    14 var obj = {name: 'Jack'}
    15 mixin(obj, person, permission)
    16 console.log(obj) // Object { name="Jack", age=29, open=1}

    以下类库都是对对象的掺合

    • jQuery的$.extend 操作对象,将其它对象的属性方法拷贝到目标对象。
    • RequireJS的私有的mixin 操作对象,将其它对象的属性方法拷贝到目标对象。
    • ExtJS的Ext.apply 也是操作对象,它还提供了一个defaults参数。
    • Underscore.js 的 _.extend,把第二个参数起的所有对象都拷贝到第一个参数

    二、掺和类(Mixin Classes)

    有的翻译过来叫做掺元类,它是一种不需要用到严格的继承就可以复用代码的一种技术。如果多个类想用到某个类的某个方法,可以通过扩充这些类的原型已达到共享该方法。比如先创建一个包含各种通用方法的类,然后让其它类扩充于它。这个包含通用方法的类就叫掺元类。多数时候它不会直接实例化或调用,而是作为其它类的模板用于扩充。

    先看最简单的实现

     1 // 工具方法,实现mixin
     2 function augment(destClass, srcClass) {
     3     var destProto = destClass.prototype
     4     var srcProto  = srcClass.prototype
     5     for (var method in srcProto) {
     6         if (!destProto[method]) {
     7             destProto[method] = srcProto[method]
     8         }
     9     }
    10 }
    11  
    12 function Person() {} // 具有两个方法的类,用于mixin
    13 Person.prototype.getName = function() {}
    14 Person.prototype.getAge  = function() {}
    15  
    16 function Student() {} // 没有任何方法的类
    17  
    18 augment(Student, Person) // 调用,拷贝
    19  
    20 var s1 = new Student()
    21 console.log(s1) // Student { getName=function(), getAge=function()}

    工具函数augment接受两个参数,都是函数类型(类),第一个类会从第二个类的原型上继承其方法。即使用Person类扩充了Student类。

    我们知道,某些语言如C++/Python允许子类继承多个父类,但在JavaScript中是不允许的,因为一个构造器只有一个原型对象,不过这可以通过多个掺元类的方式实现扩充,这实际是一种变相多继承的实现。和mixin方法类似,修改下augment方法。

     1 function augment(destClass, /*, Any number of classes */) {
     2     var classes = Array.prototype.slice.call(arguments, 1)
     3     for (var i=0; i<classes.length; i++) {
     4         var srcClass = classes[i]
     5         var srcProto  = srcClass.prototype
     6         var destProto = destClass.prototype    
     7         for (var method in srcProto) {
     8             if (!destProto[method]) {
     9                 destProto[method] = srcProto[method]
    10             }
    11         }      
    12     }
    13 }

    这样就实现了多继承。

    有时不想继承所有的方法,指向拷贝指定的方法,增加一个参数methods

     1 1
     2 2
     3 3
     4 4
     5 5
     6 6
     7 7
     8 8
     9 9
    10 10
    11 11
    12 12
    13 13
    14 14
    15 15
    16 16
    17 17
    18 18
    19 19
    20 20
    21 21
    22 function augment(destClass, srcClass, methods) {
    23     var srcProto  = srcClass.prototype
    24     var destProto = destClass.prototype    
    25     for (var i=0; i<methods.length; i++) {
    26         var method = methods[i]
    27         if (!destProto[method]) {
    28             destProto[method] = srcProto[method]
    29         }
    30     }
    31 }
    32 function Person() {}
    33 Person.prototype.getName = function() {}
    34 Person.prototype.setName  = function() {}
    35 Person.prototype.getAge  = function() {}
    36 Person.prototype.setAge  = function() {}
    37  
    38 function Student() {}
    39  
    40 augment(Student, Person, ['getName', 'setName'])
    41 var s1 = new Student()
    42 console.log(s1) // Student { getName=function(), setName=function()}

    Backbone是广泛使用掺元类的库

    首先,Backbone库自身就采用Mixin classes方式组织,如Backbone.Events是最底层的掺元类,它的方法(on/off/trigger...)都被Backbone.Model/Backbone.Collection/Backbone.View等继承。代码片段如下

    1 _.extend(Model.prototype, Events, {
    2     ...
    3 })
    4 _.extend(Collection.prototype, Events, {
    5     ...
    6 })
    7 _.extend(View.prototype, Events, {
    8     ...
    9 })

    它这里使用_.extend来扩充Model,Collection,View的原型,把Events的方法都拷贝到原型。即Event就是一个掺元类(虽然被实现为一个对象)

    其次,我们使用Backbone开发时,你写的模型会用Backbone.Model去扩充,视图会用Backbone.View去扩充。如

     1 var MyModel = Backbone.Model.extend({
     2     instanceProp: xx
     3 },{
     4     classProp: yy
     5 })
     6  
     7 var MyView = Backbone.Model.extend({
     8     instanceProp: xx
     9 },{
    10     classProp: yy
    11 })

    这时,Backbone.Model/Backbone.View等就是掺元类了。当然,你还可以把underscore当做掺元对象,因为Backbone的很多类都继承了_.extend方法,如Backbone.Events/Backbone.Model等。

  • 相关阅读:
    Node 修改默认镜像源
    Mac下apache虚拟主机配置
    Grep命令出现 Binary file (standard input) matches
    idea取出方法参数提示
    Java8 Optional用法
    Codeforces Round #638 (Div. 2)
    Codeforces Round #637 (Div. 2)
    Codeforces Round #636 (Div. 3)
    Tree
    Educational Codeforces Round 85
  • 原文地址:https://www.cnblogs.com/linghu-java/p/9243857.html
Copyright © 2011-2022 走看看