Почему эта действительно сложная вычисляемая функция имени свойства работает так, как работает? - PullRequest
0 голосов
/ 01 сентября 2018

@ raina77ow недавно помог мне выяснить имена вычисляемых свойств. Как часть их ответа на мой вопрос , они поделились действительно хитрым фрагментом кода, демонстрирующим интересные аспекты JavaScript:

const increment = (() => { let x = 0; return () => ++x })();
const movingTarget = { toString: increment };
const weirdObjectLiteral = { [movingTarget]: 42 };
console.log( weirdObjectLiteral[movingTarget] ); // undefined

Когда я запускаю этот образец в CLI узла, эта последняя строка постоянно выводит undefined, в то время как значение x в increment постоянно увеличивается.

Если мы заменим const movingTarget = { toString: increment }; на const movingTarget = { [toString]: increment };, это поведение прекратится, и вместо этого мы получим вывод 42, а x в increment останется прежним.

Может кто-нибудь помочь мне понять, почему это так? Что такого в JavaScript, который заставляет вещи работать таким образом?

Смежный вопрос : существует ли x в функции в increment, пока мы явно не удалим increment из памяти?

Ответы [ 2 ]

0 голосов
/ 01 сентября 2018

Давайте немного разбавим сложность, слегка изменив пример. Следующий пример - это то же самое, что toString вызывается каждый раз, когда movingTarget оценивается, поэтому мы просто избавимся от него и вызовем функцию сами:

let x = 0;
let func = () => ++x;

const weirdObjectLiteral = { [func()]: 42 };   // equivalent to weirdObjectLiteral = { "1": 42 }

console.log( weirdObjectLiteral[func()] );     // equivalent to weirdObjectLiteral["2"]

См? Когда мы в первый раз вызвали func, возвращаемое значение было 1, поэтому свойство computed - "1". Во второй раз, когда мы вызвали func, возвращаемое значение было 2, мы попытались получить к нему доступ и получили undefined назад, потому что нет свойства "2".

Как это связано с примером в вопросе?

Это связано с тем, что в исходном коде мы используем movingTarget как значение вычисляемого свойства и ключ для доступа к этому свойству. Поскольку оба они ожидают строки, movingTarget преобразуется в строку, вызывая ее метод toString. Этот метод toString определяется как функция, которая увеличивает x и возвращает свое новое значение (т.е. внутренняя функция, возвращаемая IIFE, функция () => ++x). Поэтому, в основном, всякий раз, когда мы использовали movingTarget в качестве вычисляемого значения свойства или ключа, эта функция вызывается и используется возвращаемое значение.

0 голосов
/ 01 сентября 2018

Давайте оценим следующий литерал объекта:

 {[toString]: increment }

toString - это идентификатор, указывающий на window.toString (функция). Как указано в ответе, toString будет вызываться при этом, поскольку ключи объекта всегда являются строками:

 {[toString.toString()]: increment }

Теперь это приводит к чему-то вроде:

 {["function() { [native code] }"]: increment }

Теперь, если мы вызовем toString() для этого объекта, стандарт Object.prototype.toString будет вызван в части {[movingTarget]: 42}, и в результате получится [Object object] (как всегда):

 let x = 0;
 let movingTarget = { ["function() { [native code] }"]: () => ++x };

 console.log(
  movingTarget.toString(), // [Object object]
  {[movingTarget]: 42} // {["[Object object]"]: 42}
 );

Вот почему движущаяся цель больше не движется. В исходном коде было установлено toString объекта, и он вызывается всякий раз, когда movingTarget превращается в строку:

 let x = 0;
 let movingTarget = { toString: () => ++x };
 console.log(
   movingTarget.toString(), // 1
   "" + movingTarget, // 2
  {[movingTarget]: 42} // { 3: 42 }
 );
...