在JavaScript中为传递给函数的对象赋值

在JavaScript中为传递给函数的对象赋值,javascript,pass-by-reference,pass-by-value,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" 这让我很困惑,因为我一直在看Douglas Crockford的一些JavaScript视频,还记得

我是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"
这让我很困惑,因为我一直在看Douglas Crockford的一些JavaScript视频,还记得他说过“JavaScript总是通过引用传递”之类的话

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

x   ---->"blah"
然后,当调用
foo
时,
bar
引用相同的数据:

x   ---->"blah"
bar -----^
因此,当将“其他内容”分配给
条时,会发生以下情况:

x   ---->"blah"
bar ---->"something else"
这是JavaScript中实际发生的事情的准确模型,还是我遗漏了其他东西

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

编辑:


Douglas Crockford在文章中说“对象总是通过引用传递,而不是通过值传递”,这是正确的,但是函数的参数是通过值传递的,只是引用是通过值传递的。

您的解释是正确的

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

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

数字、字符串等基本值不是通过引用传递的,而是通过对象传递的。例如:

var myObj = {
  value: "Hello"
};

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

change(myObj);
console.log(myObj.value) // Prompts "Bye"
var x = {Value: "blah"};
foo(x);

你的解释恰到好处

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

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

function foo(bar) {
    bar = "something else";
}
由于JavaScript中的所有内容都是按值传递的,即使是引用,JavaScript也会在内存中复制该引用,现在在该函数中称为
bar

foo(x); // Copies the value of x (a reference) to bar
在这一点上,我们有两个独立的变量<代码>x
。两者恰好具有相同的值,
0x100
。因此,如果要更改对象的某个属性(其中一个是引用对象),它将同时影响
x
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 myObj = {
  value: "Hello"
};

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

change(myObj);
console.log(myObj.value) // Prompts "Bye"
var x = {Value: "blah"};
foo(x);
现在,我们有一个对对象的引用,该对象具有一个名为
Value
的属性,其中包含对内存中某个字符串的引用

foo
中,我们可以执行以下操作:

bar.Value = "something else";
这将影响
x
Value
属性,因为
bar
x
都引用了相同的对象,并且您从未更改过其中任何一个的值

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

“JavaScript总是通过引用传递”是一个善意的谎言和术语混淆。虽然有一些“灰色地带”,我还是经过

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

通过引用调用意味着(对许多人来说)分配给参数会影响调用者中的绑定

在按引用调用求值(也称为按引用传递)中,函数接收对用作参数的变量的隐式引用,而不是其值的副本。这通常意味着函数可以修改(即赋值)用作参数的变量,调用方将看到该变量

JavaScript中并非如此,正如原始帖子在观察到的行为中所指出的那样。重新分配参数(可以将其视为具有动态提供的值的局部变量)不会影响任何提供的参数

有时,“引用调用”被[令人困惑地]用来表示“共享调用”或“引用值调用”,如下所述;在C++和VB等语言中发现了真正的引用调用,而不是JavaScript。
JavaScript的调用约定可以完全从语义的角度进行讨论。

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

通过共享调用的语义与通过引用调用的语义不同,因为调用方看不到函数中函数参数的赋值,因此,例如,如果传递了变量,则不可能在调用方的作用域中模拟对该变量的赋值。但是,由于函数可以访问与调用方相同的对象(不复制),因此调用方可以看到函数中这些对象的变化(如果对象是可变的),这可能与按值调用语义不同

ultrayoshi的答案a中提供了这些共享突变的一个例子