为在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

在这一点上,我们有两个单独的变量。 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";

这将影响xValue属性,因为barx引用同一个对象,并且您从未更改过任何一个对象的值。

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


你的解释是正确的。

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

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

下一篇: DacPac exclude users and logins on export or import