为什么+对于连接这么不好?


44

每个人都在说JavaScript的问题之一是使用+[ example ]进行字符串连接。有人说问题不在使用+,而是强制类型[请参阅上一个示例的注释]。但是强类型语言将+用于连接和强制类型没有任何问题。例如,在C#中:

int number = 1;
MyObject obj = new MyObject();

var result = "Hello" + number + obj;
// is equivalent to
string result = "hello" + number.ToString() + obj.ToString();

那么,为什么JavaScript中的字符串连接这么大的问题呢?


17
为什么呢bad?是谁Everyone
Neal

3
我认为问题是+是数学运算符。+的隐含功能使该标准过程变得混乱。因为虽然“ Hello” +数字可能有效,但“ + Hello”可能无效。
SoylentGray 2011年

3
@尼尔,我同意-我想知道在所有这些问题中似乎所有这些神秘的“ everyones”是谁。
GrandmasterB

使用+进行串联是可以的。使用动态类型是可以的。以相同的语言使用两者都是不好的^ H ^ H ^ H导致歧义。
格里

6
@尼尔-“每个人”都是道格拉斯·克罗克福德。他在他的书“ JavaScript:好的部分”中将其列为“糟糕的”。
kirk.burleson

Answers:


68

考虑这段JavaScript代码:

var a = 10;
var b = 20;
console.log('result is ' + a + b);

这将记录

结果是1020

最有可能不是预期的,并且可能很难跟踪错误。


4
很好地说明了潜在的问题:当类型(示例中为string + int + int)混合使用时,字符串连接定义不明确。最好有一个完全不同的运算符(如Perl的'。'),并具有定义明确的语义。
Bruce Ediger

9
或Python的解决这种的方式,这将迫使你包裹a,并bstr(a)str(b)电话; 否则,您会得到一个ValueError: cannot concatenate 'str' and 'int'
Aphex

6
@FrustratedWithFormsDesigner:添加括号。'result is ' + (a + b)
乔恩·普迪

18
事实是,在C#中您可以执行相同的操作,并且您将获得相同的结果- System.Console.WriteLine("result is " + 10 + 20);,但是没有人抱怨C#中的错误。那就是我不理解的-JavaScript到底是什么使它更加混乱?
配置器

7
@configurator:因为人们被告知C#是完美的,而javascript却很糟糕。他们俩都错了,但是一种语言的声誉似乎仍然存在,人们的看法非常重要,尤其是在互联网上。
gbjbaanb 2012年

43

当您说“不好”时,您是说“不正确”还是“慢”?有关使用数学运算符做字符串连接的参数可以说是一个“不正确”的说法,但也有被制成,使用的参数+了很多的字符串连接的速度会非常慢。

我们不是在谈论"Hello" + number当我们谈论的性能,我们在谈论通过反复追加到它在一个循环中建立一个比较大的字符串。

var combined = "";
for (var i = 0; i < 1000000; i++) {
    combined = combined + "hello ";
}

在JavaScript(以及C#)中,字符串是不可变的。它们永远不能更改,只能替换为其他字符串。您可能知道combined + "hello "并不会直接修改combined变量-该操作会创建一个新字符串,这是将两个字符串连接在一起的结果,但是combined如果要更改该变量,则必须将该新字符串分配给该变量。

因此,此循环正在做的是创建一百万个不同的字符串对象,并丢弃其中的999,999个。创建这么多不断增长的字符串的速度并不快,现在垃圾回收器在清理之后还有很多工作要做。

C#具有完全相同的问题,该问题可以通过StringBuilder类在该环境中解决。在JavaScript中,通过建立要连接的所有字符串的数组,然后将它们最后一次连接在一起,而不是在循环中进行一百万次,可以得到更好的性能:

var parts = [];
for (var i = 0; i < 1000000; i++) {
    parts.push("hello");
}
var combined = parts.join(" ");

3
这是一个很好的答案。它不仅回答了这个问题,而且受到了教育。
Charles Sprayberry 2011年

3
这根本不是我的意思,它与问题完全无关。
配置器

4
抱歉。似乎至少有7个人从措辞上误解了您的问题。
乔尔·穆勒

9
重新性能:也许在2011年就是这种情况,但是现在+运算符方法比array.join方法要快得多(至少在v8中如此)。看到这个jsperf:jsperf.com/string-concatenation101
UpTheCreek 2014年

2
知道join方法如何一步一步地从多个部分中生成一个字符串,而不是一一合并然后生成所有这些中间字符串吗?
Angelo.Hannes 2014年

14

只要同时只能添加数字和仅连接字符串,就可以同时使用+进行加和连接操作。但是,Javascript将根据需要自动在数字和字符串之间转换,因此您具有:

3 * 5 => 15
"3" * "5" => 15
2 + " fish" => "2 fish"
"3" + "5" => "35"  // hmmm...

也许更简单地说,在存在自动类型转换的情况下重载“ +”会破坏+运算符的预期关联性:

("x" + 1) + 3 != "x" + (1 + 3)

2
串联也缺少加法的交换性质,3 + 5 == 5 + 3但是"3" + "5" != "5" + "3"
Caleth 2015年

10

字符串连接引发的典型问题涉及以下几个问题:

  1. +符号而过载
  2. 简单的错误
  3. 程序员很懒

#1

+用于字符串串联加法的第一个也是最重要的问题是您正在使用+字符串串联加法。

如果你有变量ab,并设置c = a + b有依赖于类型的歧义ab。这种歧义迫使您采取额外的步骤来缓解此问题:

字符串连音:

c = '' + a + b;

加成:

c = parseFloat(a) + parseFloat(b);

这把我带到第二点

#2

意外地将变量强制转换为字符串而没有意识到这很容易。也很容易忘记您的输入是以字符串形式输入的:

a = prompt('Pick a number');
b = prompt('Pick a second number');
alert( a + b );

由于提示符返回输入的字符串值,将产生不直观的结果。

#3

需要打字parseFloatNumber()每次我想做加法都是乏味而烦人的。我认为自己足够聪明,以至于记得不要搞乱我的动态类型,但这并不意味着我从未搞砸过我的动态类型。事实的真相是,我懒于输入parseFloatNumber()每次添加时都太懒了,因为我做了很多添加。

解?

并非所有语言都+用于字符串连接。PHP用于.连接字符串,这有助于区分何时要添加数字和何时联接字符串。

任何符号都可以用来连接字符串,但是大多数符号已经被使用,最好避免重复。如果可以的话,我可能会使用_联接字符串,并禁止输入_in变量名。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.