Будет ли объект изменять свой скрытый класс, если мы создадим новые свойства прототипа?

В V8 объект изменяет свой скрытый класс при добавлении нового свойства.

function Point(x, y) {
  this.x = x; // This will create new hidden class
  this.y = y; // This too
}

Мой вопрос прост, это создаст новый скрытый класс?

Point.prototype.z = null;

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

Большое спасибо.

Ответ 1

Ответ: да, будет создан новый скрытый класс. Однако важно понять, что это будет объект prototype, который изменит его скрытый класс, а не объект, созданный конструктором Point.

Любой объект имеет скрытый класс, прикрепленный к нему. Давайте посмотрим на код

var o = new Point();
o.z = 0;  // (1)
Point.prototype.zz = 0;  // (2)

В любой момент любой объект имеет скрытый класс, что означает, что o, o.__proto__, o.__proto__.__proto__ имеют свойственный скрытый класс, связанный с ними.

Когда вы добавляете новое свойство к объекту, изменяется только скрытый класс этого объекта. Если вы смените скрытый класс прототипа, скрытые классы объектов, которые используют этот прототип , не меняют. Нет необходимости в таком изменении, потому что мы не ожидаем, что скрытый класс объекта X будет полностью описывать макет любого объекта во всей цепочке прототипов, его скрытый класс описывает компоновку X и X самостоятельно, Кроме того, просто невозможно реализовать такое распространение вниз: для этого требуется, чтобы VM поддерживала обратные связи между прототипами и всеми связанными объектами: чтобы в любой момент для объекта X было перечислено все объекты Y, которые имеют Y.__proto__ === X.

Для приведенного выше кода означает, что оператор (1) изменяет только скрытый класс o и оператор (2) изменяет только скрытый класс Point.prototype (это тот же объект, что и o.__proto__), но не самого o.

Кроме того, если вы считаете код следующим:

Point.prototype.z = 0;  // Initial value
var o1 = new Point();
o1.z = 11;  // (3)
var o2 = new Point();

который иногда рекомендуется, то это антиспам производительности, потому что скрытые классы o1/o2 и Point.prototype отключены. Назначение в (3) изменит скрытый класс o1, хотя o1.__proto__ уже имеет свойство z. Объекты o1 и o2 будут иметь разные скрытые классы, которые вызовут весь код, который использует оба из них, чтобы пойти полиморфными и наказывать за производительность. Более того, o1 будет использовать больше места, чем в случае, если z был добавлен прямо в конструкторе, потому что z будет храниться в хранилище свойств вне объекта.