JavaScript — вычисляемые свойства — глубокая путаница

Кажется, меня очень смущают вычисляемые свойства в JavaScript.

Когда я определяю объект и помещаю [d] в качестве ключа (как ключ/имя свойства), что на самом деле делает этот [d]? Кажется, что для некоторых значений d он вычисляет s = d.toString() и использует это значение s в качестве ключа свойства. Но для других значений d (например, когда d является символом) он действительно использует значение символа в качестве ключа.

Таким образом, это двойное поведение [d] (как синтаксической конструкции) кажется запутанным. Может ли кто-нибудь подробно объяснить, как это работает?

Кстати, есть ли другие особые случаи? Или это только когда d является символом, когда у нас есть такое особое поведение?

Вернемся к основам: какие вещи могут быть ключами/именами свойств объекта? Это просто строки или только строки и символы, или есть еще что-то дополнительное...?

Пример:

var symbol = Symbol("test");

function Animal(name){
	this.name = name;
}

Animal.prototype = {};
Animal.prototype.constructor = Animal;

function Dog(breed){
    this.breed = breed;
    this.name = "Dog";
    this.s = symbol;
}

Dog.prototype = new Animal();
Dog.prototype.constructor = Dog;

console.log("001");
var d = new Dog("Sharo");
for (let x in d){
    console.log(x, ":", d[x]);
}

console.log("002");
d = new Object();
for (let x in d){
    console.log(x, ":", d[x]);
}

console.log("003");
d = new Number(5);
for (let x in d){
    console.log(x, ":", d[x]);
}

var d1 = {};
var d2 = {};

var d = new Dog("Sharo");

var m = {[d1] : 5, [d2] : 10, [d] : 20, z : 100, symbol: 2000, [symbol] : 5000};

console.log("============================");
console.log(m);

for (let x in m){
    console.log(x, ":", m[x]);
}
console.log("============================");


