#javascript
#javascript
Вопрос:
В настоящее время я изучаю Javascript и увидел, что мы можем определять члены объекта в его прототипе, а не в конструкторе объекта, поскольку это делает объекты легче, потому что они не содержат код метода в каждом экземпляре.
Я вижу, как переменные могут работать плохо, если все объекты этого конструктора указывают на одно и то же переменное поле, но методы не должны меняться слишком сильно или вообще, поэтому их совместное использование не проблема.
Есть ли причина не определять методы в прототипе, а не в конструкторе объекта?
Ответ №1:
Одной из причин, по которой можно предпочесть размещать методы в самом экземпляре, было бы обеспечение правильного this
контекста при кратком добавлении метода. Правильная this
работа — очень распространенная проблема в JavaScript.
Например:
class Foo {
i = 0;
clickHandler = () => console.log(this.i );
}
const f = new Foo();
window.onclick = f.clickHandler;
Это общий шаблон с компонентами класса в React. Если вместо этого поместить метод в прототип, иногда он может стать немного уродливее:
class Foo {
i = 0;
clickHandler() {
console.log(this.i );
}
}
const f = new Foo();
window.onclick = () => f.clickHandler();
Другой распространенный способ справиться с этим — переместить связанный метод из прототипа в экземпляр в конструкторе:
class Foo {
i = 0;
constructor() {
this.clickHandler = this.clickHandler.bind(this);
}
clickHandler() {
console.log(this.i );
}
}
const f = new Foo();
window.onclick = () => f.clickHandler();
Это причина предпочесть один другому? Это зависит от вас, но следует иметь в виду одно неоспоримое преимущество.
Комментарии:
1. Почему эта проблема привязки является такой проблемой в React? Ваш второй пример отлично работает в ванильном JS со стандартным eventlistener :
window.addEventListener("click", () => f.clickHandler())
.2. Конечно, но для этого требуется выполнять
() => obj.fn()
каждый разfn
, когда требуется вызвать (и становится уродливее, когда необходимо передать дополнительные аргументы). Использование вместо этого полей класса (которые, кстати, помещают метод в сам экземпляр) является лишь одним из многих доступных вариантов.
Ответ №2:
Просто идея от меня — есть способ справиться с «правильным» контекстом выполнения, например, обработчиков кликов на уровне прототипа. Рассмотрим следующий пример:
class AbstractClickHandler {
// the "proper" this is ensured via closure over the actual implementation
clickHandler() {
return (...args) => {
this.handleClick.apply(this, args)
}
}
// no-op in base class
handleClick() {
}
}
class ClickHandler extends AbstractClickHandler {
constructor(data) {
super()
this.data = data
}
// actual implementation of the handler logic
handleClick(arg1, arg2) {
console.log(`Click handled with data ${this.data} and arguments ${arg1}, ${arg2}`)
}
}
const instance = new ClickHandler(42)
const handler = instance.clickHandler()
handler.call(null, 'one', 'two')
Таким образом, нет необходимости определять связанный обработчик в каждом соответствующем конструкторе.