Что вернёт метод book.getUpperName()?
Дан код:
function Book() {
this.name = 'foo';
}
Book.prototype = {
getName: function () {
return this.name;
},
};
var book = new Book();
Book.prototype.getUpperName = function () {
return this.getName().toUpperCase();
};
console.log(book.getUpperName());
Что вернет метод console.log(book.getUpperName())?
Теория по задаче
Рассмотрим детально, как устроены прототипы и работа с ними в JavaScript.
Концепция прототипов в JavaScript
Каждый объект в JavaScript имеет скрытое свойство __proto__, которое ссылается на его прототип. Именно через прототип реализовано наследование в JavaScript. Когда мы пытаемся обратиться к свойству или методу объекта, JavaScript первым делом ищет это свойство непосредственно в самом объекте. Если свойство отсутствует, JavaScript поднимается вверх по цепочке прототипов и продолжает поиск в родительском прототипе, пока либо не найдет нужное свойство, либо не достигнет вершины цепочки (свойство __proto__, равный null).
Работа с функцией-конструктором и её прототипом
Функция-контруктор служит шаблоном для создания нового объекта. Каждый раз, когда мы используем оператор new, создаётся новый объект, связанный с прототипом конструктора. Например, рассмотрим фрагмент кода:
function Book() {
this.name = 'foo';
}
Здесь конструктор Book устанавливает свойство name каждого создаваемого экземпляра. Однако важно отметить, что методы и общие свойства часто определяются на уровне прототипа, чтобы избежать дублирования памяти для каждого отдельного объекта.
Далее в коде изменяется прототип конструктора Book следующим образом:
Book.prototype = {
getName: function() {
return this.name;
}
};
Теперь каждый экземпляр, созданный через конструктор Book, получит доступ к методу getName, который возвращает значение свойства name. Обратите внимание, что сам объект-потомок не хранит копию метода, а берёт его из своего прототипа.
Создание экземпляра и расширение прототипа
Создав экземпляр класса Book:
const book = new Book();
мы получили объект с единственным собственным свойством name. Остальные методы берутся из прототипа. Важно помнить, что изменения в прототипе влияют на существующие экземпляры, поскольку все они связаны с одним и тем же прототипом.
Следующая часть кода добавляет дополнительный метод к прототипу:
Book.prototype.getUpperName = function() {
return this.getName().toUpperCase();
}
Этот метод вызывает ранее определенный метод getName и преобразует полученный результат в верхний регистр.
Важность понимания контекста this
Контекст this в JavaScript определяет, какой объект рассматривается при выполнении метода. Внутри конструктора и методов, вызванных через экземпляр, this относится к самому этому экземпляру. То есть, когда мы вызываем book.getUpperName(), this внутри метода указывает на объект book, а значит, обращение к this.name действительно даст нам установленное значение "foo".
Итоговая логика выполнения
Итак, выполняя вызов:
console.log(book.getUpperName());
происходит следующее:
- Метод
getUpperNameнаходит и вызывает методgetName, передавая текущий контекст (this), т.е. экземплярbook. - Метод
getNameвозвращает значение свойстваname, которое установлено как"foo". - Полученная строка
"foo"преобразуется в верхний регистр, давая итоговую строку"FOO".