Javascript库中的mixin()和extend()有啥区别
Posted
技术标签:
【中文标题】Javascript库中的mixin()和extend()有啥区别【英文标题】:What's the difference between mixin() and extend() in Javascript librariesJavascript库中的mixin()和extend()有什么区别 【发布时间】:2011-11-22 07:14:39 【问题描述】:我正在浏览各种库,看到很多extend() 弹出,但我也看到mixin() 出现。 YUI 既有 mixin 又有扩展。
这两个概念有什么区别?我什么时候会在 mixin 和扩展对象之间做出决定?
谢谢, 马特
【问题讨论】:
【参考方案1】:为清楚起见编辑:这些有时是相同的,有时不是; mixin 是用于在多个对象之间重用函数的模式的名称,而 extend 更多的是用于这样做的算法/方法。有相同的情况(例如下划线 _.extend)和不同的情况(backbone.js 的扩展)。
在它们不同的情况下,extend 通常会将原型链接到正在扩展的对象,而 mixin 会将方法列表复制到目标上。
【讨论】:
除非你想要两个类给你的行为。除非您进入多重继承,否则不能为此使用继承,这有其自身的一系列问题。 我不确定这有什么关系,因为extend和mixin与继承等没有直接关系,它们只是模仿这种行为的一种方法。 我只是在解释他们没有相同行为的情况。 啊,我明白你的意思了。我猜有一些库使用扩展来链接原型,这只能完成一次,其中 mixin 会倾向于一个一个地复制它。当然,并不是所有的库都这样做,所以这是一个需要检查的重要区别。【参考方案2】:mixins 不适用于 instanceof,但 extends 可以。 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
和其他未输入的字段时遇到了这个问题。它不可能从Field
中的Labelable
行为中受益,因为它们不能扩展Field
,因为它也包含所有输入行为。
【讨论】:
这个答案是 Ext.js 特有的,不适用于许多其他 javascript 库(jQuery 和 Underscore 只是两个例子)。 @natlee75 我的回答是 mixins 不使用原型链,并且它在instanceof
测试中失败,Ext-JS 是它们的一种实现(它们让你检查某个东西是否是 mixin以不同的方式)。据我所知,jquery 或 underscore 不会通过在原型链中设置它们来实现混合(它们只是复制属性)。您的评论暗示 jquery 和 underscore 可以具有与 instanceof 一起正常工作的 mixin?请详细说明/
不,但他们的extend
函数也不起作用。只有“类库”(如Ext.define
)使用“扩展”术语来表示原型继承。
@Bergi ECMAScript 6 也使用extends for prototypal inheritance【参考方案3】:
extend 方法在 JavaScript 库中非常常见,通常是一种允许消费代码将一个或多个对象的所有“自己的”属性和方法添加到目标对象的方法。代码通常非常简单:遍历每个参数的所有自己的键,而不是第一个参数,并将其中存储的值复制到第一个参数。
“混合”是指一种设计模式,在该模式中,您将一个对象用作一种容器,用于存放您希望在系统中的多个对象之间共享的一组特定属性和方法。例如,您可能有可以应用于应用程序中所有 UI 组件的宽度和高度 getter 和 setter,因此对于 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);
【讨论】:
你能告诉我为什么你会做Array.prototype.slice.call(arguments, 2)
而不是arguments.slice(2)
。只想知道什么时候更有用。提前致谢
@cantfindaname88: arguments
,虽然Array
-like 不是Array
实例,所以它没有.slice()
方法。【参考方案4】:
你绝对可以使用extends来创建mixins。
Mixins 提供了多重继承的所有好处,没有层次结构(JavaScript 中的原型继承)。它们都允许您在多个对象上重用一个接口(或一组函数)。使用 mixins 不会遇到在父子关系中可能遇到的“钻石问题”。
当一个对象从两个对象继承相同的函数(甚至函数名)时,就会出现菱形问题。为什么?如果这两个对象之一修改了函数,添加了功能(即在 Java 中称为“super”),JavaScript 不再知道如何解释/组合这两种方法。 Mixins 是一种避免这种层级关系的方法。它们定义了您可以在任何地方粘贴的功能。 Mixin 通常也不包含它们自己的数据。
例如,我可以在 jQuery 中使用 $.extend()
编写一个 mixin。
var newmixin = $.extend(, mixin1, mixin2)
将合并两个接口,并将它们展平(覆盖名称冲突)。
这里有 3 件事需要考虑:
-
这两个接口的组合是否具有“层次结构”,即。
父/子关系。在 JavaScript 中,这意味着原型继承。
函数是否被复制或引用?如果原来的方法改变了,继承的方法也会改变吗?
当两个同名的方法组合在一起时会发生什么?这些冲突是如何处理的?
【讨论】:
以上是关于Javascript库中的mixin()和extend()有啥区别的主要内容,如果未能解决你的问题,请参考以下文章
从mixin到new和prototype:Javascript原型机制详解
从mixin到new和prototype:Javascript原型机制详解