隐式和显式混合之间的区别

时间:2017-10-01 07:09:06

标签: javascript mixins

示例来自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章节之外,只有一篇文章出现了,而且那篇文章没有提到“隐式混合”或“明确混合”。

那么,有什么区别?

2 个答案:

答案 0 :(得分:1)

我同意你的陈述,在第一个例子中,Vehicle的行为被明确复制到Car。这就是为什么它可以被称为显式混合。 程序员明确声明她将重用Vehicle对象中的方法。

我不会称之为完全混音,因为mixin函数不会制作Vehicle方法的完整副本,而只会复制不是的方法出现在Car对象中。

在第二个例子中,程序员没有公开或明确地表示她将扩展(=某些流行库中的mixin函数的名称)Another对象。找出Another对象重用Something行为的唯一方法是查看如何定义不同的方法。它只是在定义内部(因此隐式),你发现该对象借用了另一个对象的方法。

我想起来就是这样......

  • 如果扩展操作在代码中明显 ex ,则mixin可以称为 ex plicit。
  • 如果另一个对象的方法在 侧使用方法的定义,则mixin可以被称为 im plicit。

答案 1 :(得分:1)

我将保留我的三个赞成票中的每一个,因为所有Q&一个有共同思想的人。但是,一遍又一遍地思考任何一个例子,Q和A,我都会贡献我的2个。

正如我所看到的,Kyle的/ @ Keith的第一个例子已经是唯一的,因为属性复制mixin函数使用了一个警卫。

一方面,这不是人们对mix​​in的期望,因为大多数基于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应用于类型,从而在此类型上创建一个现在拥有此行为的闭包,并且还将调用其中的上下文行为。

要点:

  

...这是否意味着应用和调用实际上导致某种应用程序混合? ...

是的,applycall是将基于函数的mixin应用于类型/对象的方法......

  

...如果是这样,那么这不是第二个例子的混合吗?

...但是没有,第二个例子(Kyle所谓的'隐式mixin')尽可能远离mixin。缺少通过自己的抽象提供和应用其他行为的整个部分。此示例仅演示了如何在另一个对象的上下文中重用方法。

基于函数的mixins需要通过apply / call进行委派才能在对象上生效。但apply / {{}}方法直接确实使得这个代表团不仅仅是一个混合。