我是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}}中的道格拉斯·克罗克福德说“对象总是通过引用传递,它们不是通过值传递”,这是正确的,但函数的参数是通过值传递的,它只是通过值传递引用答案 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
此时,我们有两个独立的变量。 x
和bar
。两个发生具有相同的值0x100
。因此,如果您要更改其中任何一个引用的对象的属性,它将同时影响x
和bar
。
然而,你做的是指定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}}属性,因为x
和bar
都引用了同一个对象,而您从未更改其中任何一个的值。< / 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);
答案 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评估(也称为pass-by-reference)中,函数接收对用作参数的变量的隐式引用,而不是其值的副本。 这通常意味着该函数可以修改(即分配给)用作参数的变量 - 其调用者将看到的内容。
这是不是在JavaScript中的情况,因为原始帖子已在观察到的行为中注明。重新分配参数(可以将其视为具有动态提供值的局部变量)不会影响任何提供的参数。
有时,“参考呼叫”[混淆]用于表示“通过共享呼叫”或“按参考值调用值”,如下所述; true Call By Reference可以在C ++和VB等语言中找到,但不能在JavaScript中找到。
JavaScript的调用约定可以完全按照Call By [Object] Sharing语义进行讨论。
所有JavaScript对象都是值;所有原始值(所有值的子集)都是不可变的。
通过共享调用的语义与通过引用调用的不同之处在于,对函数内的函数参数的赋值对于调用者是不可见的,因此例如,如果传递了变量,则无法在调用者范围内模拟该变量的赋值。但是因为函数可以访问与调用者相同的对象(没有复制),所以对象中的那些对象的突变(如果对象是可变的)对于调用者是可见的,这可能看起来与调用不同按价值语义。
这个共享突变的一个例子在ultrayoshi的答案中提供,可以简单地解释:当一个表达式(例如变量访问)评估一个对象,并且所述对象被传递给一个函数,没有复制/克隆。
虽然术语“按引用值调用值”通常用于描述行为,但应注意Java / C#意义上的JavaScript does not have "references"(或“非引用”值)所以这个术语有微妙的误导性 - 至少它不是说Call By Reference,它有各种各样的内涵,很多人都理解低解释。
在call-by-value中,计算参数表达式,并将结果值绑定到函数中的相应变量。如果函数或过程能够为其参数赋值,则只有其本地副本是赋值 - 也就是说,传递给函数调用的[任何变量]在函数返回时在调用者的作用域中保持不变。
因为只传递了对象的“引用”(而不是所述对象的复制/克隆),所以语义只是Call By Sharing的语义。但是,我在JavaScript中避免使用这个术语,因为它带来了不必要的实现细节,并且还引入了实现如何传递对象与原始值的区别。
描述“值为引用的值调用”是常见的(但不应理解为通过引用调用);另一个术语是分享呼叫。
因此,当我在谈论用JavaScript调用约定时,
我更喜欢使用Call By Sharing来讨论行为,我避免调用[Value / Reference],因为它们有太多不同的“含义”并拖入不必要的实施细节。