Расширить EventTarget, без синтаксиса “class”

125
23 октября 2019, 11:30

Как я могу записать аналог class MyClass extends EventTarget{}, без использования синтаксиса "class"? В спецификации сказано что это только синтаксический сахар, что происходит при его использовании?

Мне интересно узнать что именно происходит при произнесении этого заклинания. Я пробовал что-то вроде

function MyClass(){
    var tmp = new EventTarget;
    //здесь можно задать новые методы, например tmp.hi = _=>{alert("Hi")}
    return tmp;
}

Но в этом случае (new MyClass).constructor.name выводит "EventTarget". Это не удобно для отладки.

Answer 1

В общем случае конструкция

class MyTarget extends EventTarget {
    constructor() { 
        super();
        // init MyTarget
    }
    foo() { 
        // ... 
    }
}

(почти) эквивалентна следующей:

function MyTarget() {
    EventTarget.apply(this);
    // init MyTarget
}
MyTarget.prototype = Object.create(EventTarget.prototype);
MyTarget.prototype.constructor = MyTarget;
MyTarget.prototype.foo = function() {
    // ...
};

Однако так сделать невозможно, т. к. EventTarget не допускает вызов через apply, только через new — так что в данном случае можно только использовать extends.

Если хочется узнать подробнее, нужно читать про цепочку прототипов и свойство Function.prototype в JS: например, MDN или ООП в прототипном стиле.

UPD

Более точный вариант, использующий рефлексию и изменения прототипа (не рекомендуется к production-использованию!):

function MyTarget() {
    const self = Reflect.construct(EventTarget, arguments, this.constructor);
    // init self
    return self;
}
Object.setPrototypeOf(MyTarget, EventTarget);
MyTarget.prototype = Object.create(EventTarget.prototype);
MyTarget.prototype.constructor = MyTarget;
MyTarget.prototype.foo = function() {
    // ...
};

UPD2

Вариант для старых браузеров, конкретно для IE, не поддерживающего Reflect.construct (также исключительно в демонстрационных целях):

function MyTarget() {
    var bound = EventTarget.bind(null, arguments);
    var self = new bound();
    Object.setPrototypeOf(self, MyTarget.prototype);
    // init self
    return self;
}
Object.setPrototypeOf(MyTarget, EventTarget);
MyTarget.prototype = Object.create(EventTarget.prototype);
MyTarget.prototype.constructor = MyTarget;
MyTarget.prototype.foo = function() {
   // ...
};

Также для поддержки IE10- можно заменить Object.setPrototypeOf(o, proto);, на o.__proto__ = proto;.

Однако насколько я могу судить, всё это довольно бессмысленно. Reflect.construct поддерживается всеми браузерами кроме IE, а IE, если верить MDN не поддерживает конструктор EventTarget. ¯_(ツ)_/¯

UPD3

Исправил ошибку в примере с Reflect.construct — нужно было возвращать именно объект от Reflect.construct, а возвращался (неявно) объект, созданный new.

Дополнительно также отмечу, что для ещё большего соответствия поведению extends стоит вместо

MyTarget.prototype = Object.create(EventTarget.prototype);
MyTarget.prototype.constructor = MyTarget;

делать так (чтобы свойство constructor было неперечисляемым):

MyTarget.prototype = Object.create(EventTarget.prototype, {
    constructor: { value: MyTarget, writable: true, configurable: true }
});
Answer 2

Вы правильно понимаете, что это синтаксический сахар, но он призван упростить чтение кода.

На самом деле происходит прототипное наследование

function EventTarget(eventName) { 
 this.eventName = eventName; 
} 
 
EventTarget.prototype.eventFunc = function() { 
    console.log(this.eventName); 
}; 
 
function MyClass(eventName) { 
    EventTarget.call(this, eventName); 
} 
 
MyClass.prototype = new EventTarget(null); 
 
MyClass.prototype.anotherFunc = function() { 
    this.eventFunc(); 
}; 
 
var m = new MyClass('It is event'); 
m.anotherFunc();

READ ALSO
Как добавить/удалить класс в куки?

Как добавить/удалить класс в куки?

Пожалуйста, подскажите, как можно при: добавлении, удаление классов (при toggleClass например) сохранять результат после перезагрузки страницыЯ...

128
Вывести массив коротким образом в String

Вывести массив коротким образом в String

Есть массив,числа это типа номера маршруток на данном улицеЧисла идут только на возрастание и по порядку

114
Сортировка базы данных по нажитию на кнопки

Сортировка базы данных по нажитию на кнопки

У меня вот есть база данных в формате json

122
Как сделать отмену действия?

Как сделать отмену действия?

В чём ошибка моего кода? Первая ветвь работает нормально, как сделать чтобы при повторном нажатии заработала и вторая ветвь?

130