为什么这个非常棘手的计算 属性 名称函数会按照它的方式工作?
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 值。
@raina77ow 最近帮我算出了计算出的 属性 名字。作为
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 值。