zoukankan      html  css  js  c++  java
  • 掺合模式(Mixin)

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

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

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

    一、掺合对象 (Mixin object)

    先看最简单的mixin实现

    function mixin(dest, src) {
    	for (var key in src) {
    		dest[key] = src[key]
    	}
    }
    

    使用下

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

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

    function mixin(dest, src) {
    	for (var key in src) {
    		if (!dest[key]) {
    			dest[key] = src[key]
    		}
    	}
    }
    var person = {name: 'John', age: 29}
    var obj = {name: 'Jack'}
    mixin(obj, person)
    console.log(obj) // Object { name="Jack", age=29}
    

    当然,你可以提供更强大,灵活的Mixin,比如可以将任意多个对象掺合到目标对象

    function mixin(dest /*, Any number of objects */) {
    	var sources = Array.prototype.slice.call(arguments, 1)
    	for (var i=0; i<sources.length; i++) {
    		var src = sources[i]
    		for (var key in src) {
    			if (!dest[key]) {
    				dest[key] = src[key]
    			}
    		}	
    	}
    }
    var person = {name: 'John', age: 29, toString: function(){return 'aa'}}
    var permission = {open: 1}
    var obj = {name: 'Jack'}
    mixin(obj, person, permission)
    console.log(obj) // Object { name="Jack", age=29, open=1}
    

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

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

    二、掺和类(Mixin Classes)

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

    先看最简单的实现

    // 工具方法,实现mixin
    function augment(destClass, srcClass) {
    	var destProto = destClass.prototype
    	var srcProto  = srcClass.prototype
    	for (var method in srcProto) {
    		if (!destProto[method]) {
    			destProto[method] = srcProto[method]
    		}
    	}
    }
    
    function Person() {} // 具有两个方法的类,用于mixin
    Person.prototype.getName = function() {}
    Person.prototype.getAge  = function() {}
    
    function Student() {} // 没有任何方法的类
    
    augment(Student, Person) // 调用,拷贝
    
    var s1 = new Student()
    console.log(s1) // Student { getName=function(), getAge=function()}
    

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

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

    function augment(destClass, /*, Any number of classes */) {
    	var classes = Array.prototype.slice.call(arguments, 1)
    	for (var i=0; i<classes.length; i++) {
    		var srcClass = classes[i]
    		var srcProto  = srcClass.prototype
    		var destProto = destClass.prototype		
    		for (var method in srcProto) {
    			if (!destProto[method]) {
    				destProto[method] = srcProto[method]
    			}
    		}		
    	}
    }
    

    这样就实现了多继承。

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

    function augment(destClass, srcClass, methods) {
    	var srcProto  = srcClass.prototype
    	var destProto = destClass.prototype		
    	for (var i=0; i<methods.length; i++) {
    		var method = methods[i]
    		if (!destProto[method]) {
    			destProto[method] = srcProto[method]
    		}
    	}
    }
    function Person() {}
    Person.prototype.getName = function() {}
    Person.prototype.setName  = function() {}
    Person.prototype.getAge  = function() {}
    Person.prototype.setAge  = function() {}
    
    function Student() {}
    
    augment(Student, Person, ['getName', 'setName'])
    var s1 = new Student()
    console.log(s1) // Student { getName=function(), setName=function()}
    

    Backbone是广泛使用掺元类的库

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

    _.extend(Model.prototype, Events, {
    	...
    })
    _.extend(Collection.prototype, Events, {
    	...
    })
    _.extend(View.prototype, Events, {
    	...
    })
    

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

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

    var MyModel = Backbone.Model.extend({
    	instanceProp: xx
    },{
    	classProp: yy
    })
    
    var MyView = Backbone.Model.extend({
    	instanceProp: xx
    },{
    	classProp: yy
    })
    

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

  • 相关阅读:
    Java实现 蓝桥杯VIP 算法训练 传球游戏
    Java实现 蓝桥杯VIP 算法训练 Hanoi问题
    Java实现 蓝桥杯VIP 算法训练 蜜蜂飞舞
    Java实现 蓝桥杯VIP 算法训练 奇偶判断
    Java实现 蓝桥杯VIP 算法训练 传球游戏
    Java实现 蓝桥杯VIP 算法训练 Hanoi问题
    Java实现 蓝桥杯VIP 算法训练 Hanoi问题
    Java实现 蓝桥杯VIP 算法训练 蜜蜂飞舞
    Java实现 蓝桥杯VIP 算法训练 蜜蜂飞舞
    Qt: 访问容器(三种方法,加上for循环就四种了)good
  • 原文地址:https://www.cnblogs.com/snandy/p/3086663.html
Copyright © 2011-2022 走看看