【问题标题】:Javascript - primitive vs reference types [duplicate]Javascript - 原始类型与引用类型
【发布时间】:2015-02-05 02:30:57
【问题描述】:

在下面的代码中,我们传递了一个对象。因此,根据 javascript,我们正在传递一个引用并进行操作。

var a = new Number(10);
x(a);
alert(a);

function x(n) {
n = n + 2;
}

但是警报的是 10 而不是 12。为什么?

【问题讨论】:

标签: javascript pass-by-reference


【解决方案1】:
var a = new Number(10);    
x(a);
alert(a);

function x(n) {
    n = n + 2; // NOT VALID as this would essentially mean 10 = 10 + 2 since you are passing the 'value' of a and not 'a' itself
}

您需要编写以下内容才能使其正常工作

var a = new Number(10);
x(a);
alert(a);

function x(n) {
    a = n + 2; // reassign value of 'a' equal to the value passed into the function plus 2
}

【讨论】:

  • 我不是在寻找有效的代码。你的第一组解释“10 = 10 + 2”是不对的。
  • 此等式无效。因为左边是一个数字
  • 对。你是对的。
【解决方案2】:

JavaScript 参数传递的工作方式与 Java 类似。单个值通过值传递,但对象属性通过指针值通过引用传递。值本身不会在函数中被修改,但对象的属性会被修改。

考虑以下代码:

function doThis(param1, param2) {
    param1++;
    if(param2 && param2.value) {
        param2.value++;
    }
}

var initialValue = 2;
var initialObject = {value: 2};

doThis(initialValue, initialObject);

alert(initialValue); //2
alert(initialObject.value); //3

http://jsfiddle.net/bfm01b4x/

【讨论】:

【解决方案3】:

让我试着用例子来回答:

function modify(obj) {
    // modifying the object itself
    // though the object was passed as reference
    // it behaves as pass by value
    obj = {c:3};
}

var a = {b:2}
modify(a);
console.log(a)
// Object {b: 2}

function increment(obj) {
    // modifying the value of an attribute
    // working on the same reference
    obj.b = obj.b + 1;
}

var a = {b:2}
increment(a);
console.log(a)
// Object {b: 3}

function augument(obj) {
    // augument an attribute
    // working on the same reference
    obj.c = 3;
}

var a = {b:2}
augument(a);
console.log(a)
// Object {b: 2, c: 3}

请参考JSFiddle 进行工作演示。

