【问题标题】:The difference between implicit and explicit mixins隐式和显式 mixins 的区别
【发布时间】:2017-10-01 07:09:06
【问题描述】:

示例取自 You Don't Know JS this & Object Prototypes(here 是具体章节,向下滚动一点或者直接搜索“mixin”、“explicit”或“implicit”这个词,文章应该出现)。

在那本书中,“混合”被解释为“混合不同对象的行为”(释义)。

但是,我无法理解显式和隐式 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,因此是隐式混合?这就是区别所在吗?如果这就是区别,那么第一个示例也包含一个隐式混合示例,因为它还引用并绑定了Vehicle 的方法之一的this。如果我的假设是正确的,那么 IMO,第一个示例应称为“完全混合”,后者应称为“部分混合”,因为这些名称更适合行为。

我在网上搜索,除了你不知道的 JS 章节,只出现了 1 篇文章,并且没有提到“隐式混入”或“显式混入”。

那么,有什么区别呢?

【问题讨论】:

  • 这是怎么看的。不同之处在于,第一个属性是使用函数显式映射的。在第二个属性中已经设置,因为调用父函数正在设置属性,所以赋值是隐式的。
  • 那个“隐式混合”被称为delegation
  • @Bergi 你确定是委托吗?如果是,那么代表团在哪里进行?在第二个示例中,我没有看到任何委托,属性被复制而不是像方法那样委托。我忽略了什么?
  • 是的,.call(this) 是经典的 delegation 方法行为。
  • @Bergi 你能详细说明一下吗?

标签: javascript mixins


【解决方案1】:

我将保留我的三张赞成票中的每一张,因为所有问答都有一个深思熟虑的共同点。但是一遍又一遍地思考任何示例,Q 和 A,我将贡献我的 2cents。

在我看来,Kyle 的/@Keith 的第一个示例已经是独一无二的,因为属性复制 mixin 函数使用了保护。

一方面,这不是人们对 mixin 的期望,因为大多数基于 mixin 的组合(在理论上和实践中)只是覆盖已经存在的行为(最后应用的行为获胜)。

另一方面,可以控制目标对象的行为实现的优势,否则会被覆盖(给定的第一个示例中的drive)。这个唯一简单的 guard 技巧创建了一个特殊的模式,它完全不符合经典 mixin 模式/方法的功能/机制......让我们发明术语 fill-in 对它来说,因为它保持现有行为不变,但填补了尚未存在的行为的其他空白。

仍然在谈论控制目标对象行为的优势……Kyle 的方法确实很有吸引力……因为……使用经典的 mixins 无法解决同名方法/行为的冲突,这就是 特征 用于。 Traits 确实为组合提供了功能/操作符,人们可以通过一些开销购买。

结论:

Kyle 的第一个示例的模式可以称为(避免冲突)填充。此外,此示例确实实现了一个其他冲突的方法 - drive - 部分使用 explicit delegate - 因此它通过在另一个对象的上下文(...委托)。

提供的第二个示例显然既不接近任何形式的混合,也不接近上面标记的填充。它只是通过 JavaScript 的两种调用方法之一再次使用显式委托的代码...call/apply

将始终显式使用经典/真正的 mixin。 每个 mixin 都具有显式混合到另一个对象(基于对象和 copy 的方法)或显式应用的行为到另一个对象(基于函数的 mixins/Flight Mixins,通过 call/apply 应用于对象)。


附录

... 或者明确地应用于其他对象(基于函数的 mixins/Flight Mixins,通过 call/apply 应用于对象)。

这是否意味着 apply 和 call 实际上会导致某种应用程序 mixin ?如果是这样,那第二个例子不也是一种混合吗?

感谢您提出这个问题。在写上述内容时,我确实希望有人能准确指出这一点。

写法有区别……

// 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 ? ...

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

...如果是这样,那第二个例子不也是一种混合吗?

...但是不,第二个示例(Kyle 所谓的“隐式混合”)尽可能远离混合。缺少通过自己的抽象提供和应用附加行为的整个部分。这个例子只是演示了如何在另一个对象的上下文中重用一个方法。

基于函数的混入需要通过apply/call 委托才能在对象上生效。但是applying/calling 直接使用一个方法确实使这个委托不仅仅是一个 mixin。

【讨论】:

  • 或者明确地应用于其他对象(基于函数的 mixins/通过 call/apply 应用于对象的飞行 Mixins)。 这是否意味着 applycall 实际上会导致某种应用程序混入?如果是这样,那第二个例子不也是一种混合吗?
  • 很好的答案顺便说一句
  • @Taurus,您可能会在我的帖子下方阅读“附录”作为您问题的答案。
  • 那么,您是说为了让我们能够将其称为基于函数的混入,函数必须在目标对象上设置一些东西?如果这是真的,那么你的第二个例子实际上就是凯尔所说的隐式混合。
  • 1/2 @Taurus - 一点也不。我说过,mixin 必须是一组行为的 container 或至少一个 behaviorhost,无论 mixin 是如何的已实现(无论是基于对象的mixin 实用程序还是仅基于函数)。我进一步说,仅通过call/apply 进行的委派还不是mixin/还不是mixin。如果我不够清楚,我会再次提到,使用 mixin 总是很明确的;因此永远不会有像 implicit mixin 模式这样的东西......
【解决方案2】:

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

我不会将其称为完整的 mixin,因为 mixin 函数不会完全复制 Vehicle 方法,而只会复制 不 的方法strong> 存在于Car 对象中。

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

我是这样想的……

  • 如果代码中明确ex按下了扩展操作,则可以将 mixin 称为 explicit。
  • 如果在方法定义中中使用另一个对象的方法,则可以将 mixin 称为 implicit。

【讨论】:

  • 但是在这种情况下,第二个例子是隐式的,而第一个例子既是显式的又是隐式的(参见Car.drive的正文)。
猜你喜欢
  • 1970-01-01
  • 2015-04-23
  • 2013-05-19
  • 1970-01-01
  • 2016-12-16
  • 1970-01-01
  • 2015-08-18
  • 2018-09-05
  • 2017-05-30
相关资源
最近更新 更多