为在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 -----^
所以当“别的东西”被分配给bar
,会发生这种情况:
x ---->"blah"
bar ---->"something else"
这是JavaScript中实际发生的事情的准确模型,还是我错过了其他的东西?
作为一个额外的问题,有什么方法可以说,改变这个变量引用的数据? 这是一种经常出现的情况,还是可以很容易地避免?
编辑:
在我观看的视频中,道格拉斯克罗克福德说道:“对象总是通过引用传递的,它们不通过值传递”,这是正确的,但函数的参数是通过值传递的,只是参考值是通过值传递的。
“JavaScript总是经过引用”,这是一个[白色]谎言和术语混淆。 虽然存在一些“灰色地带”,但我依据这些评估策略的定义。
这是我的论点和推理。 如果您持有不同的观点,请确保您至少可以支持它。
呼叫参考
呼叫引用意味着(对很多人)分配给参数会影响呼叫者的绑定。
在引用引用评估(也称为传递引用)中,函数接收对用作参数的变量的隐式引用,而不是其值。 这通常意味着该函数可以修改(即分配)作为参数的变量 - 将由调用者看到的东西。
在JavaScript中这种情况并非如此,因为原始文章在观察到的行为中已经注意到了。 重新分配一个参数(可以认为它是一个具有动态提供值的局部变量)对任何提供的参数都没有影响。
有时候,“通过引用来呼叫”被[confusingly]用来表示“通过共享呼叫”或“通过[参考文献]的价值呼叫” Call By Reference可以在C ++和VB等语言中找到,但不支持JavaScript。
通过[对象]共享呼叫
JavaScript的调用约定可以完全根据Call By [Object]共享语义来讨论。
所有JavaScript对象都是值; 并且所有原始值(它们是所有值的子集)都是不可变的。
共享调用的语义与引用调用的不同之处在于,调用者对函数内的函数参数的赋值对调用者来说是不可见的,例如,如果传递了一个变量,就不可能在调用者的变量中模拟赋值范围。 然而,由于该函数可以访问与调用方相同的对象(不进行任何复制),因此对于调用者来说,这些对象的变化(如果对象是可变的)对于调用者来说是可见的,这可能看起来不同于按值调用的语义。
这些共享突变的例子在ultrayoshi的答案中提供,可以简单地解释:当一个表达式(如变量访问)评估为一个对象,并且所述对象被传递给一个函数时,不会创建副本/克隆。
根据价值调用[参考资料]
尽管术语“引用值”通常用于描述行为,但应该注意的是,JavaScript没有Java / C#意义上的“引用”(或“非引用”值),所以这个术语很容易让人产生误解 - 至少它并没有说Call By Reference,它有各种各样的内涵,而且很多人都理解了一个很低的解释。
在值调用中,参数表达式被计算,并且结果值被绑定到函数中的相应变量。如果函数或过程能够为其参数赋值,则只分配其本地副本 - 即当函数返回时,传递给函数调用的[任何变量]在调用者的作用域中保持不变。
因为只传递一个对象的“引用”(而不是所述对象的复制/克隆),所以语义仅仅是通过共享呼叫的。 但是,我在JavaScript中避免了这个术语,因为那样会带来不必要的实现细节,并且还会在实现如何传递对象与原始值之间引入差异。
“价值作为参考的价值呼叫”的描述是常见的(但不应该被理解为是通过引用来调用); 另一个术语是分享呼叫。
因此,当我谈论JavaScript中的调用约定时,
我更喜欢使用Call By Sharing来讨论行为,我避免了Call By [Value / Reference],因为它们有太多不同的“含义”,并拖入不必要的实现细节。
你的解释是现货。
首先,你有一个叫做x
的变量,它是一个字符串对象的引用。 假设内存是0x100。 x
指向0x100
,其中包含字节等于:
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";
这将影响x
的Value
属性,因为bar
和x
引用同一个对象,并且您从未更改过任何一个对象的值。
换句话说,你不能重新分配你传递给函数的引用,因为你只是重新分配一个副本。 但是,您可以更改被引用对象的属性,因为该引用的其他副本都指向您要更改的数据。
你的解释是正确的。
您可以更改对象中键的值,这可以让您执行类似于传递引用的操作:
function foo(bar) {
bar.msg = "something else";
}
var x = { msg: "blah" };
foo(x);
alert(x.msg);
链接地址: http://www.djcxy.com/p/77579.html
上一篇: Assigning to a new value to an object passed into a function in JavaScript