Javascript: смешивание в геттере (распространение объекта)


Я попытался создать смешивание в геттере в объект JS с помощью синтаксиса оператора spread , однако он всегда возвращает null.

HTML:

<body>
  <div id="wrapperA"></div>
  <div id="wrapperB"></div>
</body>
<script src='./test.js'></script>

JS:

"use strict";

const mixin = {
    get wrapper() { return document.getElementById(this.wrappername); }
}

const wrapperA = {
  wrappername: 'wrapperA',
  ...mixin
}

const wrapperB = {
  wrappername: 'wrapperB',
  ...mixin
}

console.log(wrapperA);
console.log(wrapperB);

Вывод консоли:

{wrappername: "wrapperA", wrapper: null}
{wrappername: "wrapperB", wrapper: null}

Это ссылается на функцию расширения, которая должна работать, и из того, что я могу сказать выше, код создал непреднамеренное закрытие. Однако он читается довольно плохо по сравнению с синтаксисом .... Кто-нибудь знает, как заставить этот код работать последнее решение? Знают ли разработчики ES об этой проблеме и будет ли она исправлена в ES7?

1 2

1 ответ:

Это не ошибка. При интерпретации синтаксиса спреда вычисляются значения свойств mixin, то есть вызывается геттер wrapper с this, равным mixin. Заметим, что this - это не новый объект, который строится, как ... имеет приоритет над последовательностью запятой. Таким образом, в тот момент, когда выполняется ..., конечный объект не находится в поле зрения. Во-вторых, скопированное свойство больше не является геттером, а обычным свойством с атомарным значением (не a функция).

Поведение может быть лучше понято с помощью почти идентичного процесса, который выполняется при использовании Object.assign:

Object.assign({
  wrappername: 'wrapperA'
}, mixin);

Если вы хотите, чтобы геттер wrapper был вызван с новым объектом как this, то сделайте:

"use strict";

class Wrapper {
    constructor(wrappername) {
        this.wrappername = wrappername;
    }
    get wrapper() {
        return document.getElementById(this.wrappername); 
    }
}

const wrapperA = new Wrapper('wrapperA');
const wrapperB = new Wrapper('wrapperB');

console.log(wrapperA.wrapper);
console.log(wrapperB.wrapper);
<div id="wrapperA"></div>
<div id="wrapperB"></div>

Множественное Наследование

Если вам действительно нужно множественное наследование, то посмотрите на такую библиотеку, как Ring.js , что делает это действительно легким.

Есть несколько вопросов и ответов на mixin реализации на StackOverflow . Вот одна из многих идей, выведенных из этой статьи :

"use strict";
function MixinNameGetter(superclass) {
    return class extends superclass {  
        get wrapper() {
            return document.getElementById(this.wrappername); 
        }
    }
}

function MixinLetterGetter(superclass) {
    return class extends superclass {  
        get letter() {
            return this.wrappername.substr(-1); 
        }
    }
}

class Wrapper {
    constructor(wrappername) {
        this.wrappername = wrappername;
    }
}

class ExtendedWrapper extends MixinNameGetter(MixinLetterGetter(Wrapper)) {
}

const wrapperA = new ExtendedWrapper('wrapperA');
const wrapperB = new ExtendedWrapper('wrapperB');

console.log(wrapperA.wrapper, wrapperA.letter);
console.log(wrapperB.wrapper, wrapperB.letter);
<div id="wrapperA"></div>
<div id="wrapperB"></div>
Хотя это эффективно обеспечивает множественное наследование, результирующая иерархия классов, производных от выражений, на самом деле не является компонентом эффективного кода.

Декораторы

Другой подход заключается в отказе от идеи миксинов и использовании декораторов. вместо этого:

"use strict";
function DecoratorNameGetter(target) {
    Object.defineProperty(target, 'wrapper', {
        get: function () { 
            return document.getElementById(this.wrappername); 
        }
    });
}

function DecoratorLetterGetter(target) {
    Object.defineProperty(target, 'letter', {
        get: function () {
            return this.wrappername.substr(-1); 
        }
    });
}

class Wrapper {
    constructor(wrappername) {
        this.wrappername = wrappername;
        DecoratorNameGetter(this);
        DecoratorLetterGetter(this);
    }
}

const wrapperA = new Wrapper('wrapperA');
const wrapperB = new Wrapper('wrapperB');

console.log(wrapperA.wrapper, wrapperA.letter);
console.log(wrapperB.wrapper, wrapperB.letter);
<div id="wrapperA"></div>
<div id="wrapperB"></div>
Это приводит к плоской структуре (без цепочки прототипов), где расширение происходит в самом целевом объекте.