Каков наилучший способ использовать mixins в JS? - PullRequest
0 голосов
/ 31 мая 2018

Недавно я наткнулся на две статьи о миксинах.Это сбило меня с толку, между которыми один лучше, чем другой.

Первый из mdn

var calculatorMixin = Base => class extends Base {
  calc() { }
};
var randomizerMixin = Base => class extends Base {
  randomize() { }
};

class Foo { }
class Bar extends calculatorMixin(randomizerMixin(Foo)) { }

Второй из https://javascript.info/mixins

let sayMixin = {
  say(phrase) {
    alert(phrase);
  }
};

let sayHiMixin = {
  __proto__: sayMixin, // (or we could use Object.create to set the prototype here)

  sayHi() {
    // call parent method
    super.say(`Hello ${this.name}`);
  },
  sayBye() {
    super.say(`Bye ${this.name}`);
  }
};

class User {
  constructor(name) {
    this.name = name;
  }
}

// copy the methods
Object.assign(User.prototype, sayHiMixin);

// now User can say hi
new User("Dude").sayHi(); // Hello Dude!

Объект, созданный в этих сценариях, также имеет различную композицию / структуру.

enter image description here

  • Теперь я в замешательстве: кто из них лучше других?

  • Какие преимущества дает одно из них?

  • Иследовательно, какой из них я предпочитаю использовать.

Ответы [ 2 ]

0 голосов
/ 22 января 2019

Простой способ сделать это:

// mixin
let sayHiMixin = {
  sayHi() {
    alert(`Hello ${this.name}`);
  },
  sayBye() {
    alert(`Bye ${this.name}`);
  }
};

// usage:
class User {
  constructor(name) {
    this.name = name;
  }
}

// copy the methods
Object.assign(User.prototype, sayHiMixin);

// now User can say hi
new User("Dude!").sayHi(); // Hello Dude!
0 голосов
/ 02 июня 2018

Я полностью согласен с Джаредом Смитом.Как и в случае с любым инструментом или набором инструментов, нужно знать, использовать ли его вообще.Если по какой-либо причине кто-то выбирает концепцию mixins , он должен действительно знать, на что он способен и чего он пропускает, особенно если он применяется к парадигмам / концепциям программирования JavaScript.

Итам я самоуверен, таким образом, следующие мысли и технические подходы будут предоставлены с моей собственной точки зрения.Другие решения гораздо более распространены, и некоторые из них я тоже иногда использую.

Давайте возьмем первый источник, предоставленный ОП.Если кто-то, например, переписывает приведенный выше пример во что-то вроде ...

const calculatorMixin = Base => class extends Base {
  calc() { }
};
const randomizerMixin = Base => class extends Base {
  randomize() { }
};

class Baz { }
const calcAndRandomizeMixin = calculatorMixin(randomizerMixin(Baz));

class Biz extends calcAndRandomizeMixin { }

const biz = new Biz;

console.log('(biz instanceof Biz) ? ', (biz instanceof Biz));
console.log('(biz instanceof Baz) ? ', (biz instanceof Baz));
console.log('(biz instanceof calcAndRandomizeMixin) ? ', (biz instanceof calcAndRandomizeMixin));
.as-console-wrapper { max-height: 100%!important; top: 0; }

... тогда моя самая большая проблема связана с самим подходом, поскольку он полностью основан на классах и их расширении. «mixins» - это классы, созданные мгновенно фабриками классов.Они всегда расширяют другой класс.Таким образом, это чистое наследство.

И хотя кто-то писал такой mixin-класс как контейнер поведения, который "может" делать вещи и типпозже "имеет" определенное поведение, этот подход технически вовсе не признает концепцию миксинов, потому что по самой своей природе он основан на потомке-родителе или "является" отношение.

Второй подход, с использованием объектов и Object.assign, на первый взгляд выглядит как современный вариант многих древних миксиновых подходов, которые тогда использовали комбинацию поведения, связанного с объектами.и самописный метод extends ... как ... extends(targetObject, mixinSourceObject).

Уникальным для этого подхода является то, как он поддерживает / решает «Composite-Mixins» ... Mixinsкоторые созданы из других миксинов.Связывание поведения делегированием super и присвоение другого объектного миксина свойству __proto__ миксина, на мой взгляд, жизнеспособно и элегантно.

Лично я бы потратил больше времени на изучение / изучение этого вопроса.во-вторых, предоставленный подход.

И есть еще один подход ... функциональные миксины.Код второго, объектно-ориентированного примера миксина, переписанного в его функциональный кулон, выглядит следующим образом ...

const sayMixin = (function () {   // basic function-based mixin.

  // shared code.                 //
  function say(phrase) {          // a single implementation
    console.log(phrase);          // of `say` behavior ...
  }                               //

  // return function based mixin. //
  return function sayMixin () {   // ... that might get applied
    this.say = say;               // many times but always as
  };                              // reference / shared code.

}());

