Как реализовать функцию "расширения классов" (например, функцию декоратора) в 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
}