ogg*_*ter 9 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"
Run Code Online (Sandbox Code Playgroud)
这让我很困惑,因为我一直在观看道格拉斯·克罗克福德的一些JavaScript视频,并记得他说像"JavaScript总是通过引用传递".
我可以解释这种情况的方式是JavaScript传递对象的引用,但这些引用被复制.这意味着在foo
函数中,我正在分配一个新的引用bar
,然后超出范围,留下x
未触及的引用.基本上我们从:
x ---->"blah"
Run Code Online (Sandbox Code Playgroud)
然后在foo
调用时,bar
引用相同的数据:
x ---->"blah"
bar -----^
Run Code Online (Sandbox Code Playgroud)
因此,当指定"其他东西"时bar
,会发生这种情况:
x ---->"blah"
bar ---->"something else"
Run Code Online (Sandbox Code Playgroud)
这是JavaScript中实际发生的事情的准确模型,还是我错过了其他什么?
作为一个额外的问题,有没有办法说,改变这个变量引用的数据?这是经常出现的情况,还是可以轻易避免?
编辑:
道格拉斯·克罗克福德在我看过的视频中说"对象总是通过引用传递它们不是通过值传递",这是正确的,但函数的参数是通过值传递的,它只是通过值传递引用.
你的解释是现实的.
首先,您有一个名为变量的变量x
,它是对字符串对象的引用.假设内存为0x100. x
指向0x100
,其中包含字节blah:
var x = "blah"; // x is 0x100 which references a string in memory
Run Code Online (Sandbox Code Playgroud)
接下来,您将0x100
进入函数foo
:
function foo(bar) {
bar = "something else";
}
Run Code Online (Sandbox Code Playgroud)
由于JavaScript中的所有内容都是按值传递的,即使是引用,JavaScript 也会在内存中创建此引用的副本,现在bar
在该函数中调用它:
foo(x); // Copies the value of x (a reference) to bar
Run Code Online (Sandbox Code Playgroud)
此时,我们有两个独立的变量. x
和bar
.两者都碰巧具有相同的价值0x100
.因此,如果您要更改其中任何一个引用的对象的属性,它将同时影响x
和bar
.
但是,你正在做的是分配bar
指向别的东西:
bar = "something else"; // Now references some other string we just created
Run Code Online (Sandbox Code Playgroud)
现在,bar
重新分配引用我们刚为其分配内存的新字符串. bar
不再具有值0x100
,它现在具有某个其他地址的值(比方说0x500
). x
当然还有一个值,0x100
因为bar
它只是一个副本 x
,而不是一个参考x
.
出于这个原因,当你:
alert(x);
Run Code Online (Sandbox Code Playgroud)
您仍将获得原始值,因为这x
是指向的.
第二个问题:
有没有办法说,改变这个变量引用的数据?这是经常出现的情况,还是可以轻易避免?
是的,只需将其包装在另一个对象中.例如:
var x = {Value: "blah"};
foo(x);
Run Code Online (Sandbox Code Playgroud)
现在,我们引用了一个带有被调用属性的对象,该对象Value
包含对某个内存中某个字符串的引用.
在foo
,我们可以做:
bar.Value = "something else";
Run Code Online (Sandbox Code Playgroud)
这将影响到Value
财产x
,因为两者bar
并x
引用同一个对象,你永远不会改变其中任何价值.
换句话说,您不能重新分配您传递给函数的引用,因为您只是重新分配副本.但是,您可以更改被引用对象的属性,因为该引用的其他副本都指向您正在更改的数据.
use*_*740 -2
“JavaScript 总是通过引用传递”是一个[善意的]谎言并且是术语的混淆。虽然存在一些“灰色地带”,但我还是遵循评估策略的这些定义。
\n\n以下是我的论点和推理。如果您持有不同的观点,请确保您至少可以支持它。
\n\n通过引用调用(对于很多人来说)意味着分配给参数会影响调用者中的绑定。
\n\n\n\n\n在按引用调用计算(也称为按引用传递)中,函数接收对用作参数的变量的隐式引用,而不是其值的副本。这通常意味着该函数可以修改(即分配给)用作参数的变量\xe2\x80\x94,该变量将被其调用者看到。
\n
JavaScript 中的情况并非如此,正如原始帖子在观察到的行为中指出的那样。重新分配参数(可以将其视为具有动态提供值的局部变量)不会影响任何提供的参数。
\n\n有时,“通过引用调用”[令人困惑地]被用来表示“通过共享调用”或“通过[引用的]值调用”,如下所述;真正的“按引用调用”存在于 C++ 和 VB 等语言中,但 JavaScript 中却没有。
\n\nJavaScript 的调用约定完全可以通过Call By [Object]sharing来讨论语义来讨论。
\n\n所有 JavaScript 对象都是值;所有原始值(所有值的子集)都是不可变的。
\n\n\n\n\n通过共享调用的语义与通过引用调用不同,因为调用者看不到函数内函数参数的赋值,因此,例如,如果传递了一个变量,则不可能在函数中模拟对该变量的赋值。调用者的范围。然而由于函数可以访问与调用者相同的对象(不进行复制),因此如果对象是可变的,则函数内这些对象的突变对调用者来说是可见的,这可能与按值调用语义不同。
\n
ultrayoshi 的答案中提供了这些共享突变的示例,并且可以简单地解释:当表达式(例如变量访问)计算为对象并且所述对象被传递给函数时,不进行复制/克隆。
\n\n虽然术语“按引用的值调用”经常用于描述该行为,但应该注意的是,JavaScript没有Java/C# 意义上的“引用”(或“非引用”值),因此这个术语有微妙的误导性——至少它不是说Call By Reference,它有各种各样的含义,而且很多人理解的解释很浅薄。
\n\n\n\n\n在按值调用中,对参数表达式进行求值,并将结果值绑定到函数中相应的变量。如果函数或过程能够为其参数赋值,则仅为其本地副本分配 \xe2 \x80\x94 也就是说,当函数返回时,传递给函数调用的[任何变量]在调用者的作用域中不会发生变化。
\n
因为仅传递对对象的“引用”(而不是所述对象的副本/克隆),所以语义仅仅是“通过共享调用”的语义。然而,我在 JavaScript 中避免使用这个术语,因为它会带来不必要的实现细节,并且还会在实现传递对象与原始值的方式上引入分歧。
\n\n\n\n\n“按值调用,其中值是引用”的描述很常见(但不应理解为按引用调用);另一个术语是共享呼叫。
\n
因此,当我谈论 JavaScript 中的调用约定时,
\n\n我更喜欢使用通过共享调用来讨论行为,并且避免通过[值/引用]调用,因为它们有太多不同的“含义”并拖入不必要的实现细节。
\n 归档时间: |
|
查看次数: |
2702 次 |
最近记录: |