const sayHiMixin = (function () { // function-based *composite-mixin*.

  // shared code.

  // object that helps with behavior forwarding.
  const sayProxy = {};

  // apply behavior of `sayMixin`.
  sayMixin.call(sayProxy);

  // a single implementation of `sayHi` behavior.
  function sayHi() {
    sayProxy.say(`Hello ${this.name}!`);  // forwarding.
  }
  // a single implementation of `sayBye` behavior.
  function sayBye() {
    sayProxy.say(`Bye ${this.name}!`);    // forwarding.
  }

  // return function based composite mixin.
  return function sayHiMixin () {
    this.sayHi = sayHi;   // - always shares one and the ...
    this.sayBye = sayBye; //   ... same implementation(s).
  };

}());


class User {
  constructor(name) {

    // public property.
    this.name = name;
  }
}
// apply the composite `sayHiMixin`.
sayHiMixin.call(User.prototype);

// now a `User` can say hi and bye
const dude = new User('Dude');

dude.sayHi();   // Hello Dude!
dude.sayBye();  // Bye Dude!

console.log('dude.name : ', dude.name); // Dude
.as-console-wrapper { max-height: 100%!important; top: 0; }

Есть некоторые веские аргументы в пользу того, что даже сегодня выбирают этот подход.Во-первых, и это имеет общее с обоими другими, упомянутыми ОП ... нет необходимости в дополнительной библиотеке.Во-вторых, он работает в любой среде ES3, в отличие от других.В-третьих, подход на основе функций реализует миксины как «применимые типы», таким образом, каждый получает делегирование и инкапсуляцию бесплатно.

Сила обоих будет продемонстрирована сейчас.Продолжая работать с примером кода 2-го примера и введенным подходом на основе смешанных функций, можно очень легко создать другого пользователя, который скрывает свой начальный name от публичного, но действительно выставляет его через поведение say.Конечно, следующий код предназначен только для понимания концепций.На практике вряд ли можно было бы понять, что такое «1054 *« гибридный композитный миксин », подобный этому ...

const sayMixin = (function () {   // basic function-based mixin.

  // shared code.                 //
  function say(phrase) {          // a single implementation
    console.log(phrase);          // of `say` behavior ...
  }                               //

  // return function based mixin. //
  return function sayMixin () {   // ... that might get applied
    this.say = say;               // many times but always as
  };                              // reference / shared code.

}());

const sayHiMixin = (function () { // function-based *composite-mixin*.

  // shared code.

  // object that helps with behavior forwarding.
  const sayProxy = {};

  // apply behavior of `sayMixin`.
  sayMixin.call(sayProxy);

  // a single implementation of `sayHi` behavior.
  function sayHi() {
    sayProxy.say(`Hello ${this.name}!`);  // forwarding.
  }
  // a single implementation of `sayBye` behavior.
  function sayBye() {
    sayProxy.say(`Bye ${this.name}!`);    // forwarding.
  }

  // // return function based composite mixin.
  // return function sayHiMixin () {
  //   this.sayHi = sayHi;   // - always shares one and the ...
  //   this.sayBye = sayBye; //   ... same implementation(s).
  // };

  // return function based hybrid composite mixin.
  return function sayHiMixin (properties) {
    if (properties && (typeof properties === 'object')) {

      console.log('sayHiMixin :: payload bound to behavior');

      this.sayHi = sayHi.bind(properties);    // - creates each a ...
      this.sayBye = sayBye.bind(properties);  //   ... new reference.
    } else {
      console.log('sayHiMixin :: direct behavior reference');

      this.sayHi = sayHi;   // - always shares one and the ...
      this.sayBye = sayBye; //   ... same implementation(s).
    }
  };

}());


class User {
  constructor(name) {

    // public property.
    this.name = name;
  }
}
// apply the composite `sayHiMixin`.
sayHiMixin.call(User.prototype);

// now a `User` can say hi and bye
const dude = new User('Dude');

dude.sayHi();   // Hello Dude!
dude.sayBye();  // Bye Dude!

console.log('dude.name : ', dude.name); // Dude


class AnotherUser {
  constructor(name) {

    // local property + public accessor methods.
    sayHiMixin.call(this, { name: name });
  }
}

// now a `User` can say hi and bye
const john = new AnotherUser('John');

john.sayHi();   // Hello John!
john.sayBye();  // Bye John!

console.log('john.name : ', john.name); // undefined
.as-console-wrapper { max-height: 100%!important; top: 0; }

Мнение о функциональных миксинах

Только с теми функциями, которые уже есть в ES3, инкапсуляция через замыкания, явное делегирование функций и применение различных контекстов через call / apply, уже можно начинать с композиции на основе миксинов.Комбинация этих методов позволяет использовать более мощные концепции, такие как разрешение конфликтов, которые могут / будут основываться на уже продемонстрированной пересылке через прокси-ссылку и некоторый состав функций.Возможно введение и обход дополнительного состояния.Таким образом, можно даже реализовать концепции, выходящие за пределы миксинов, такие как Черты , Состояния Stateful и Таланты , причем последняя является композиционной концепцией, которая действительно соответствует языковым парадигмам JavaScript.

Практическое правило использования миксина

Используйте это только в том случае, если повторное использование кода может быть описано прилагательным, например observable и / или если во всей системе неодинаковые классы и / или гетерогенные типы нуждаются в одном и том жедополнительное поведение.

...