示例来自You Do not Know JS this&对象原型(here是特定的章节,向下滚动一点或只搜索单词“mixin”,“explicit”或“implicit”,文章应该出现。)
在那本书中,“mixin”被解释为“混合不同对象的行为”(释义)。
然而,我无法理解Explicit和Implicit mixins之间的区别。
这是一个明确的mixin:
// vastly simplified `mixin(..)` example:
function mixin( sourceObj, targetObj ) {
for (var key in sourceObj) {
// only copy if not already present
if (!(key in targetObj)) {
targetObj[key] = sourceObj[key];
}
}
return targetObj;
}
var Vehicle = {
engines: 1,
ignition: function() {
console.log( "Turning on my engine." );
},
drive: function() {
this.ignition();
console.log( "Steering and moving forward!" );
}
};
var Car = mixin( Vehicle, {
wheels: 4,
drive: function() {
Vehicle.drive.call( this );
console.log( "Rolling on all " + this.wheels + " wheels!" );
}
} );
这是一个隐含的混合:
var Something = {
cool: function() {
this.greeting = "Hello World";
this.count = this.count ? this.count + 1 : 1;
}
};
Something.cool();
Something.greeting; // "Hello World"
Something.count; // 1
var Another = {
cool: function() {
// implicit mixin of `Something` to `Another`
Something.cool.call( this );
}
};
Another.cool();
Another.greeting; // "Hello World"
Another.count; // 1 (not shared state with `Something`)
我能找到的唯一区别是第一个示例使用函数将Vehicle
的所有行为复制到Car
,而第二个示例仅使用单个方法。所以我认为在第一个示例中,Vehicle
的行为被显式复制到Car
,而在第二个示例中,只引用了一个方法,其this
绑定到Another
因此,隐含的混合?这是差异所在吗?如果这是差异,那么第一个示例也包含一个Implicit Mix-in的示例,因为它还引用并绑定this
方法之一的Vehicle
。如果我的假设是正确的,那么IMO,第一个例子应该被称为“完全混合”,而后者应该被称为“部分混合”,因为这些名称更适合行为。
我在线搜索除了你不知道的JS章节之外,只有一篇文章出现了,而且那篇文章没有提到“隐式混合”或“明确混合”。
那么,有什么区别?
答案 0 :(得分:1)
我同意你的陈述,在第一个例子中,Vehicle
的行为被明确复制到Car
。这就是为什么它可以被称为显式混合。
程序员明确声明她将重用Vehicle
对象中的方法。
我不会称之为完全混音,因为mixin函数不会制作Vehicle
方法的完整副本,而只会复制不是的方法出现在Car
对象中。
在第二个例子中,程序员没有公开或明确地表示她将扩展(=某些流行库中的mixin函数的名称)Another
对象。找出Another
对象重用Something
行为的唯一方法是查看如何定义不同的方法。它只是在定义内部(因此隐式),你发现该对象借用了另一个对象的方法。
我想起来就是这样......
答案 1 :(得分:1)
我将保留我的三个赞成票中的每一个,因为所有Q&一个有共同思想的人。但是,一遍又一遍地思考任何一个例子,Q和A,我都会贡献我的2个。
正如我所看到的,Kyle的/ @ Keith的第一个例子已经是唯一的,因为属性复制mixin
函数使用了一个警卫。
一方面,这不是人们对mixin的期望,因为大多数基于mixin的组合(在理论和实践中)只是覆盖已经存在的行为(最后应用的行为获胜)。
另一方面,有一个好处是控制目标对象的行为实现,否则将被覆盖(drive
来自给定的第一个例子)。这个唯一简单的后卫技巧创造了一种特殊模式,不能满足经典mixin模式/方法的所有功能/机制......让我们发明术语 fill-in < / em> ,因为它保持现有行为不变,但填补了尚未存在的行为的其他空白。
还在谈论控制目标对象行为的优势...... Kyle的方法仍然很有吸引力......因为......使用经典的mixins无法解决同名方法/行为的冲突,这就是特征适用于。 Traits确实为组合提供了功能/操作符,只需要一些开销即可。
结论:
凯尔的第一个例子的模式可以称为 (冲突避免)填写 。此外,此示例确实实现了另一种冲突方法 - drive
- 部分使用 显式委派 - 因此它通过显式调用/调用此方法借用了另一个对象的方法在另一个对象(...委托)的上下文中。
所提供的第二个例子显然既不接近任何形式的mixin也不接近上述标记的填充。只是代码通过两种JavaScript的调用方法之一再次使用显式委派...... call
/ apply
。
将始终明确使用经典/真实mixin。每个mixin都会带有明确混合到另一个对象(基于对象和copy
的方法)或明确表示的行为应用于通过call
/ apply
应用于对象的其他对象(基于函数的mixins / Flight Mixins )。
附录
...或明确将应用于其他对象(通过调用/应用应用于对象的基于函数的mixins / Flight Mixins)。
这是否意味着应用和调用实际上导致某种应用程序混合?如果是这样,那么这不会使第二个例子成为一种混合吗?
感谢您提出这个问题。在写上述内容时,我确实希望有人能够指出这一点。
写作有所不同......
// generic template ... generic implementation of a `last` list method
function getLastItemOfAppliedListContext() {
return this[this.length - 1];
}
var
arr = [9, 8, 7],
str = 'foo bar';
console.log('arr : ', arr);
// delegation ... invoke the above template within an explicitly applied context
console.log('getLastItemOfAppliedListContext.call(arr) : ',getLastItemOfAppliedListContext.call(arr));
console.log('str : ', str);
// delegation ... invoke the above template within an explicitly applied context
console.log('getLastItemOfAppliedListContext.call(str) : ',getLastItemOfAppliedListContext.call(str));
.as-console-wrapper { max-height: 100%!important; top: 0; }
......或遵循这样的方法......
function withGetLastListItem() { // function based mixin approach …
this.last = function () { // … providing a generic implementation …
return this[this.length - 1]; // … of a `last` list method / behavior.
}
}
var
list = {length: 3, 0: 'foo', 1: 'bar', 2: 'baz'},
arr = [9, 8, 7],
str = 'foo bar';
// does create a closure each ...
withGetLastListItem.call(list); // ... over `list`,
withGetLastListItem.call(arr); // ... over `arr`,
withGetLastListItem.call(String.prototype); // ... and over `String.prototype`
// ... applying to each a very own enclosed `last` method/behavior.
console.log('list : ', list);
console.log('list.last() : ', list.last());
console.log('arr : ', arr);
console.log('arr.last() : ', arr.last());
console.log('str : ', str);
console.log('str.last() : ', str.last());
console.log('list.last : ', list.last);
console.log('arr.last : ', arr.last);
console.log('str.last : ', str.last);
// will always be `false` due to each type carrying a very
// own enclosed version of the same `last` implementation
console.log('(list.last === arr.last) ? ', (list.last === arr.last));
console.log('(list.last === str.last) ? ', (list.last === str.last));
.as-console-wrapper { max-height: 100%!important; top: 0; }
第一个提供的代码块是直接方法委派的示例。第二个示例以多种方式使用委托。首先,基于函数的mixin是一个容纳至少一个行为或一组许多行为的容器。其次,必须将基于函数的mixin应用于类型,从而在此类型上创建一个现在拥有此行为的闭包,并且还将调用其中的上下文行为。
要点:
...这是否意味着应用和调用实际上导致某种应用程序混合? ...
是的,apply
和call
是将基于函数的mixin应用于类型/对象的方法......
...如果是这样,那么这不是第二个例子的混合吗?
...但是没有,第二个例子(Kyle所谓的'隐式mixin')尽可能远离mixin。缺少通过自己的抽象提供和应用其他行为的整个部分。此示例仅演示了如何在另一个对象的上下文中重用方法。
基于函数的mixins需要通过apply
/ call
进行委派才能在对象上生效。但apply
/ {{}}方法直接确实使得这个代表团不仅仅是一个混合。