Куда идет JavaScript если свойства нет в объекте?
Комментарии (3)
Ответ сгенерирован нейросетью и может содержать ошибки
Поиск свойства в JavaScript и цепочка прототипов
Когда в JavaScript происходит попытка чтения свойства объекта, который не существует в самом объекте, движок языка начинает поиск этого свойства по специальной цепочке — цепочке прототипов (prototype chain). Это один из фундаментальных механизмов языка, основа его объектной модели.
Основной механизм поиска
Процесс происходит по следующему алгоритму:
- Поиск в самом объекте: Движок сначала проверяет, существует ли свойство непосредственно в объекте (в его собственных свойствах).
- Переход к
[[Prototype]]: Если свойство не найдено, движок обращается к внутреннему свойству объекта[[Prototype]](которое доступно черезObject.getPrototypeOf(obj)или через устаревшее, но известное свойство__proto__). - Рекурсивный поиск по цепочке: Поиск продолжается в объекте-прототипе. Если свойство не найдено и там, движок переходит к прототипу этого прототипа, и так далее, пока не достигнет конца цепочки.
- Завершение поиска: Цепочка прототипов заканчивается на объекте
null. Если свойство не было найдено на протяжении всей цепочки, результатом являетсяundefined.
Пример цепочки прототипов
Рассмотрим классический пример с созданием объекта через функцию-конструктор.
// Функция-конструктор
function Animal(name) {
this.name = name;
}
// Добавляем метод в прототип функции-конструктора
Animal.prototype.sayName = function() {
console.log(`My name is ${this.name}`);
};
// Создаем экземпляр
const cat = new Animal('Whiskers');
// Попытка чтения свойства
console.log(cat.name); // "Whiskers" - свойство есть в самом объекте
console.log(cat.sayName()); // "My name is Whiskers" - метод найден в прототипе
console.log(cat.age); // undefined - свойство отсутствует в объекте и в цепочке прототипов
В данном случае:
- Свойство
nameнаходится непосредственно в объектеcat. - Метод
sayNameнаходится вAnimal.prototype. Движок не найдет его вcat, поэтому перейдет по ссылке[[Prototype]]объектаcat(которая равнаAnimal.prototype) и найдет метод там. - Свойство
ageотсутствует как вcat, так и вAnimal.prototype. Поиск продолжится дальше:[[Prototype]]объектаAnimal.prototype— это стандартныйObject.prototype. Там этого свойства тоже нет.[[Prototype]]объектаObject.prototypeравенnull, поиск завершается, возвращаетсяundefined.
Визуализация цепочки и технические детали
Цепочка для объекта cat выглядит так:
cat (экземпляр) → Animal.prototype → Object.prototype → null
Ключевые технические моменты:
- Связь устанавливается при создании объекта: При использовании оператора
new, свойство[[Prototype]]созданного объекта явно устанавливается в значение свойстваprototypeфункции-конструктора. - Доступ через
Object.getPrototypeOf(): Для получения прототипа объекта следует использовать современный методObject.getPrototypeOf(obj). Свойство__proto__является устаревшим геттером/сеттером для[[Prototype]]. Object.prototype— вершина для большинства объектов: Практически все объекты, созданные с помощью литерала{}, конструктораnew Object()или черезnewс пользовательскими функциями, в конечном счете имеютObject.prototypeв своей цепочке. Именно здесь находятся универсальные методы, такие какtoString(),hasOwnProperty()иisPrototypeOf().- Разница между
undefinedиnullв цепочке: Если в цепочке встречается свойство со значениемundefined, это не остановит поиск. Поиск остановится только когда следующий прототип равенnull.
Современная альтернатива: классы (ES2015)
Синтаксис классов в ES2015 является "синтаксическим сахаром" над существующей моделью прототипов, но делает работу с цепочкой более интуитивной.
class Animal {
constructor(name) {
this.name = name;
}
sayName() {
console.log(`My name is ${this.name}`);
}
}
const cat = new Animal('Whiskers');
console.log(cat.sayName()); // Метод находится в Animal.prototype
Методы класса автоматически помещаются в свойство prototype функции-конструктора класса. Таким образом, механизм поиска свойства остается абсолютно идентичным описанному выше.
Практическое значение для разработчика
Понимание цепочки прототипов критически важно для:
- Эффективного использования наследования: Позволяет создавать иерархии объектов, экономя память (методы хранятся в прототипе, а не копируются в каждый экземпляр).
- Понимания поведения объектов: Знание, где искать методы типа
toString, помогает избежать ошибок. - Оптимизации производительности: Длинные цепочки прототипов могут негативно повлиять на скорость поиска свойства. В современных приложениях часто используют композицию вместо глубокого наследования.
- Работы с полифилами (polyfills): Полифилы для новых методов языка (например,
Array.prototype.flat) добавляются именно в объекты-прототипы (Array.prototype), чтобы они были доступны всем экземплярам через цепочку.
Таким образом, когда свойство отсутствует в объекте, JavaScript совершает переход по цепочке прототипов ([[Prototype]]) до объекта null. Этот механизм является основой для наследования, экономии памяти и организации кода в языке.