2011-09-21 71 views

回答

16

Mixin不能使用instanceof,但可以扩展do。 Mixin允许多重继承,但通过伪造它,而不是通过适当地链接原型。

我将展示一个Ext-JS示例,但该概念适用于任何提供mixin的类库,它们都只是将属性复制到对象而不是链接原型。

Ext.define('Ext.Window', { 
    extend: 'Ext.Panel', 
    requires: 'Ext.Tool', 
    mixins: { 
     draggable: 'Ext.util.Draggable' 
    } 
}); 

Ext.Window instanceof Ext.Panel //true 
Ext.Window instanceof Ext.util.Draggable // false 

Mixins是向对象添加一些功能而不使用继承的好方法。如果您必须继承某些功能才能获得某些功能,则无法使用两个中的功能。 Many people believe it's evil

当Ext-JS想要将Labelable功能添加到FieldSet和其他未输入字段的其他功能时,Ext-JS遇到了这个问题。 Field内部的Labelable行为无法受益,因为它们无法扩展Field,因为它也具有所有输入行为。

+1

这个答案非常特定于Ext.js,不适用于其他许多JavaScript库(jQuery和Underscore只是两个示例)。 – natlee75

+0

@ natlee75我的答案是说mixin不使用原型链,并且'instanceof'测试失败,Ext-JS是它们的一个实现(它们让你检查某个mixin是否以不同的方式)。据我所知,jquery或下划线不通过在原型链中设置它们来实现混合(它们只是复制属性)。你的评论意味着jQuery和下划线可以有混合使用instanceof正确工作?请详细说明/ –

+2

不,但是他们的“扩展”功能也不行。只有“类库”(如'Ext.define')使用“扩展”术语来进行原型继承。 – Bergi

0

编辑为清楚:这些有时同样的事情,有时不; mixin是用于在多个对象之间重用函数的模式的名称,并且扩展更多地是用于这样做的算法/方法。有些情况下它们是相同的(例如下划线_.extend)以及它们不同的情况(backbone.js的扩展)。

在它们不同的情况下,扩展通常会将原型链接到正在扩展的对象,而mixin会将方法列表复制到目标上。

+0

除了当你想要两个类给你的行为。除非你进入多重继承,否则它不能使用继承,它有自己的问题。 –

+0

我不确定这是如何相关的,因为扩展和mixin与继承等没有直接关系,他们只是模仿这种行为的方法。 – taxilian

+0

我只是解释他们没有相同行为的情况。 –

2

你可以使用扩展来创建mixins。

Mixin提供多重继承的所有好处,没有层次结构(JavaScript中的原型继承)。它们都允许您在多个对象上重用一个接口(或一组函数)。有了mixin,就不会遇到你可以在父子关系中遇到的“钻石问题”。

钻石问题发生在一个对象从两个对象继承相同的函数(甚至函数名)时。为什么?如果这两个对象中的一个对该函数进行修改,添加功能(即在Java中称为“超级”),则JavaScript不知道如何解释/组合这两种方法。 Mixins是避免这种过敏症的一种方法。他们定义了可以粘在任何地方的功能。 Mixins通常也不包含他们自己的数据。

因此,我可以,例如,在jQuery中使用$.extend()编写mixin。 var newmixin = $.extend({}, mixin1, mixin2)将结合两个接口,并将它们弄平(覆盖名称冲突)。

这里要考虑三件事情:

  1. 请问这两个接口的组合,具有“层次”,即。父母/子女关系。在JavaScript中,这意味着原型继承。
  2. 函数是复制还是引用?如果原始方法改变,继承的方法是否也会改变?
  3. 当两个同名的方法组合时会发生什么?这些冲突是如何处理的?
5

扩展方法在JavaScript库中很常见,并且通常是一种简单地允许消费代码将一个或多个对象的所有“属性”属性和方法添加到目标对象的方法。代码通常非常简单:遍历每个参数的所有自己的键,并将其中存储的值复制到第一个参数。

“Mixin”指的是一种设计模式,您可以在其中使用一个对象作为您希望在系统中的多个对象之间共享的特定属性和方法集的一种容器。例如,您可能有宽度和高度的getter和setter,它们可以应用于应用程序中的所有UI组件,因此,在JavaScript的情况下,您可以创建一个可以用“new”实例化的函数或一个对象字面值持有这些方法。然后,您可以使用“扩展”类型函数将这些方法复制到系统中的任意数量的对象上。

Underscore有一个mixin方法,它基本上只是一个扩展,其中所有传入的对象的方法都被添加到基础下划线对象以用于链接。 jQuery使用jQuery.fn的扩展方法做类似的事情。

就我个人而言,我喜欢保持原样扩展,即“将所有对象从这些对象复制到此对象”类型行为,同时具有单独的mixin方法,该方法只接受单个源对象,然后将所有更多参数作为属性的名称和复制方法(如果只传入目标和源代码而没有进一步的参数,那么它就像单源扩展一样)。

例如

function mixin(target, source) { 
    function copyProperty(key) { 
     target[key] = source[key]; 
    } 

    if (arguments.length > 2) { 
     // If there are arguments beyond target and source then treat them as 
     // keys of the specific properties/methods that should be copied over. 
     Array.prototype.slice.call(arguments, 2).forEach(copyProperty); 
    } else { 
     // Otherwise copy all properties/methods from the source to the target. 
     Object.keys(source).forEach(copyProperty); 
    } 
} 
+1

你能告诉我为什么你会做'Array.prototype.slice.call(arguments ,2)'over'arguments.slice(2)'。只是想知道它何时更有用。在此先感谢 – cantfindaname88

+3

@ cantfindaname88:'arguments',而'Array'类,不是'Array'实例,所以它没有'.slice()'方法。 –

相关问题