Как использовать инструкции конструктора es6 с другим контекстом - PullRequest
0 голосов
/ 11 февраля 2019

Можно ли использовать инструкции конструктора es6 в другом экземпляре, изменив контекст "this" (вызов, применение или другое)?Это возможно, используя es5 "классы".Вот небольшой пример того, что я имею в виду:

function ES5() {
  this.foo = 'foo';
}

class ES6 {
  constructor() {
    this.bar = 'bar';
  }
}

var a = new ES6();
ES5.call(a);
console.log(a.foo + a.bar); //foobar



var b = new ES5();
//Reflect.construct(ES6); ??
ES6.call(b); //TypeError: Class constructor ES6 cannot be invoked without 'new'

console.log(b.foo + b.bar); //how to get foobar here too?

Редактировать: Мой вопрос не имеет ничего общего с новым ключевым словом.Ответ, который я ищу, заключается в том, как выполнить инструкции, помещенные в конструктор es6, с использованием другого контекста «this» (с ключевым словом new или без него).

1 Ответ

0 голосов
/ 23 февраля 2019

Как отметили вы и ваши комментарии, попытка вызова конструкторов классов с пользовательским this контекстом - это на самом деле не то, что вы хотите попробовать, если есть какой-то способ обойти это.Это было сделано намеренно!

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


Обходной путь 1

Хотя установить this напрямую невозможнов вызове конструктора можно установить прототип this для объекта по вашему выбору .

. Для этого вы можете использовать Reflect.construct() для вызова внутреннего * 1020.* метод с пользовательским значением new.target.this будет инициализирован для объекта, унаследованного от new.target.prototype.

Построение по вашему примеру:

function ES5() {
    this.foo = 'foo';
}

class ES6 {
    constructor() {
        this.bar = 'bar';
    }
}

let b = new ES5();

function TemporaryHelperConstructor() {}
TemporaryHelperConstructor.prototype = b;

b = Reflect.construct( ES6, [], TemporaryHelperConstructor ); // The third argument corresponds to the value of new.target

console.log( b.foo + b.bar ); // foobar !

(Точная работа Reflect.construct() и внутренний метод [[Construct]] описаны в разделах 26.1.2 и 9.2.2 спецификаций)

Потенциальные проблемы

  • На самом деле конструктор класса не вызывается с this, связанным с b, он вызывается с this, связанным спустой объект, напрямую наследуемый от b.Это может привести к проблемам, если вы или конструктор класса полагаетесь на такие методы, как Object.getOwnPropertyNames(), Object.getPrototypeOf() и т. Д.

Обходной путь 2

Хотя невозможно вызватьвнутренний метод [[Call]] конструктора класса, не вызывая TypeError, , можно извлечь блок кода, прикрепленный к конструктору класса, и создать из него обычную функцию, которую затем можно вызвать с помощью пользовательскогоthis привязка.

Вы можете использовать метод Function.prototype.toString() для извлечения блока кода конструктора класса в виде строки.Затем конструктор Function() может сделать из этой строки обычную функцию, которую вы можете вызывать с пользовательской привязкой this через Function.prototype.apply().

Опираясь на ваш пример:

function ES5() {
    this.foo = 'foo';
}

class ES6 {
    constructor() {
        this.bar = 'bar';
    }
}

const b = new ES5();

const constructorBody = ES6.toString().match( /(?<=constructor\(\) ){[^}]*}/ )[0]
const ordinaryFunction = Function( constructorBody )

ordinaryFunction.apply( b ); // No TypeError

console.log( b.foo + b.bar ); // foobar !

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

(Согласно разделу 19.2.3.5 спецификаций, вы можете полагаться на достаточно последовательный вывод Function.prototype.toString(), чтобы этот подход работал во всех реализациях.)

Потенциальные проблемы

  • new.target будет установлен на undefined при выполнении обычной функции (как всегда в случае с[[Call]] вызовов), которые могут вызвать проблемы, если конструктор класса использовал его.
  • Закрытия исходного конструктора класса будут потеряны для новой функции, созданной с помощью Function() ( MDN ), что может привести к ReferenceErrors, если конструктор класса полагался на них.
  • Этот подход приведет к SyntaxError при применении к производному классу с использованием super(), что не является допустимым синтаксисом в обычномфункции.

Заключение

Идеального решения вашей проблемы не существует.Если ваш сценарий использования достаточно прост, вы все равно сможете достичь того, чего хотите.Частичные обходные пути будут сопровождаться пагубными проблемами - действовать осторожно!

...