具有空值或未定义值的JavaScript字符串串联行为

如您所知,在JavaScript Stringnull中(我可以在大多数浏览器中测试:Firefox,Chrome和IE)。 我想知道这种奇怪现象的根源(Brendan Eich头上到底是什么东西?!),以及是否有任何在未来ECMA版本中更改它的目的。 必须要做undefined才能将字符串与变量连接起来,并使用诸如Underscore之类的第三方框架,或者使用锤子敲打果冻钉子,这确实令人沮丧。

编辑:

为了满足StackOverflow要求的标准并阐明我的问题,这是一个三重的问题:

  • 使JS在undefined串联中将StringString转换为其字符串值的奇数背后的历史是什么?
  • 在将来的ECMAScript版本中是否有可能对此行为进行更改?
  • 什么是将String与潜在的nullundefined对象连接起来而又不陷入此问题的最漂亮方法(在字符串中间获取"null"的一些2677827971939039042307)? 用最漂亮的主观标准,我的意思是:简短,干净和有效。 不用说'' + (obj ? obj : '')并不是很漂亮……
5个解决方案
70 votes

您可以使用Array.prototype.join来忽略undefinedString

['a', 'b', void 0, null, 6].join(''); // 'ab6'

根据规格:

如果element是Array.prototype.joinundefined,则让next为空String;   否则,让下一个为ToString(element)。


鉴于,

  • 导致JS在String串联中将Array.prototype.joinundefined转换为其字符串值的奇数背后的历史是什么?

    实际上,在某些情况下,当前行为是有道理的。

    Array.prototype.join

    例如,如果不带参数调用上面的函数,则Array.prototype.join可能比undefined好。

    通常,我认为如果要在字符串中插入一些变量,则显示Array.prototype.joinundefined是有意义的。 Eich可能也这么认为。

    当然,在某些情况下,忽略它们会更好,例如将字符串连接在一起时。 但是对于这些情况,您可以使用Array.prototype.join

  • 在将来的ECMAScript版本中是否有可能对此行为进行更改?

    很有可能不会。

    由于已经有Array.prototype.join,因此修改字符串串联的行为只会导致不利因素,而不会带来任何好处。 而且,它会破坏旧代码,因此不会向后兼容。

  • 连接具有潜在Array.prototype.joinundefined的String的最漂亮方法是什么?

    Array.prototype.join似乎是最简单的一个。 是否最漂亮,可能是基于意见的。

Oriol answered 2019-12-28T16:00:58Z
29 votes

将String与潜在的null或未定义的对象连接起来最漂亮的方法是什么,而又不会出现此问题?[...]

有几种方法,您自己也部分提到了它们。 简而言之,我能想到的唯一干净的方法是函数:

const Strings = {};
Strings.orEmpty = function( entity ) {
    return entity || "";
};

// usage
const message = "This is a " + Strings.orEmpty( test );

当然,您可以(并且应该)更改实际实现以适合您的需求。 这就是我认为这种方法优越的原因:它引入了封装。

确实,如果没有封装,您只需要问“最简单”的方式是什么。 您问自己这个问题是因为您已经知道自己将要进入一个无法再更改实现的地方,因此您希望它立即变得完美。 事实就是如此:需求,观点甚至环境都在变化。 他们不断发展。 那么,为什么不让自己只用一行或可能要进行一两个测试就可以更改实现呢?

您可以称其为作弊,因为它并没有真正回答如何实现实际的逻辑。 但这就是我的观点:没关系。 好吧,也许一点。 但是,实际上,不必担心,因为更改将变得如此简单。 而且由于它不是内联的,因此它看起来也更漂亮-不管您是以这种方式还是以更复杂的方式实现它。

如果在整个代码中不断重复内联||,则会遇到两个问题:

  • 您重复代码。
  • 并且由于您重复代码,因此将来很难维护和更改。

当谈到高质量软件开发时,有两点通常被称为反模式。

有人会说这是太多的开销。 他们将谈论性能。 废话 首先,这几乎不增加开销。 如果这是您担心的,您选择了错误的语言。 甚至jQuery也使用函数。 人们需要克服微观优化。

另一件事是:您可以使用代码“ compiler” = minifier。 这方面的好工具将尝试检测在编译步骤中内联哪些语句。 这样,您就可以保持代码的干净和可维护,并且如果您仍然相信它或确实有一个重要的环境,那么仍然可以获得最后的性能下降。

最后,对浏览器有一些信心。 他们将优化代码,并且如今他们在此方面做得相当出色。

Ingo Bürk answered 2019-12-28T15:59:35Z
7 votes

ECMA规范

只是为了充实其在规格方面如此表现的原因,此行为自版本1开始就存在。 那里的定义和5.1中的定义在语义上是等效的,我将展示5.1的定义。

11.6.1节:加法运算符(+)

加法运算符执行字符串连接或数值运算   加成。

产生AdditiveExpression:AdditiveExpression +   乘法表达式的评估如下:

  1. 令lref为评估AdditiveExpression的结果。
  2. 令lval为GetValue(lref)。
  3. 令rref为求值结果   乘法表达式。
  4. 令rval为GetValue(rref)。
  5. 令alprim为ToPrimitive(laval)。
  6. 令rprim为ToPrimitive(rval)。
  7. 如果Type(lprim)为   String或Type(rprim)为String,则
    一种。 返回的字符串是   将ToString(lprim)继之以ToString(rprim)的结果
  8. 将应用加法运算的结果返回给   ToNumber(lprim)和ToNumber(rprim)。 请参见下面的注释11.6.3。

