Как эмулировать Javascript `super` для определения` new.target`? - PullRequest
0 голосов
/ 29 января 2019

Я пытаюсь эмулировать этот JavaScript без использования ключевых слов ES6 (например, без class, super или extends):

class Foo {
  constructor() {
    if (!new.target) 
      console.log('Foo() must be called with new');
  }
}

class Bar extends Foo {
  constructor() {
    super(...arguments);
  }
}

var bar = new Bar();
var barIsFoo = bar instanceof Foo;
console.log(barIsFoo); // true

Я получил это далеко, но они не эквивалентны.Следующие броски (я регистрирую вместо), в то время как последний не делает:

function Foo() {
  if (!new.target) 
      console.log('Foo() must be called with new');
}

function Bar() {
  Foo.apply(this, arguments)
}
Bar.prototype = Object.create(Foo.prototype);
Bar.prototype.constructor = Bar;

var bar = new Bar();
var barIsFoo = bar instanceof Foo;
console.log(barIsFoo);

Итак, как мне эмулировать предоставление значения для new.target, когда я звоню в Foo из Bar?


ТакПохоже, что нет apply или call, который позволяет передавать new.target.Я полагаю, что это победило бы цель new.target (хотя тот факт, что все в JS является публичным, мне очень понравился).

Так что для эмуляции в ES5 нам нужно что-то добавить.

Одно решение в ответе ниже выделяет новый объект.

В этом решении добавлены новые функции construct, которые можно как обычно объединить в ES5, и оставить саму функцию свободной, чтобы ничего не делать, кроме проверки, используется ли она в качестве конструктора.

function Foo() {
  if (!new.target) 
    throw 'Foo() must be called with new';
  console.log('Foo new check');
  Foo.prototype.construct.apply(this, arguments);
}
Foo.prototype.construct = function() {
  console.log('Foo construction logic');
}

function Bar() {
  if (!new.target) 
    throw 'Bar() must be called with new';
  console.log('Bar new check');
  Bar.prototype.construct.apply(this, arguments);
}
Bar.prototype = Object.create(Foo.prototype);
Bar.prototype.constructor = Bar;
Bar.prototype.construct = function() {
  // super()
  Foo.prototype.construct.apply(this, arguments);

  console.log('Bar construction logic');
}

var bar = new Bar();
var barIsFoo = bar instanceof Foo;
console.log(barIsFoo);

В итоге, кажется, что ES6 обладает не не просто синтаксическим сахаром по сравнению с ES5.Конечно, они могли бы просто добавить Function.prototype.super(target, arguments, newTarget), и тогда мы могли бы просто использовать это.Надеюсь, они это сделают!


Только super может вызвать функцию в Javascript, и this не будет доступен немедленно.Так что super уникален.И super может быть вызван только в контексте constructor, который может использоваться только в контексте class.Ооо, все эти ключевые слова необходимы, чтобы super работал.Итак, Javascript представил очень специфическую объектно-ориентированную функцию.Похоже, что построение языка поверх идеи «прототипа» имеет свои ограничения.

Какой позор ...

Интересно, почему вдруг javascript решил применить этот один инвариант.this недоступен до вызова super.Почему бы просто не сделать super короткой рукой для BaseType.prototype.constructor.call(this, ...).Почему бы не позволить его вызывать более одного раза?В Javascript мы можем отбросить многие другие проблемы, зачем начинать применять их сейчас?

Ну, в любом случае ...

Итак, двойная нижняя строка, существует ранний связанный вызов Javascriptsuper, у которого нет эквивалента с поздней привязкой (в отличие, например, от foo.bar(), который можно назвать с поздней (r) связью через bar.call('foo')).

Ответы [ 2 ]

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

Синтаксис класса ES6 не является синтаксическим сахаром для ES5, но является в значительной степени синтаксическим сахаром для других функций ES6.

class Bar extends Foo {
  constructor() {
    super(...arguments);
  }
}

очень похоже на то, что вы сделали

let Bar = function(...args) {
  const _this = Reflect.construct(Object.getPrototypeOf(Bar), args, new.target);
  return _this;
};
Object.setPrototypeOf(Bar, Foo);
Object.setPrototypeOf(Bar.prototype, Foo.prototype);

, где Reflect.construct создает и объект с данным значением new.target и вызывает данный конструктор с набором аргументов.

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

Используйте Object.assign для назначения new Foo(...arguments) родительского конструктора для экземпляра:

function Foo(arg) {
  if (!new.target) 
    throw 'Foo() must be called with new';
  this.arg = arg;
  this.fooProp = 'fooProp';
}

function Bar() {
  Object.assign(
    this,
    new Foo(...arguments)
  );
  this.barProp = 'barProp';
}
Bar.prototype = Object.create(Foo.prototype);
Bar.prototype.constructor = Bar;

var bar = new Bar('abc');
var barIsFoo = bar instanceof Foo;
console.log(barIsFoo);
console.log(bar);

Но new Foo(...arguments) - это синтаксис ES6.Чтобы перевести это на ES5, вместо этого используйте

new (Function.prototype.bind.apply(Foo, [null, ...arguments]))()

(который заботится о части new), что снова приводит к

new (Function.prototype.bind.apply(Foo, [null].concat(Array.prototype.slice.call(arguments))))()

function Foo(arg) {
  if (!new.target) 
    throw 'Foo() must be called with new';
  this.arg = arg;
  this.fooProp = 'fooProp';
}

function Bar() {
  Object.assign(
    this,
    new (Function.prototype.bind.apply(Foo, [null].concat(Array.prototype.slice.call(arguments))))()
  );
  this.barProp = 'barProp';
}
Bar.prototype = Object.create(Foo.prototype);
Bar.prototype.constructor = Bar;

var bar = new Bar('abc');
var barIsFoo = bar instanceof Foo;
console.log(barIsFoo);
console.log(bar);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...