person peter.petrov    schedule 21.12.2019    source источник
comment
Вы читали напр. developer.mozilla.org/en-US/docs/ Интернет/JavaScript/Справочник/? Имена свойств представляют собой строку или символ. Любое другое значение, включая число, преобразуется в строку.   -  person jonrsharpe    schedule 21.12.2019
comment
@jonrsharpe Нет, я не... Только книги (некоторые из них 15 лет назад, когда вообще не было символов)... А некоторые из них (даже недавние) считают эту информацию слишком простой и просто пропускают упоминание Symbol. Они просто говорят об именах свойств, и естественно предположить, что имена — это строки и только строки. Вот что вызвало мое замешательство. Эта ссылка, кажется, именно то, что мне нужно! Спасибо.   -  person peter.petrov    schedule 21.12.2019
comment
@jonrsharpe Имена свойств представляют собой строку или символ. Любое другое значение, включая число, преобразуется в строку. Как их принуждают? Позвонив toString(), как я догадался, или... каким-то другим способом?   -  person peter.petrov    schedule 21.12.2019
comment
@Андреас Спасибо. Вы хотели вместо этого указать мне на ToPropertyKey или...?   -  person peter.petrov    schedule 21.12.2019
comment
Почему символ остается символом, а все остальное преобразуется в строку? 12.2.6.8 Семантика среды выполнения: оценка -› ComputedPropertyName -› Шаг 4 -› 7.1. 14 ToPropertyKey (аргумент) -> Шаг 3 + 4   -  person Andreas    schedule 21.12.2019
comment
@Andreas Да, я видел это, оно прямо под ToObject, на которое ты указал мне первым. Спасибо еще раз.   -  person peter.petrov    schedule 21.12.2019
comment
Мой первый комментарий был для: Может ли кто-нибудь подробно объяснить, как это работает? Между прочим, есть ли другие особые случаи?, а второй для: Это просто строки или только строки и символы, или есть еще что-то дополнительное...?   -  person Andreas    schedule 21.12.2019
comment
@Andreas Хорошо... Понятно... Неважно... Думаю, теперь я понял.   -  person peter.petrov    schedule 21.12.2019
comment
@peter.petrov, насколько я знаю, вычисленное значение свойства будет работать с ключом объекта, и значение ключа должно иметь тип строки или числа, а не что-то еще. Таким образом, в основном javascript, за обложками, попытается отобразить «любое значение» (с учетом их типа) в строку или число, чтобы быть совместимым значением ключа для объекта. Это конец истории.. теперь, как это происходит, я думаю, что для символов карта проста, для других вещей, я думаю, к этой вещи применяется метод .toString(). Для меня это абсолютно правильный вопрос, за который проголосовали, кстати.   -  person Victor    schedule 21.12.2019
comment
@Victor - и значение ключа должно быть строкой или числом, а не чем-то другим Нет, это неправильно. Ключи свойств объекта никогда не являются числами. Это могут быть строки или символы. Подробности в ответах ниже.   -  person T.J. Crowder    schedule 21.12.2019
comment
@T.J.Crowder, я не эксперт в javascript .. но в Chrome, насколько я могу кодировать, я действительно вижу ключи как числа. Позвольте мне поделиться своей точкой зрения с этим изображением: imgbbb.com/image/L4PBKi (см. внизу, консоль хрома, строки кода, которые я написал и оцениваются интерпретатором).   -  person Victor    schedule 21.12.2019
comment
@Victor - Именно так devtools показывает это вам, потому что вам разрешено использовать числовые литералы в качестве имен ключей в литералах объектов, но результирующий ключ свойства представляет собой строку. Попробуйте это: for (const key in {1:1}) { console.log(`${key}: ${typeof key}`); } (И, опять же, см. ответы ниже, которые относятся к соответствующим частям спецификации.)   -  person T.J. Crowder    schedule 21.12.2019
comment
@T.J.Crowder, эй!! вы были правы... и спасибо, что нашли время (и терпение), чтобы указать на это. Кстати: ранее я проголосовал за ваш ответ (на данный момент единственный ... определенно он заслуживает большего количества голосов) изображение/L4PIlH   -  person Victor    schedule 21.12.2019
comment
@Victor - С удовольствием. :-) Если вас интересуют новые возможности, добавляемые в JavaScript, у меня есть книга, которая выйдет в начале следующего года — ссылки в моем профиле. :-) Удачного кодирования!   -  person T.J. Crowder    schedule 21.12.2019


Ответы (2)


Поскольку никто не заинтересован в ответе на этот вопрос, я отвечу на него сам, основываясь на комментариях, которые я получил выше и из-за которых я больше не запутался.

Обратите внимание, что этот ответ здесь основан на ES6. Я имею в виду... кто знает, что еще ждет JavaScript в будущем :)

Когда я определяю объект и помещаю [d] в качестве ключа (как ключ/имя свойства), что на самом деле делает этот [d]? Кажется, что для некоторых объектов d он вычисляет s = d.toString() и использует это значение s в качестве ключа свойства. Но для других объектов d (например, когда d является символом) он действительно использует значение символа в качестве ключа.

Да, это правильно. Когда d является символом, его значение используется напрямую. Когда d не является символом, его значение приводится к строке, и эта строка используется в качестве имени/ключа свойства. Принуждение больше похоже на String(d), чем на d.toString().

Таким образом, это двойное поведение [d] (как синтаксической конструкции) кажется запутанным. Может ли кто-нибудь подробно объяснить, как это работает?

Уже объяснил выше.

Кстати, есть ли другие особые случаи? Или это только когда d является символом, когда у нас есть такое особое поведение?

Других "особых случаев" нет. Начиная с ES6 ключами свойств могут быть только строки и символы.

Вернемся к основам: какие вещи могут быть ключами/именами свойств объекта? Это просто строки или только строки и символы, или есть еще что-то дополнительное...?

