【问题标题】:Why is an expression in an object initializer not re-evaluated each time the property is read?为什么每次读取属性时都不会重新评估对象初始值设定项中的表达式?
【发布时间】:2014-09-10 06:08:49
【问题描述】:

2014 年 7 月 15 日对MDN guide on working with objects 的修订指出:

如果在顶级脚本中使用对象初始值设定项创建对象,JavaScript 会在每次计算包含对象字面量的表达式时解释该对象。

但是,在下面的 sn-p 中,当评估 objLit2.val2 时,它不使用最后设置的值,应该是 10100;它改为使用1000,这是在定义objLit2 时声明的值。为什么会这样?

var i = 1000;

function iPlus3() {
  alert("iPlus3");
  
  return i + 3;
}

var objLit2 = {
  val: iPlus3,
  val2: i = i + 1
};

function setValue() {
  i = 10;
  console.log("objLit2Val1", objLit2.val(), objLit2.val2); // Outputs 13 1001 and not 13 11
  
  i = 100;
  console.log("objLit2Val2", objLit2.val(), objLit2.val2); // Outputs 103 1001 and not 103 101
  
  // If `val` is interpreted at the time of the call, why is `val2` not also interpreted on each call?
}

setValue();

【问题讨论】:

    标签: javascript function javascript-objects


    【解决方案1】:

    objLit2 是顶级声明。因此,它会在您的脚本首次执行时进行评估。评估后,属性objLit2.val2 将设置其值。除非您有意识地更改属性 objLit2.val2 的值,否则它不会仅仅因为您在代码中的其他位置引用 objLit2 而获得不同的值。

    一旦评估了objLit2,属性objLit2.val2 将包含一个原始数字,该数字不再与变量i 有任何联系。 objLit2.val2 的值是独立的,它的值不会受到任何其他变量的更改的影响。

    javascript 中的基元(如数字和布尔值)存储为与任何其他变量无关的不同值。 javascript 中的对象存储为对原始对象的引用。由于objLit2.val2 是一个数字,它只是一个独立的值。

    所以,你可以这样做:

    var i = 1000;
    var objLit2 = { val : iPlus3, val2 : i = i + 1 };
    console.log(objLit2.val2);    // shows 1001
    i += 1000;
    console.log(objLit2.val2);    // still shows 1001
    

    另一方面,对象存储为对原始对象的引用。所以,如果你这样做:

    var indexes = [1,2,3];
    var objLit2 = { val : indexes, val2 : i = i + 1 };
    console.log(objLit2.indexes);    // shows [1,2,3]
    indexes[0] = 0;
    console.log(objLit2.indexes);    // shows [0,2,3]
    

    因为数组是一个对象,所以当您在 objLit2 文字中指定数组时,它只是存储对该数组的引用(而不是数组的单独副本)。如果您更改该原始数组,您将在包含对该数组的引用的所有其他变量中看到该更改。


    您所引用的内容将适用于在某个范围内(例如在函数内部)声明的对象,因为每次创建该范围时(例如每次运行函数时)都会重新评估它们。

    【讨论】:

    • 在 val2 的情况下,它是存储的值(即)1001,在 val 的情况下,它是指向存储的函数的指针。当“JavaScript 每次 to 评估表达式时都会解释对象”objLit2,1001 仅保留 1001,但是在两次调用中,同一位置引用的函数的值分别为 13 和 103。
    【解决方案2】:

    在您的初始化程序中,i 递增一次,然后将其值存储在 objLit2.val2 中。由于i 是一个原始类型——一个数字——objLit2.val2 是一个与i 不同的变量。

    这与一个对象被设置为等于另一个对象的情况形成对比。

    var objLit3 = objLit2;
    objLit2.val2 = 5;
    console.log(objLit2.val2); // Also 5
    

    在此处的示例中,objLit3 持有对同一底层对象的新引用,而不是创建新的实例。数字不会发生这种情况。每个分配了数字的新变量都是内存中的一个新实例。

    【讨论】:

      【解决方案3】:

      当 JavaScript 创建对象字面量时,键具有一定的价值。该值可以是函数、数字等...在您的情况下,您正在提供要立即评估该值的内容...例如,这在某种程度上等同于正在发生的事情:

      var i = 1000;
      
      function demo() {
          return i = i + 1;
      }
      
      var objLit2 = {
          val2: demo()
      };
      

      【讨论】:

        【解决方案4】:

        让我们逐步介绍一下您的代码的摘要版本。右侧显示的是每个语句的变量中存储的内容。

        var i = 1000;                                   // i = 1000
        var objLit2 = { val: iPlus3, val2: i = i + 1 }; // i = 1001, objLit2.val2 = 1001
        
        i = 10;                                         // i = 10, objLit2.val2 = 1001
        innerHTML = (i + 3).toString() + ' ' + '1001';  // Outputs: 13 1001
        i = 100;                                        // i = 100, objLit2.val2 = 1001
        innerHTML = (i + 3).toString() + ' ' + '1001';  // Outputs: 103 1001
        

        您似乎认为声明val2 : i = i + 1 意味着对i 的引用分配给val2。情况并非如此,因为 javascript 中的 number 类型是 immutable。这意味着当一个对象(如iobjLit2)被分配一个数字时,它将始终存储该数字的值而不是引用。

        例如:

        var i, val2;
        i = 1000;  // i = 1000
        i = i + 1; // i = 1001
        val2 = i;  // i = 1001, val2 = 1001
        val2++;    // i = 1001, val2 = 1002
        

        注意以下与对象类型有关的内容(这种方式是因为它分配的是object 类型而不是number 类型):

        var objectType  = { numType: 14 }, // objectType  = { numType: 14 }
            otherObject = objectType;      // otherObject = &objectType; (reference to)
        
        // So the following statement...
        otherObject.numType = 15;          // objectType  = { numType: 15 };
        // ...is equivalent to:            // otherObject = &objectType; (reference to)
        objectType.numType = 15;
        // since:
        otherObject == objectType;
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2018-06-19
          • 2023-03-05
          • 2011-02-16
          • 1970-01-01
          • 1970-01-01
          • 2016-03-02
          • 2012-11-03
          • 1970-01-01
          相关资源
          最近更新 更多