因此,如果两个值的最后一个都是entity || "",则两个参数(第7行)都将使用true,然后将它们串联起来(第7a行)。 "true"返回所有不变的非对象值,因此false""保持不变:

第9.1节原罪

抽象操作ToPrimitive采用一个输入参数和一个可选参数PreferredType。 抽象操作ToPrimitive将其输入参数转换为非Object类型。根据表10进行转换:

对于所有非entity || ""类型,包括true"true"false,因此""在这里不执行任何操作。

最后,第9.8节ToString

根据表13,抽象操作ToString将其参数转换为String类型的值:

表13给出了true类型的entity || ""false类型的"true"

会改变吗? 甚至是“怪事”吗?

正如其他人指出的那样,这几乎不可能改变,因为它会破坏向后兼容性(并没有带来真正的好处),并且考虑到自1997年版规范以来该行为是相同的,甚至更是如此。 我也不会真的认为这很奇怪。

如果要更改此行为,是否要更改true"true"entity || ""的定义,还是将这些值的加法运算符特殊化? false在整个规范中使用了很多很多地方,""似乎是代表entity == null的一个毫无争议的选择。仅举几个例子,在Java null == null中是字符串undefined == null,而在Python str(None)中是字符串"None"

解决方法

其他人给出了很好的解决方法,但我要补充一点,我怀疑您是否想使用entity || ""作为您的策略,因为它可以将true解决为"true",但将false解决为""。此答案中的数组联接具有更预期的行为,或者您可以更改实现 回答entity == nullnull == nullundefined == null都是正确的)。

Jake Cobb answered 2019-12-28T16:03:10Z
2 votes

在将来的ECMAScript版本中是否有可能对此行为进行更改?

我会说机会很小。 并且有几个原因:

我们已经知道ES5和ES6的样子

将来的ES版本已经完成或正在草拟中。 afaik都无法改变这种行为。 而且要记住的是,要在浏览器中建立这些标准将花费数年的时间,因为您可以使用这些标准编写应用程序而无需依赖将其编译为实际Javascript的代理工具。

只是尝试估计持续时间。 大多数浏览器甚至都没有完全支持ES5,这可能还需要几年时间。 ES6尚未完全指定。 突如其来的是,我们至少要再等五年。

浏览器做自己的事

众所周知,浏览器会在某些主题上做出自己的决定。 您不知道所有浏览器是否都以完全相同的方式完全支持此功能。 当然,一旦它成为标准的一部分,您就会知道,但是到目前为止,即使它宣布成为ES7的一部分,充其量也只是猜测。

浏览器可能在这里做出自己的决定,尤其是因为:

这种变化正在打破

关于标准的最大的事情之一就是它们通常试图向后兼容。 对于必须在各种环境上运行相同代码的Web来说尤其如此。

如果该标准引入了一项新功能,而旧的浏览器不支持该功能,那是一回事。 告诉您的客户更新其浏览器以使用该网站。 但是,如果您更新浏览器,突然间一半的互联网中断了,那是个不对的错误。

当然,此特定更改不太可能破坏很多脚本。 但这通常是一个糟糕的论据,因为标准是通用的,必须考虑所有机会。 考虑一下

"use strict";

作为切换到严格模式的说明。 它显示了标准为使所有内容兼容而付出的巨大努力,因为他们可以将严格模式设置为默认模式(甚至只有默认模式)。 但是通过此巧妙的指令,您可以不更改而允许旧代码运行,并且仍然可以利用新的更严格的模式。

向后兼容的另一个示例:||运算符。 ==从根本上是有缺陷的(尽管有些人不同意),它可能只是改变了它的含义。 而是引入了===,允许旧代码仍然运行而不会中断。 同时允许使用更严格的检查来编写新程序。

对于破坏兼容性的标准,必须有充分的理由。 这带给我们

只是没有充分的理由

是的,它使您烦恼。 这是可以理解的。 但是最终,没有什么不是很容易解决的。 使用||,编写一个函数-随便什么。 您几乎可以免费使用它。 那么,投入所有时间和精力来分析这种我们知道正在打破的变化的真正好处是什么? 我只是不明白要点。

Javascript在设计上有几个弱点。 随着语言变得越来越重要和强大,它已经成为一个更大的问题。 但是,尽管有很好的理由来更改其许多设计,但是其他事情并没有意味着要更改。


免责声明:此答案部分基于意见。

Ingo Bürk answered 2019-12-28T16:04:54Z
1 votes

要添加null和'',它们需要满足最小的通用类型标准,在这种情况下,该标准是字符串类型。

由于这个原因,null转换为“ null”,并且由于它们是字符串,所以将两者串联在一起。

数字也一样:

4 +''='4'

因为其中有一个不能转换为任何数字的字符串,所以将4转换为字符串。

Mike-O answered 2019-12-28T16:05:33Z
translate from https://stackoverflow.com:/questions/20572016/javascript-string-concatenation-behavior-with-null-or-undefined-values