为传递给JavaScript中的函数的对象分配新值

时间:2014-02-20 19:18:13

标签: javascript pass-by-reference pass-by-value

我是JavaScript新手(虽然在C ++方面经验丰富),今天我写了这样的东西:

function foo(bar) {
    bar = "something else";
}
var x = "blah";
foo(x);
alert(x); // Alerts with "blah", but I was expecting it to alert with "something else"

这让我很困惑,因为我一直在观看道格拉斯·克罗克福德的一些JavaScript视频,并记得他说“JavaScript总是通过参考传递”。

我可以解释这种情况的方式是JavaScript传递对象的引用,但这些引用被复制。这意味着在foo函数中,我正在为bar分配一个新的引用,然后超出范围,留下x未触及的引用。基本上我们从:

开始
x   ---->"blah"

然后,当foo被调用时,bar引用相同的数据:

x   ---->"blah"
bar -----^

因此,当“{else}”分配给bar时,会发生这种情况:

x   ---->"blah"
bar ---->"something else"

这是JavaScript中实际发生的事情的准确模型,还是我错过了其他内容?

作为一个额外的问题,有没有办法说,更改此变量引用的数据?这种情况经常出现,还是可以轻易避免?

修改

<{3}}中的道格拉斯·克罗克福德说“对象总是通过引用传递,它们不是通过值传递”,这是正确的,但函数的参数是通过值传递的,它只是通过值传递引用

6 个答案:

答案 0 :(得分:7)

你的解释是现实。

首先,您有一个名为x的变量,它是字符串对象的引用。假设内存为0x100。 x指向0x100,其中包含字节 blah

var x = "blah"; // x is 0x100 which references a string in memory

接下来,您将0x100传递给函数foo

function foo(bar) {
    bar = "something else";
}

由于 JavaScript中的所有内容都是按值传递的,即使是引用,JavaScript也会在内存中创建此引用的副本,现在在该函数中称为bar

foo(x); // Copies the value of x (a reference) to bar

此时,我们有两个独立的变量。 xbar。两个发生具有相同的值0x100。因此,如果您要更改其中任何一个引用的对象的属性,它将同时影响xbar

然而,做的是指定bar指向别的东西:

bar = "something else"; // Now references some other string we just created

现在,bar被重新分配以引用我们刚为其分配内存的新字符串。 bar不再具有0x100的值,它现在具有某个其他地址的值(例如0x500)。 x当然仍然的值为0x100,因为bar只是 x副本,而不是对x的引用。

出于这个原因,当你:

alert(x);

您仍然可以获得原始值,因为这是x指向的内容。

第二个问题:

  

有什么方法可以说,更改此变量引用的数据?   这种情况经常出现,还是可以轻易避免?

是的,只需将其包装在另一个对象中即可。例如:

var x = {Value: "blah"};
foo(x);

现在,我们引用了一个名为Value的属性的对象,该对象包含对某个内存中某个字符串的引用。

foo中,我们可以:

bar.Value = "something else";

这将影响Value的{​​{1}}属性,因为xbar都引用了同一个对象,而您从未更改其中任何一个的值。< / p>

换句话说,您不能重新分配您传入函数的引用,因为您只是重新分配副本。但是,您可以更改被引用对象的属性,因为该引用的其他副本都指向您正在更改的数据。

答案 1 :(得分:4)

您的解释是正确的。

您可以更改对象中键的值,这样可以执行类似于传递引用的操作:

function foo(bar) {
  bar.msg = "something else";
}
var x = { msg: "blah" };
foo(x);
alert(x.msg);

答案 2 :(得分:1)

  道格拉斯·克罗克福德在我看过的视频中说“对象总是如此   通过引用传递它们没有按值传递“,这是正确的,   但是函数的参数是通过值传递的,它只是   引用按值传递。

这不正确。所描述的内容恰恰称为按值传递。像Java一样,JavaScript只有pass-by-value。没有传递参考。

你的理解是正确的。 JavaScript中的每个值都是基元或引用(指向对象的指针)。对象本身永远不能直接成为值。传递或分配引用(指向对象的指针)时,指针的新副本将看到与原始指针相同的对象。但它们仍然是两个不同的指针变量。

答案 3 :(得分:0)

