Как реализовать функцию "расширения классов" (например, функцию декоратора) в ES5, которая также работает с классами ES2015

Я задал похожий вопрос вчера, и он был отмечен как дубликат
ES6: вызвать конструктор класса без нового ключевого слова

Поскольку вышеупомянутый связанный документ не дает полного ответа, я теперь задал тот же вопрос немного по-другому сегодня... и предоставил возможное решение ниже:

Как мне реализовать функцию ES5 "extension" (она должна просто расширять класс) в следующей демонстрации, чтобы также работать с классами ES2015 без получения сообщения "Невозможно вызвать baseClass конструктора классов без ошибки" new ""?

[Редактировать: функция "Расширение" будет частью (переносимой) библиотеки ES5. Затем эта библиотека может позже использоваться кем-то, кто использует либо ES5, либо>= ES2015 - оба случая должны работать]

// ES5 function (similar to: constr => class extends constr {})
function extend(constr) {
  var ret = function () {
    constr.apply(null, arguments)
  }

  ret.prototype = Object.create(constr.prototype)

  return ret
}


// ES2015 code
const
  baseClass = class {},
  extendedClass = extend(baseClass)

new extendedClass() // <- Error: Class constructor baseClass cannot be invoked without 'new'

Смотрите: https://jsfiddle.net/kjf7cheo/

Одно решение может выглядеть следующим образом (=> с использованием "новой функции (...)"). Что-то не так / небезопасно / должно рассматриваться с этим решением?

var extend = null

if (typeof Proxy === 'function') { // check for ECMAScript version >= 2015
  try {
    extend =
      new Function('baseClass', 'return class extends baseClass {}')
  } catch (e) {
    // ignore
  }
}

if (!extend) {
  extend = function (baseClass) {
    var ret = function () {
      baseClass.apply(this, arguments)
    }

    ret.prototype = Object.create(baseClass.prototype)

    return ret
  }
}

// Result: function "extend"

2 ответа

Я бы попробовал использовать Reflect.construct за это:

let extend = function (constr) {
    let ret;
    if(Reflect && Reflect.construct){
        ret = function(...args){
            let inst = Reflect.construct(constr, args, eval('new.target'));
            return inst;
        }
    } else {
        ret = function () {
        constr.apply(this, arguments)
        }
    }
    ret.prototype = Object.create(constr.prototype);
    ret.prototype.constructor = ret;
    return ret;
}

eval line просто делает так, что вы не получите синтаксическую ошибку при переносе на ES5. Это необходимо, если вы хотите расширить созданные вами конструкторы extend в противном случае прототипы будут потеряны.

Я думаю, что это могло бы быть решением без использования "eval(...)" или "new Function(...)" ... что вы думаете?

Найдите демо здесь: https://jsbin.com/vupefasepa/edit?js,console

var hasReflection =
  typeof Reflect === 'object'
    && Reflect
    && typeof Reflect.construct === 'function'

function extend(baseClass) {
  var ret = function () {
    if (!(this instanceof ret)) {
      throw new Error("Class constructor cannot be invoked without 'new'")
    }

    var type = this.constructor

    return hasReflection
      ? Reflect.construct(baseClass, arguments, type)
      : void(baseClass.apply(this, arguments))
  }

  ret.prototype = Object.create(baseClass.prototype, {
    constructor: {
      value: ret
    }
  })

  return ret
}
Другие вопросы по тегам