【问题标题】:Why does this really tricky computed property name function works the way it does?为什么这个非常棘手的计算属性名称函数会以它的方式工作?
【发布时间】:2019-02-07 08:06:56
【问题描述】:

@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,而 increment 中的值 x 不断增加。

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

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

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

【问题讨论】:

    标签: javascript ecmascript-6 computed-properties


    【解决方案1】:

    让我们评估以下对象字面量:

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

    【讨论】:

      【解决方案2】:

      让我们稍微改变一下例子来淡化复杂性。下面的例子和每次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,它返回的值是1,所以“计算”属性是"1"。第二次调用func,返回值为2,我们尝试访问它并返回undefined,因为没有属性"2"。

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

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

      【讨论】:

      • ...实际上相当于weirdObjectLiteral["2"];因此undefined。但总的来说,我喜欢这个解释。 )
      • ooooooh 这是因为您正在重置它的 toString 功能!!! d'oh,好的,现在这对我来说很有意义!
      • 嘿,这就是让我点击的答案,我们正在为该对象覆盖toString - 如果您编辑它以使其更准确(根据raina77ow的评论),我会接受这个。
      猜你喜欢
      • 2012-06-15
      • 2018-01-01
      • 2022-11-20
      • 2021-03-29
      • 2018-12-18
      • 2019-07-09
      • 1970-01-01
      • 2012-01-07
      • 1970-01-01
      相关资源
      最近更新 更多