我知道你已经用你的编辑回答了你自己的问题......

  道格拉斯·克罗克福德在我看过的视频中说“对象总是通过引用来传递它们不是通过值传递”,这是正确的,但是函数的参数是通过值传递的,它只是通过值传递引用。 / p>

但是因为这个编辑让我突然意识到这是如何工作的...我承认我在这里苦苦挣扎的是一个代码示例和笔,我认为它真的可以证明它。

x = {};
y = x; //point y reference to same object x is pointed to

console.log(x === y) //true both pointed at same object

function funky(o) //pass by value a reference to object
{
  o = null; //change reference to point to null away from object
}

funky(x);

console.log(x)//not null still an object

var myObj = {
  value: "Hello"
};

function change(localObj) {//pass reference to object
  localObj.value = "Bye";//change property through the reference
  localObj = null;//point the reference to null away from object
}

console.log(x === y) //still same reference;
change(myObj);
console.log(myObj.value) // Prompts "Bye"
console.log(myObj) //not null - its an object;
x = myObj; //point x to different object
console.log(x === y) //false pointed at different object;
console.log(x);
console.log(y);

https://codepen.io/waynetheisinger/pres/YewMeN

答案 4 :(得分:-3)

数字,字符串等原始值不通过引用传递,只传递对象。例如:

var myObj = {
  value: "Hello"
};

function change(localObj) {
  localObj.value = "Bye";
}

change(myObj);
console.log(myObj.value) // Prompts "Bye"

答案 5 :(得分:-3)

“JavaScript始终通过引用传递”是一个 [白色]谎言混淆的术语。虽然有一些“灰色区域”,但我会去these definitions of evaluation strategies

以下是我的论点和推理。如果您持有不同的视图,请确保您至少可以支持它。

Call By Reference

按参考呼叫意味着(到许多人),分配给参数会影响调用者中的绑定。

  

在call-by-reference评估(也称为pass-by-reference)中,函数接收对用作参数的变量的隐式引用,而不是其值的副本。 这通常意味着该函数可以修改(即分配给)用作参数的变量 - 其调用者将看到的内容。

这是不是在JavaScript中的情况,因为原始帖子已在观察到的行为中注明。重新分配参数(可以将其视为具有动态提供值的局部变量)不会影响任何提供的参数。

有时,“参考呼叫”[混淆]用于表示“通过共享呼叫”或“按参考值调用值”,如下所述; true Call By Reference可以在C ++和VB等语言中找到,但不能在JavaScript中找到。

Call By [Object] Sharing

JavaScript的调用约定可以完全按照Call By [Object] Sharing语义进行讨论。

所有JavaScript对象都是值;所有原始值(所有值的子集)都是不可变的。

  

通过共享调用的语义与通过引用调用的不同之处在于,对函数内的函数参数的赋值对于调用者是不可见的,因此例如,如果传递了变量,则无法在调用者范围内模拟该变量的赋值。但是因为函数可以访问与调用者相同的对象(没有复制),所以对象中的那些对象的突变(如果对象是可变的)对于调用者是可见的,这可能看起来与调用不同按价值语义。

这个共享突变的一个例子在ultrayoshi的答案中提供,可以简单地解释:当一个表达式(例如变量访问)评估一个对象,并且所述对象被传递给一个函数,没有复制/克隆。

Call By Value [of the Reference]

虽然术语“按引用值调用值”通常用于描述行为,但应注意Java / C#意义上的JavaScript does not have "references"(或“非引用”值)所以这个术语有微妙的误导性 - 至少它不是说Call By Reference,它有各种各样的内涵,很多人都理解低解释。

  

在call-by-value中,计算参数表达式,并将结果值绑定到函数中的相应变量。如果函数或过程能够为其参数赋值,则只有其本地副本是赋值 - 也就是说,传递给函数调用的[任何变量]在函数返回时在调用者的作用域中保持不变。

因为只传递了对象的“引用”(而不是所述对象的复制/克隆),所以语义只是Call By Sharing的语义。但是,我在JavaScript中避免使用这个术语,因为它带来了不必要的实现细节,并且还引入了实现如何传递对象与原始值的区别。

  

描述“值为引用的值调用”是常见的(但不应理解为通过引用调用);另一个术语是分享呼叫。


因此,当我在谈论用JavaScript调用约定时,

更喜欢使用Call By Sharing来讨论行为,我避免调用[Value / Reference],因为它们有太多不同的“含义”并拖入不必要的实施细节。