将字符串传递给函数时,是按值复制还是按引用传递?

14

由于JavaScript中的字符串是基本类型,将字符串传递给函数会创建它的本地副本吗?我对此感到疑惑,因为在字符串被创建后就不能修改,所以看起来JavaScript虚拟机不会仅仅在内部传递字符串的地址。

如果有人告诉我不必担心这个问题(与Web开发人员交谈时经常发生这种情况),那么我正在开发HTML5游戏,垃圾收集是一个主要问题,所以我真的需要知道。


4
也许这是一个愚蠢的问题,但为什么不试一试呢? - ulentini
2
每个原始类型都是按值传递的。请参见http://snook.ca/archives/javascript/javascript_pass - user2193789
如果你认为垃圾回收是一个大问题,那么请始终使用delete语句来释放新创建的对象。 - user2193789
1
@silentboy:delete与垃圾回收完全无关。这不是C++,JavaScript中的delete意味着完全不同的东西。 - T.J. Crowder
1
因为字符串是不可变的,所以这并不重要。如果它们通过引用传递,对引用的更改不会影响原始字符串,但你对原始字符串所做的任何操作也同样不会影响引用。你能做的只是读取原始字符串,或者基于原始字符串创建一个新的字符串。 - kennebec
显示剩余5条评论
2个回答

13

这个字符串将会被按引用传递。

字符串是不可变的,所以无论何时你尝试改变它,都会得到一个新的字符串(例如通过 value+="more" 进行操作)。

另外请参见:什么是不可变的?

@T.J. Crowder: 按值传递与按引用传递 - 如果你正在看语言定义,那么你是正确的。然而,我认为没有一种实现会真正创建字符串的副本,因为这样做会非常慢。此外,由于字符串是不可变的原始类型,所以没有必要复制它们,因为它们不能被改变。


2
“该字符串将通过引用传递。”你把它说成是事实。你有任何证据吗?我相信这是真的(更准确地说,我相信对字符串的引用是按值传递的,在JavaScript中没有任何“按引用传递”的情况),但如果没有能够引用的东西,我会犹豫地将其陈述为裸的事实。 - T.J. Crowder
@ Chris:我并不是说字符串内容的副本被复制了。我相当确定它们没有被复制(正如我在我的答案中所说),而是将对这些内容的引用(按值)传递到函数中。 - T.J. Crowder
我知道字符串是不可变的,我在问题中已经说过了,这也是我首先提出这个问题的原因。除非有人能够提供参考资料,否则我明天可能会接受这个答案。虽然这个答案基于常识,但当涉及到这些事情时,我想要更多的东西可能有点困难。 - dreta
然而,我认为没有实际上创建字符串副本的实现,因为这将会非常慢。我从未说过我认为有这样的实现。事实上,在我的回答中,我说了相反的话。 - T.J. Crowder
关于赋值怎么处理?字符串变量的值是克隆还是复制对该字符串值的引用?我假设这也是通过引用,但是《You Don't Know JS》一书中的这部分内容说得不同:“简单值(又称标量原语)始终通过值复制进行分配/传递:null、undefined、string、number、boolean和ES6的symbol。” 这里是特定章节的链接(我会立即跳转到“值与引用”部分)。 - doubleOrt
显示剩余5条评论

6

我认为规范对这一点是没有明确说明的。但是,如果传递实际字符串内容而不是传递指向内存中该内容的引用,那么这将是一个真正愚蠢的实现,即使字符串在理论上是“原语”。我怀疑大多数实现在处理“原语”字符串时与处理对象引用的方式相似(在这方面,显然不同于其他方面,例如 === ),但只是没有使用 Object 类的附属物。


如果传递了对某个东西的引用,那么在下面的例子中,b 有一个 bar 属性会更合理吧?var a = "foo" var b = a; a["bar"] = "baz"; console.log(b["bar"]);现在,我确定有些地方出了问题,导致我感到困惑,这是什么原因呢? - doubleOrt
@Taurus:这是 JavaScript 中令人困惑的部分,但它已被规范覆盖。 :-) 请记住,JavaScript 既有字符串原始值,也有字符串对象a [包含/具有对] 原始值字符串 "foo" 的引用。语句 a["bar"] = "baz"a 获取原始字符串,但由于它随后将其用作对象,因此会创建一个新的临时字符串对象并用于表达式的该部分。该临时字符串对象获得了 bar 属性,但由于没有保存... (续) - T.J. Crowder
如果我们将 a 的值设置为一个对象,然后将其值设置为 null,那么该对象就会被垃圾回收。这是因为当我们将 a 的值设置为 null 时,它不再引用该对象。只要没有任何地方引用该对象(不是 a,当然也不是 b),该对象就立即可以进行垃圾回收。同时,a(和 b)仍然包含或引用原始字符串 "foo",由于是原始类型,因此无法具有临时属性。这种行为在规范中的PutValue操作中有所描述。我们可以通过向 String.prototype 添加一个方法来观察字符串对象的创建...(续) - T.J. Crowder
(继续) ...并将其返回给我们:Object.defineProperty(String.prototype, "getObject", {value: function() { return this; }); (http://jsfiddle.net/tyfozkmp/) 请注意,这是在松散模式下(非严格模式)。如果它在严格模式下,即使字符串对象会被创建(至少在规范术语中),我们也不会将其视为 this,因为原始值(原始类型)会作为 this 传递而不是字符串对象(在严格模式下,this 可以是原始类型;但在松散模式下不行)。这对于多种原因都很方便,尤其是为了启用 JavaScript 引擎的优化。有趣吧? :-) - T.J. Crowder
这是什么鬼!即使 a["bar"] 返回 undefined,我还以为它会返回 "baz"!感谢解释,很抱歉回复晚了。首先允许这种行为是否更合适(通过在尝试向字符串添加属性时抛出错误),这样的表达式是不是毫无用处? - doubleOrt
@Taurus:你知道吗,我从来没有听过有人建议过这样做,但在严格模式下对我来说是有意义的。 :-) - T.J. Crowder

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接