【讨论】:

    【解决方案4】:

    nx 的本地地址,首先它被设置为与全局a 相同的引用。然后将右侧n + 2 评估为一个数字(原始)。 分配的左侧n,永远不会被评估,它只是一个标识符。所以我们的局部变量现在设置为右侧的原始值。 a 引用的值实际上从未被修改过。见

    var a = new Number(10);
    x(a);
    alert(a);  // 10
    
    function x(n) {
      alert(typeof n);  // object
      n = n + 2;
      alert(typeof n);  // number
    }
    

    【讨论】:

      【解决方案5】:

      当你计算时

      n + 2
      

      即使n 确实是Number 对象实例,这也会产生一个新的“本机数字”。

      分配给n 然后只更改局部变量n 引用的内容,而不更改Number 对象实例。你可以看到

      n = new Number(10);
      console.log(typeof n);     // ---> "object"
      console.log(n + 2);        // ---> 12
      console.log(typeof (n+2)); // ---> "number"
      n = n + 2;
      console.log(typeof n);     // ---> "number"
      

      在 Javascript(或 Python 或 Lisp)中,无法传递变量的“地址”,以便被调用的函数对其进行变异。你唯一能做的就是传递一个 setter 函数......例如:

      function foo(setter) {
          setter(42);
      }
      
      funciton bar() {
          var x = 12;
          foo(function(newx){x = newx;});
          console.log(x); // ---> 42
      }
      

      【讨论】:

      • 所以数字是不可变的,对吧?如果x = new Number(5)x = 5,则不存在使x 保持相同引用但具有不同值的操作。当我执行console.log(new Number(5)) 时,它会给出Number {[[PrimitiveValue]]: 5}。但我假设你不能改变 [[PrimitiveValue]],对吧?
      • @soktinpk:是的,数字和字符串在 Javascript 中是不可变的。但是,您可以使用 valueOf() 方法创建一个对象,该方法的行为类似于数学表达式中的数字,但具有您可以控制的可变状态。
      【解决方案6】:

      答案相当简单:因为 ECMAScript 是按值传递而不是不是按引用传递,而您的代码证明了这一点。 (更准确地说,是call-by-sharing,是一种特殊的传值方式。)

      请参阅Is JavaScript a pass-by-reference or pass-by-value language? 了解更多信息。

      ECMAScript 使用按值传递,或者更准确地说,是按值传递的一种特殊情况,其中传递的值始终 是一个指针。这种特殊情况有时也称为 call-by-sharing、call-by-object-sharing 或 call-by-object。

      Java(用于对象)、C#(默认情况下用于引用类型)、Smalltalk、Python、Ruby 以及几乎所有创建的面向对象语言都使用相同的约定。

      注意:某些类型(例如)Numbers 实际上是通过值直接传递的,而不是通过中间指针传递的。但是,由于它们是不可变的,在这种情况下,按值传递和按对象共享之间没有可观察到的行为差异,因此您可以通过简单地处理所有内容来大大简化您的心智模型作为按对象共享调用。只需将这些特殊情况解释为您无需担心的内部编译器优化即可。

      这是一个简单的示例,您可以运行它来确定 ECMAScript(或任何其他语言,在您翻译后)的参数传递约定:

      function isEcmascriptPassByValue(foo) {
        foo.push('More precisely, it is call-by-object-sharing!');
        foo = 'No, ECMAScript is pass-by-reference.';
        return;
      }
      
      var bar = ['Yes, of course, ECMAScript *is* pass-by-value!'];
      
      isEcmascriptPassByValue(bar);
      
      console.log(bar);
      // Yes, of course, ECMAScript *is* pass-by-value!,
      // More precisely, it is call-by-object-sharing!

      如果你熟悉 C#,这是了解值类型和引用类型的值传递和引用传递之间的区别的一个很好的方法,因为 C# 支持所有 4 种组合:值类型的值(“传统的值传递”),引用类型的值传递(共享调用、对象调用、对象共享调用,如 ECMAScript 中)、传递-引用类型的按引用,值类型的按引用。

      (实际上,即使您了解 C#,这也不难。)

      struct MutableCell
      {
          public string value;
      }
      
      class Program
      {
          static void IsCSharpPassByValue(string[] foo, MutableCell bar, ref string baz, ref MutableCell qux)
          {
              foo[0] = "More precisely, for reference types it is call-by-object-sharing, which is a special case of pass-by-value.";
              foo = new string[] { "C# is not pass-by-reference." };
      
              bar.value = "For value types, it is *not* call-by-sharing.";
              bar = new MutableCell { value = "And also not pass-by-reference." };
      
              baz = "It also supports pass-by-reference if explicitly requested.";
      
              qux = new MutableCell { value = "Pass-by-reference is supported for value types as well." };
          }
      
          static void Main(string[] args)
          {
              var quux = new string[] { "Yes, of course, C# *is* pass-by-value!" };
      
              var corge = new MutableCell { value = "For value types it is pure pass-by-value." };
      
              var grault = "This string will vanish because of pass-by-reference.";
      
              var garply = new MutableCell { value = "This string will vanish because of pass-by-reference." };
      
              IsCSharpPassByValue(quux, corge, ref grault, ref garply);
      
              Console.WriteLine(quux[0]);
              // More precisely, for reference types it is call-by-object-sharing, which is a special case of pass-by-value.
      
              Console.WriteLine(corge.value);
              // For value types it is pure pass-by-value.
      
              Console.WriteLine(grault);
              // It also supports pass-by-reference if explicitly requested.
      
              Console.WriteLine(garply.value);
              // Pass-by-reference is supported for value types as well.
          }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2010-12-06
        • 2013-05-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多