JavaScript 按引用与按值

IT技术 javascript reference pass-by-reference pass-by-value
2021-02-06 19:27:59

我正在寻找一些很好的综合阅读材料,了解 JavaScript 何时按值传递某些内容,何时按引用传递,以及何时修改传递的项目会影响函数外的值,何时不会。我也感兴趣的是,当分配给另一个变量时是按引用还是按值,以及这是否遵循与作为函数参数传递不同的规则。

我已经做了很多搜索并找到了很多具体的例子(其中很多都在 SO 上),从中我可以开始拼凑出真正的规则,但我还没有找到一个单独的、写得很好的文档描述了这一切。

另外,语言中有没有办法控制某些东西是通过引用还是通过值传递?

以下是我想了解的一些问题类型。这些只是示例 - 我实际上希望了解语言所遵循的规则,而不仅仅是特定示例的答案。但是,这里有一些例子:

function f(a,b,c) {
   a = 3;
   b.push("foo");
   c.first = false;
}

var x = 4;
var y = ["eeny", "miny", "mo"];
var z = {first: true};
f(x,y,z);

对于所有不同类型,x、y 和 z 的内容何时在 f 的范围之外发生变化?

function f() {
    var a = ["1", "2", "3"];
    var b = a[1];
    a[1] = "4";
    // what is the value of b now for all possible data types that the array in "a" might hold?
}

function f() {
    var a = [{yellow: "blue"}, {red: "cyan"}, {green: "magenta"}];
    var b = a[1];
    a[1].red = "tan";
    // what is the value of b now and why?
    b.red = "black";
    // did the value of a[1].red change when I assigned to b.red?
}

如果我想制作一个对象的完全独立的副本(没有任何引用),那么最好的做法是什么?

4个回答

我的理解是,这实际上很简单:

  • Javascript总是按值传递,但是当变量引用对象(包括数组)时,“值”就是对对象的引用。
  • 改变一个变量的值永远不会改变底层的原语或对象,它只是将变量指向一个新的原语或对象。
  • 但是,更改变量引用的对象属性确实会更改底层对象。

因此,要完成您的一些示例:

function f(a,b,c) {
    // Argument a is re-assigned to a new value.
    // The object or primitive referenced by the original a is unchanged.
    a = 3;
    // Calling b.push changes its properties - it adds
    // a new property b[b.length] with the value "foo".
    // So the object referenced by b has been changed.
    b.push("foo");
    // The "first" property of argument c has been changed.
    // So the object referenced by c has been changed (unless c is a primitive)
    c.first = false;
}

var x = 4;
var y = ["eeny", "miny", "mo"];
var z = {first: true};
f(x,y,z);
console.log(x, y, z.first); // 4, ["eeny", "miny", "mo", "foo"], false

示例 2:

var a = ["1", "2", {foo:"bar"}];
var b = a[1]; // b is now "2";
var c = a[2]; // c now references {foo:"bar"}
a[1] = "4";   // a is now ["1", "4", {foo:"bar"}]; b still has the value
              // it had at the time of assignment
a[2] = "5";   // a is now ["1", "4", "5"]; c still has the value
              // it had at the time of assignment, i.e. a reference to
              // the object {foo:"bar"}
console.log(b, c.foo); // "2" "bar"
你指的是什么“混乱”?对我来说,“按值传递”是非常清楚的。
2021-03-09 19:27:59
var users = [1,2,3,4]; var x_users = users; x_users.push(5);现在 users 和 x_users 是一样的,因为它是通过引用传递的。解决此问题的一种方法是 var x_users = users.slice(0); x_users.push(6);现在 users 和 x_users 不同,因为 x_users 不是指用户。我花了一段时间才弄清楚:-) 希望这可以帮助某人。
2021-03-09 19:27:59
虽然从技术上讲是正确的,但我更愿意说 JavaScript 是通过对象共享它避免了这种混淆并转向“高级”视图。
2021-03-22 19:27:59
Changing the value of a variable never changes the underlying primitive or object. However, changing a property of an object referenced by a variable does change the underlying object.这两句话放在一起,解开了很多疑惑。谢谢!
2021-03-30 19:27:59
来自c,这是愚蠢和烦人的,状态状态状态......
2021-04-06 19:27:59

Javascript总是按值传递。但是,如果将对象传递给函数,则“值”实际上是对该对象的引用,因此该函数可以修改该对象的属性,但不会导致函数外部的变量指向某个其他对象

一个例子:

function changeParam(x, y, z) {
  x = 3;
  y = "new string";
  z["key2"] = "new";
  z["key3"] = "newer";

  z = {"new" : "object"};
}

var a = 1,
    b = "something",
    c = {"key1" : "whatever", "key2" : "original value"};

changeParam(a, b, c);

// at this point a is still 1
// b is still "something"
// c still points to the same object but its properties have been updated
// so it is now {"key1" : "whatever", "key2" : "new", "key3" : "newer"}
// c definitely doesn't point to the new object created as the last line
// of the function with z = ...
除了 JavaScript 中的几乎所有东西都是对象。
2021-03-17 19:27:59
@Hritik - 除了所有不是对象的原始值。
2021-03-21 19:27:59
Array 是一个对象,所以也会改变。
2021-04-07 19:27:59

是的,Javascript 总是按值传递,但在数组或对象中,值是对它的引用,因此您可以“更改”内容。

但是,我认为您已经在 SO 上阅读了它;在这里你有你想要的文件:

http://snook.ca/archives/javascript/javascript_pass

虽然从技术上讲是正确的,但我更愿意说 JavaScript 是通过对象共享它避免了这种混淆并转向“高级”视图。
2021-03-26 19:27:59
我做了一个小提琴来玩这个:jsfiddle.net/tkane2000/7weKS/1
2021-04-01 19:27:59
  1. 像字符串、数字这样的原始类型变量总是按值传递。
  2. 数组和对象根据这两个条件作为按引用传递或按值传递。

    • 如果您正在使用新对象或数组更改该对象或数组的值,则它是按值传递的。

      object1 = {item: "car"}; array1=[1,2,3];

    在这里,您将新对象或数组分配给旧对象。您没有更改旧对象的属性值。因此它是按值传递的。

    • 如果您正在更改对象或数组的属性值,则它是通过引用传递的。

      object1.item= "car"; array1[0]=9;

    在这里,您正在更改旧对象的属性值。您没有将新对象或数组分配给旧对象。因此它是通过引用传递的。

代码

    function passVar(object1, object2, number1) {

        object1.key1= "laptop";
        object2 = {
            key2: "computer"
        };
        number1 = number1 + 1;
    }

    var object1 = {
        key1: "car"
    };
    var object2 = {
        key2: "bike"
    };
    var number1 = 10;

    passVar(object1, object2, number1);
    console.log(object1.key1);
    console.log(object2.key2);
    console.log(number1);

Output: -
    laptop
    bike
    10
关于调用 Javascript 的“通过引用传递”的功能,有一个由来已久的术语辩论。我更喜欢回避争论,称 JS 对对象和数组的作用是“通过指针传递”。数组和对象总是通过指针传递。如果您修改传入的内容(访问指针),则会修改原始内容。如果您将不同的数组或对象分配给指针变量,则不会修改原始数组或对象,因为您的变量现在“指向”不同的数组或对象。其中大部分是“术语辩论”,因为没有关于实际发生的事情的辩论。
2021-03-09 19:27:59
@newacct - 对于那些喜欢声称“一切都是通过value传递”的人来说,无论您认为自己在技术上多么正确,这都无助于新手以任何方式理解问题。任何好的解释都必须解释对象和原语如何传递之间的区别,以便新手了解实际使用中的区别,因为像这样的问答中的目标是一个清晰的解释,可以用于那些不知道的人t 了解更精细的实现细节或术语的技术含义。
2021-03-15 19:27:59
@jfriend00:问题是,“通过”并没有什么特别之处。它的工作方式与分配或任何其他操作相同。一旦你理解了所有的值要么是原语,要么是指向对象的指针,而不是把“对象”当作值本身来讨论,那么就没有混淆了。此外,JavaScript 中传递和赋值的语义与 Java 中的相同。“一切都是按值传递的”几乎是 StackOverflow 上对 Java 的一般描述,并结合解释所有非基元是如何指向对象的指针。
2021-03-17 19:27:59
只需将上面的代码放在您的控制台中,然后看看...值发生了变化..
2021-03-21 19:27:59
@newacct - 这里的重点是向不熟练的开发人员解释并说一切都是“通过value传递”而没有更多解释只是不够。它没有解释基元和数组如何传递或分配的区别。您可以争辩说它在技术上是正确的,但对于不熟练的开发人员来说,这是一个不充分的解释。您对“一旦理解……”的评论表明,在您理解其他事物之前,这还不够-因此,您不能对新手说“一切都是通过value传递的”并完成。
2021-03-31 19:27:59