Как уже было сказано, начиная с ES6 ключами свойств могут быть только строки и символы.

Ссылки:

(1) https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Property_Accessors

«Имена свойств представляют собой строку или символ. Любое другое значение, включая число, преобразуется в строку».

(2) https://www.ecma-international.org/ecma-262/6.0/#sec-topropertykey

person peter.petrov    schedule 21.12.2019
comment
@Андреас Хорошая идея. Сделанный. - person peter.petrov; 21.12.2019
comment
Как насчет функции в качестве ключа? const m = new Map(); const p = (x) => console.log(x); m.set(p, "test"); for ([key, value] of m) { key(value); key(1); } - person Andreas; 21.12.2019

В комментарии к другому ответу вы сказали, что думали, что ключи свойств всегда были строками. Они были до ES2015. :-)

Таким образом, это двойное поведение [d] (как синтаксической конструкции) кажется запутанным. Может ли кто-нибудь подробно объяснить, как это работает?

Начиная с ES2015, в язык были добавлены символы, а ключи свойств объекта стали могут быть либо строками, либо символами. Итак, когда вы делаете:

const obj = {
    [d]: "foo"
};

... часть вычисляемого ключа свойства ([d]: "foo"), которая работает следующим образом:

  1. Пусть value будет результатом вычисления выражения "foo"
  2. Пусть keyValue будет результатом вычисления выражения d
  3. Если keyValue является символом, пусть key = keyValue; иначе пусть key = String(keyValue)
  4. Установите для свойства key на obj значение value

Я опустил пару деталей для ясности. Вы можете видеть это в абстрактной операции ToPropertyKey в спецификации, которая используется всякий раз, когда значение используется в качестве ключа свойства (в литерале объекта, как указано выше, или при доступе к свойству объекта через нотацию скобок и т. д.).

Кстати, есть ли другие особые случаи? Или это только когда d является символом, когда у нас есть такое особое поведение?

Вернемся к основам: какие вещи могут быть ключами/именами свойств объекта? Это просто строки или только строки и символы, или есть еще что-то дополнительное...?

Просто символ и строка. :-) Дело не столько в том, что Symbol является особым случаем, просто там, где раньше ключи свойств всегда были строками, теперь они могут быть строками или Symbols .

(Забавный факт: в спецификации они определяют "ключ свойства" как строку или Символ, идентифицирующий свойство, и "имя свойства" как ключ свойства, являющийся строкой. Но не полагайтесь на него, сама спецификация немного непоследовательна, а метод Object.keys возвращает массив имен свойств свойство keys не существовало до того, как эта терминология была добавлена ​​в ES 2015. А затем они добавили метод keys к массивам, который возвращает итератор чисел [индексы в массиве]. Fun fun fun... :-)) )

Все операции ES5 и более ранних версий, которые возвращали или зацикливались на именах свойств, были указаны в ES2015 для игнорирования свойств с символьными ключами. Итак, for-in, Object.keys, Object.getOwnPropertyNames все смотрят только на свойства со строковыми ключами. ES2015 добавил Reflect.ownKeys (который включает в себя как строки, так и символы) и Object.getOwnPropertySymbols (который включает только собственные свойства с символьным ключом).


Примечание:

Кажется, что для некоторых объектов d он вычисляет s = d.toString() и использует это значение s в качестве ключа свойства...

Не просто объекты, это больше похоже на String(d) (хотя, если d является объектом, то это одно и то же). Все, что не является строкой или символом, преобразуется в строку.

... Но для других объектов d (например, когда d является символом) он действительно использует значение символа в качестве ключа.

Символы не являются объектами, Символ — это примитивный тип. Но да, если ключ свойства является символом, он используется напрямую, а не преобразуется в строку.

person T.J. Crowder    schedule 21.12.2019
comment
Еще раз большое спасибо... Два примечания также очень полезны. - person peter.petrov; 21.12.2019