Почему средство доступа к свойствам JavaScript позволяет использовать произвольно вложенный массив, содержащий одно имя свойства?

avatar
smac89
9 августа 2021 в 05:12
44
1
1

Только что ответил на вопрос и немного озадачен тем, почему в JavaScript работает следующее:

const source = {
  last: "Capulet"
};

const srcKeys = Object.keys(source);

console.log(source[srcKeys]);
console.log(source[[[[srcKeys]]]]);
console.log(source[[],[[[srcKeys]]]]);
console.log(source[[],[[srcKeys]]]);
console.log(source[[[]],[],srcKeys]);
console.log(source[[[]],[[srcKeys]]]);

В попытке понять, насколько глубока кроличья нора, я обнаружил, что следующее не работает:

const source = {
  last: "Capulet"
};

const srcKeys = Object.keys(source);

console.log(source[[[],[[srcKeys]]]]);
console.log(source[[[],[],[[srcKeys]]]]);
console.log(source[[[[]],[[srcKeys]]]]);

В чем здесь логика?

Источник

Ответы (1)

avatar
smac89
9 августа 2021 в 05:12
1

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

Оператор запятой

Это не было сразу очевидно из моего тестирования, но весьма полезно понять примеры, которые сработали.

console.log(source[srcKeys]);
console.log(source[[[[srcKeys]]]]);
console.log(source[[],[[[srcKeys]]]]);
console.log(source[[],[[srcKeys]]]);
console.log(source[[[]],[],srcKeys]);
console.log(source[[[]],[[srcKeys]]]);

Вы заметите, что на самом деле существует шаблон, который изначально не был очевиден в этих утверждениях.

Каждое средство доступа к свойству является либо выражением с запятой (которое будет оцениваться как последнее значение в выражении — обычно это массив), либо просто массивом.

console.log(source[srcKeys]); // => array
console.log(source[[[[srcKeys]]]]); // => array
console.log(source[[],[[[srcKeys]]]]); // => comma expression
console.log(source[[],[[srcKeys]]]); // => comma expression
console.log(source[[[]],[],srcKeys]); // => comma expression
console.log(source[[[]],[[srcKeys]]]); // => comma expression

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

Вооружившись этими знаниями, пришло время попытаться понять, как спецификация ECMAScript излагает оценку property accessors.

.

Аксессор свойства

Я избавлю вас от кровавых подробностей и вместо этого дам краткое изложение того, что происходит почти в 99% случаев:

Выражение свойства сначала преобразуется в строку (если это не символ начиная с es6), затем возвращается ссылка на значение, которое имеет данное имя свойства


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

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

Не очень секретное открытие

Давайте теперь применим все, что мы знаем, к приведенным примерам и посмотрим, почему некоторые из них сработали, а некоторые нет.

Сначала рассмотрим примеры, которые не сработали:

console.log(source[[[],[[srcKeys]]]]); // => property accessor expression is [[],[[srcKeys]]]
console.log(source[[[],[],[[srcKeys]]]]); // => property accessor expression is [[],[],[[srcKeys]]]
console.log(source[[[[]],[[srcKeys]]]]); // => property accessor expression is [[[]],[[srcKeys]]]

Одной неизменной деталью каждого из этих выражений является то, что все они являются массивами из нескольких элементов. Быстро становится очевидным, почему они не дали нам ожидаемого значения, а предыдущие дали. Для этого я сейчас покажу результат toString для всех массивов.

const source = {
  last: "Capulet"
};

const srcKeys = Object.keys(source);

// console.log(source[srcKeys]);
console.log(srcKeys.toString());

// console.log(source[[[[srcKeys]]]]);
console.log([
  [
    [srcKeys]
  ]
].toString());

// console.log(source[[],[[[srcKeys]]]]);
console.log(([], [
  [
    [srcKeys]
  ]
]).toString());

// console.log(source[[],[[srcKeys]]]);
console.log(([], [
  [srcKeys]
]).toString());

// console.log(source[[[]],[],srcKeys]);
console.log(([
  []
], [], srcKeys).toString());

// console.log(source[[[]],[[srcKeys]]]);
console.log(([
  []
], [
  [srcKeys]
]).toString());

// console.log(source[[[],[[srcKeys]]]]);
console.log([
  [],
  [
    [srcKeys]
  ]
].toString());

// console.log(source[[[],[],[[srcKeys]]]]);
console.log([
  [],
  [],
  [
    [srcKeys]
  ]
].toString());

// console.log(source[[[[]],[[srcKeys]]]]);
console.log([
  [
    []
  ],
  [
    [srcKeys]
  ]
].toString());

И вот оно. Первые сработавшие примеры имеют значение toString last, а остальные нет.

Yousaf
9 августа 2021 в 05:45
0

Сначала я подумал, что сглаживание вложенных массивов — интересное поведение. Я ожидал, что преобразование массива в строку: [[],[[srcKeys]]] выведет: [,[srcKeys]], НО затем я понял, что массив преобразуется в строку, вызывая toString для каждого элемента массива.

smac89
9 августа 2021 в 05:53
0

@Юсаф, верно. Преобразование массивов в строки в JS дает тот же результат, что и array.join(',').