我正在寻找一些很好的综合阅读材料,以了解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?
}


如果我想对一个对象进行完全独立的复制(什么都没有引用),那么最佳的做法是什么?

#1 楼

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


JavaScript总是按值传递,但是当变量引用对象(包括数组)时,“ value”是对值的引用物体。
更改变量的值永远不会更改基础原语或对象,它只会将变量指向新的原语或对象。
但是,更改变量引用的对象的属性确实会更改基础对象。

因此,通过一些示例进行研究:

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"


评论


虽然从技术上讲是正确的,但我还是想说JavaScript是“按对象共享”。它避免了这种混乱,并转为“高级”视图。

–user166390
2011年7月7日在4:19

您指的是什么“困惑”?对我来说,“按价值传递”非常清楚。

– MEMark
2014年6月24日19:56

更改变量的值永远不会更改基础基元或对象。但是,更改变量引用的对象的属性确实会更改基础对象。这两个句子加在一起消除了很多疑问。谢谢!

–阿米托玛(Amit Tomar)
16年1月18日在5:50

来自c,这是愚蠢而烦人的state状态state ...

–拉斐尔
16-4-27在13:51



var users = [1,2,3,4]; var x_users =用户; x_users.push(5);现在,用户和x_users相同,因为它是通过引用传递的。解决此问题的一种方法是var x_users = users.slice(0); x_users.push(6);现在,用户和x_users有所不同,因为x_users并未引用用户。花了我一段时间才弄清楚:-)希望这可以对某人有所帮助。

–ralixyle
17年8月28日在8:09



#2 楼

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 = ...


评论


数组是一个对象,因此也会发生变化。

–shyammakwana.me
16年4月13日在8:14

除了JavaScript中几乎所有的东西都是对象。

–Hritik
18年11月24日在14:26

@Hritik-除了不是对象的所有原始值之外。

– nnnnnn
18/12/1在21:23

#3 楼

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

但是,我想您已经在SO上阅读了它;在这里您拥有所需的文档:

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

评论


虽然从技术上讲是正确的,但我还是想说JavaScript是“按对象共享”。它避免了这种混乱,并转为“高级”视图。

–user166390
2011年7月7日在4:20

我做了一个小提琴玩这个:jsfiddle.net/tkane2000/7weKS/1

–tkane2000
2014年5月22日13:54

#4 楼


诸如字符串,数字之类的原始类型变量始终按值传递。

基于这两个条件,数组和对象按引用传递或按值传递。



如果要使用新的Object或Array更改该Object或数组的值,则按值传递。

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


评论


只需将上面的代码放在您的控制台中,然后看……价值得到了改变。

– Mukund Kumar
14-10-28在19:13

关于调用Javascript的“通过引用传递”的说法,在术语方面存在着很长时间的争论。我倾向于避开争论,并称JS对对象和数组的作用是“按指针传递”。数组和对象总是通过指针传递。如果您修改了传入的内容(访问指针),则原始文件将被修改。如果将不同的数组或对象分配给指针变量,则原始变量不会被修改,因为您的变量现在“指向”另一个数组或对象。其中大部分是“术语辩论”,因为没有关于实际发生情况的辩论。

– jfriend00
14-10-28在20:09

@newacct-对于那些喜欢声称“一切都是通过价值传递”的人,无论您在技术上有多正确,您可能都认为自己处于某个水平,但这并不能帮助新手以任何方式理解该问题。任何好的解释都必须解释对象和基元如何传递之间的差异,以便新手理解实际使用中的差异,因为这样的问答目标是一个明确的解释,可以供那些不愿意这样做的人使用。了解更详细的实施细节或术语的技术含义。

– jfriend00
2014-10-28 20:16



@ jfriend00:问题是,“通过”没有什么特别的。它的工作方式与分配或任何其他操作相同。一旦您了解所有值都是原始值或指向对象的指针,并且不将“对象”作为值本身来谈论,那么就不会造成混淆。另外,JavaScript中传递和分配的语义与Java中的相同。而且,“一切都是通过值传递”,这几乎是在StackOverflow上通常描述Java的方式,并结合所有非基本体是对象的指针的解释。

– newacct
14-10-30在8:13



@newacct-这里的重点是向不是高级开发人员的人们解释,并说一切都是“通过价值传递”,而没有太多的解释,仅仅是不够的。它没有解释基本数据和数组的传递或分配方式的区别。您可以说这在技术上是正确的,但是对于不老练的开发人员来说,这是一个不足的解释。您对“一旦您了解...”的评论表明,仅当您了解其他事物时,这还不够。因此,您不能只对新手说“一切都是通过价值传递的”。

– jfriend00
2014年10月30日在8:31