Запутанное поведение прототипа в JavaScript

#javascript #prototype

#javascript #прототип

Вопрос:

Я не понимаю, почему происходит следующее:

 function Class() {}
Class.prototype.foo = {a: 1};
Class.prototype.bar = 1;

x = new Class();
y = new Class();

y.foo.a = 2;
y.bar = 2;

x.foo.a; //2
x.bar; //1
  

Когда я устанавливаю y.foo.a значение 2, кажется, что он выполняет ту же операцию, что и y.constructor.prototype.foo.a = 2 . Почему это должно быть, учитывая, что это y.bar = 2 не влияет y.constructor.prototype.bar ?

Ответ №1:

Вы смешиваете свойства прототипа со свойствами, которые являются локальными для экземпляра объекта. Используя y.bar = 2 , вы присваиваете свойство экземпляра ( bar ) экземпляру y . При интерпретации свойство ( bar ) сначала просматривается внутри самого экземпляра. Если он там не найден, поиск продолжается в экземплярах prototype. Теперь вы присвоили bar значение y so y.bar = 2 , но экземпляр x не знает об этом, поэтому для x поиска продолжает использовать свой прототип (то есть prototype.bar , по-прежнему со значением 1).

Для y.foo.a foo в y нет свойства экземпляра, поэтому оно просматривается в его прототипе. Там оно найдено, и свойству a присваивается новое значение. Поскольку вы тем самым изменяете значение Class.prototype свойства foo , оно также представлено в x .

Если вы хотите изменить bar в его прототипе (таким образом, исходящем из экземпляра y ), вам придется использовать прототип y ‘s constructor (который является Class ):

 y.constructor.prototype.bar = 2;
  

Может, мистер Дуглас Крокфорд сможет прояснить для вас ситуацию (перейдите примерно к 23-й минуте видео о наследовании прототипов)?

Ответ №2:

Вы читаете, y.foo но присваиваете y.bar . Это разные операции с разной семантикой. Параметр y.foo.bar должен сначала быть прочитан y.foo : он ищет значение foo в y , не может его найти, затем заглядывает в y прототип, обнаруживает объект и только после этого изменяет этот объект. присвоение y.bar просто ищет y, а затем изменяет его. x.bar и y.bar затем обозначают разные объекты, в то время как x.foo и и y.foo обозначают один и тот же объект.

Ответ №3:

При чтении атрибута выполняется поиск в объекте-прототипе, если он не определен в основном объекте.

Установка атрибута всегда устанавливает его для основного объекта и НИКОГДА для объекта-прототипа.

Из-за этого всегда бывает плохой идеей определять что-то другое, кроме функций или значений, которые никогда не изменятся в объектах-прототипах.

Чтобы действительно понять, что происходит, я рекомендую вам поэкспериментировать с

  __proto__
  

атрибут. Я знаю, не все браузеры поддерживают это, но это действительно помогает вам понять, как работает прототипирование в JavaScript.