为什么这个非常棘手的计算 属性 名称函数会按照它的方式工作?

Why does this really tricky computed property name function works the way it does?

@raina77ow 最近帮我算出了计算出的 属性 名字。作为 的一部分,他们分享了一段非常棘手的代码,展示了 JavaScript 有趣的方面:​​

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

当我 运行 在节点 CLI 中采样时,最后一行不断输出 undefined,而 increment 中的值 x 不断递增。

如果我们将 const movingTarget = { toString: increment }; 替换为 const movingTarget = { [toString]: increment };,此行为将停止发生,取而代之的是我们得到 42 的输出和 [=14] 中的 x =] 保持不变。

有人可以帮我理解为什么会这样吗? JavaScript 是什么让事情以这种方式运作?

相关问题increment 中的函数中的 x 在我们从内存中明确删除 increment 之前是否存在?

让我们评估以下对象文字:

 {[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 }
 );

让我们通过稍微改变示例来稍微简化复杂性。下面的例子基本上与每次计算 movingTarget 时调用 toString 相同,所以我们将摆脱它并自己调用函数:

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

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

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

看到了吗?我们第一次调用 func 时,它 return 的值是 1,所以 "computed" 属性 是 "1"。在我们第二次调用 func 时,returned 值为 2,我们尝试访问它并返回 undefined 因为没有 属性 "2".

这与问题中的示例有什么关系?

它是相关的,因为在原始代码中我们使用 movingTarget 作为计算的 属性 的值和访问 属性 的键。由于它们都需要字符串,因此 movingTarget 通过调用其 toString 方法被强制转换为字符串。这个 toString 方法被定义为一个增加 x 和 return 其新值的函数(即由 IIFE 编辑的内部函数 return,函数 () => ++x).所以基本上每当我们使用 movingTarget 作为计算的 属性 值或键时,都会调用该函数并使用其 return 值。