@ raina77ow最近帮助我弄清楚了计算出的属性名称。作为their answer to my question的一部分,他们共享了一些非常棘手的代码,这些代码展示了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
是否存在?
答案 0 :(得分:3)
让我们评估以下对象文字:
{[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 }
);
答案 1 :(得分:2)
让我们稍微改变一下例子,就可以使并发症减轻一点。以下示例基本上与每次对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
,因此“计算”属性为"1"
。在第二次调用func
时,返回的值是2
,我们尝试访问该值,并返回了undefined
,因为没有属性"2"
。
这与问题中的示例有什么关系?
这是相关的,因为在原始代码中,我们将movingTarget
用作计算属性的值和访问该属性的键。由于它们都期望使用字符串,因此通过调用movingTarget
方法将toString
强制为字符串。此toString
方法定义为递增x
并返回其新值的函数(即IIFE返回的内部函数,即函数() => ++x
)。因此,基本上,只要我们将movingTarget
用作计算的属性值或键,就会调用该函数并使用其返回值。