Как можно относиться к конструкторам Function & Class одинаково в отношении Function.prototype. [Call и | или apply]?
То есть может быть доступна абстракция для обеспечения того же ожидаемого поведения для
class
как с
function
?
Тест
var context = { datum0: true };
var result1 = (function Class() { this.datum1: true; return this; }).call(context);
var result2 = (class Klass { datum1 = true; }).call(context);
assert(result1).equals(context); // true
assert(result2).equals(context); // true
assert(context).has('datum0'); // [obviously] true
assert(context).has('datum1'); // true
assert(context).has('datum2'); // true
Мотивация
Иногда идеально позволить конструкции определить поведение объекта, где this
не является экземпляром самого класса, в отличие от Mixin. Это просто, когда все конструкторы могут быть приняты как конструкторы функций. Однако, если конструктор класса должен определять поведение, скажем, на new Proxy(context, handler)
, объект-контекст (this
) в пределах области действия экземпляра всегда является экземпляром данного класса. Следующий код демонстрирует такие неприятности и многое другое:
class Class {
datum1 = true;
constructor() {
document.body.addEventListener('click', this.handleClick, false); // this !== context
}
handleClick = (e) => {...};
}
var Klass = function Klass() {
function handleClick(e) {...}
this.datum2 = true;
this.handleClick = handleClick;
document.body.addEventListener('click', this.handleClick, false); // triggers proxy. this === context
return this;
};
var context = new Proxy({ datum0: true, target: Class || Klass }, {
get(object, key, receiver) {
var result = Reflect.get(object.target.prototype, key, receiver);
if (result && result.call) return new Proxy(result, { // if function, return function-proxy with trigger
apply(fn, thus, args) {
var result = Reflect.apply(fn, thus, args);
console.log(`${key} is a function and was invoked`); // mock notifier
return result;
}
});
return result;
}
});
var decorated = Class.call(context); // throws "constructor 'Class' cannot be invoked without 'new'"
var decorated = Klass.call(context); // decorated === context === true
В этом примере context
получает данные и поведение от Klass, но не от Class. Аналогично - когда мы передаем this.handleClick
в addEventListener
- прокси-геттер замечает, что handleClick
является функцией, возвращает прокси для handleClick
, и то, что передается в качестве слушателя, это то, что с триггером на сообщите нам, что метод был вызван. Это полезно, если мы хотим проверить состояние объекта и запустить шаблон refre sh. Однако - в Class
- поскольку объект-контекст всегда является экземпляром конструктора, мы не можем дублировать структуру с контекстом, который мы хотим вместо этого использовать. Более того, если мы хотим позволить разработчикам использовать функциональные конструкторы (скажем, для совместимости), наша нормализация для модулей и компонентов сводится к наименьшему общему знаменателю - никогда не использовать вызов / применять на любой зарегистрированной способности.
Теперь, если нам нужны такие функциональные возможности для наших методов, мы можем использовать ES6 Decorator для определения метода - что-то вроде ...
class Class {
@trigger() handleClick(e) {...} // [hypothetically] returns a function-proxy for handleClick
}
... однако это все еще не охватывает все мои базы - по крайней мере, те, которые я не обозначил в этом вопросе.
Вопрос
Есть ли способ поменять контекст объекта (this
) на класс-конструктор? (Возможно, используя Class.constructor.call или иным образом)