【发布时间】:2011-11-22 07:14:39
【问题描述】:
我正在浏览各种库,看到很多extend() 弹出,但我也看到mixin() 出现。 YUI 既有 mixin 又有扩展。
这两个概念有什么区别?我什么时候会在 mixin 和扩展对象之间做出决定?
谢谢, 马特
【问题讨论】:
标签: javascript design-patterns extend mixins
我正在浏览各种库,看到很多extend() 弹出,但我也看到mixin() 出现。 YUI 既有 mixin 又有扩展。
这两个概念有什么区别?我什么时候会在 mixin 和扩展对象之间做出决定?
谢谢, 马特
【问题讨论】:
标签: javascript design-patterns extend mixins
为清楚起见编辑:这些有时是相同的,有时不是; mixin 是用于在多个对象之间重用函数的模式的名称,而 extend 更多的是用于这样做的算法/方法。有相同的情况(例如下划线 _.extend)和不同的情况(backbone.js 的扩展)。
在它们不同的情况下,extend 通常会将原型链接到正在扩展的对象,而 mixin 会将方法列表复制到目标上。
【讨论】:
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,因为它也包含所有输入行为。
【讨论】:
instanceof 测试中失败,Ext-JS 是它们的一种实现(它们让你检查某个东西是否是 mixin以不同的方式)。据我所知,jquery 或 underscore 不会通过在原型链中设置它们来实现混合(它们只是复制属性)。您的评论暗示 jquery 和 underscore 可以具有与 instanceof 一起正常工作的 mixin?请详细说明/
extend 函数也不起作用。只有“类库”(如Ext.define)使用“扩展”术语来表示原型继承。
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)。只想知道什么时候更有用。提前致谢
arguments,虽然Array-like 不是Array 实例,所以它没有.slice() 方法。
你绝对可以使用extends来创建mixins。
Mixins 提供了多重继承的所有好处,没有层次结构(JavaScript 中的原型继承)。它们都允许您在多个对象上重用一个接口(或一组函数)。使用 mixins 不会遇到在父子关系中可能遇到的“钻石问题”。
当一个对象从两个对象继承相同的函数(甚至函数名)时,就会出现菱形问题。为什么?如果这两个对象之一修改了函数,添加了功能(即在 Java 中称为“super”),JavaScript 不再知道如何解释/组合这两种方法。 Mixins 是一种避免这种层级关系的方法。它们定义了您可以在任何地方粘贴的功能。 Mixin 通常也不包含它们自己的数据。
例如,我可以在 jQuery 中使用 $.extend() 编写一个 mixin。
var newmixin = $.extend({}, mixin1, mixin2) 将合并两个接口,并将它们展平(覆盖名称冲突)。
这里有 3 件事需要考虑:
【讨论】: