具有null或未定义值的JavaScript字符串连接行为

Pom*_*pot 56 javascript null string-concatenation undefined

您可能知道,在JavaScript '' + null = "null"'' + undefined = "undefined"(在大多数浏览器中我可以测试:Firefox,Chrome和IE).我想知道这种奇怪的起源(Brendan Eich头脑中究竟是什么?!)以及是否有任何改变ECMA未来版本的目标.确实非常令人沮丧的是,必须要将'sthg' + (var || '')Strings与变量连接起来并使用像Underscore或其他的第三方框架那样使用锤子进行果冻钉敲击.

编辑:

为了满足StackOverflow所要求的标准并澄清我的问题,它有三个:

  • 在串联中使JS转换null或其undefined字符串值的奇怪背后的历史是什么String
  • 在将来的ECMAScript版本中是否有机会改变这种行为?
  • 什么是连接最漂亮的方式String与潜在nullundefined对象不落入这个问题(得到一些"undefined""null"在字符串的中间)?根据主观标准最漂亮,我的意思是:简短,干净,有效.没必要说那'' + (obj ? obj : '')不是很漂亮......

Ori*_*iol 73

您可以使用Array.prototype.join忽略undefinednull:

['a', 'b', void 0, null, 6].join(''); // 'ab6'
Run Code Online (Sandbox Code Playgroud)

根据规格:

如果elementundefinednull,则接下来是空字符串; 否则,让接下来是ToString(元素).


鉴于,

  • 在串联中使JS转换null或其undefined字符串值的奇怪背后的历史是什么String

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

    function showSum(a,b) {
        alert(a + ' + ' + b + ' = ' + (+a + +b));
    }
    Run Code Online (Sandbox Code Playgroud)

    例如,如果上面的函数是在没有参数的情况下调用的,那么undefined + undefined = NaN可能比 + = NaN.

    一般来说,我认为如果你想在字符串中插入一些变量,显示undefinednull有意义.可能,Eich也这么认为.

    当然,有些情况下忽略那些会更好,例如将字符串连接在一起时.但对于那些你可以使用的情况Array.prototype.join.

  • 在将来的ECMAScript版本中是否有机会改变这种行为?

    很可能不是.

    既然已经存在Array.prototype.join,修改字符串连接的行为只会导致缺点,但没有优势.而且,它会破坏旧代码,因此它不会向后兼容.

  • 连接字符串与潜在null或最有效的方法是什么undefined

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

  • *"你可以使用Array.prototype.join忽略undefined和null"* - 值得注意的是,如果你使用空字符串作为你的`join` arg,这只能按照你的需要工作. (11认同)
  • 在 `.join()` 之后添加 `.trim()` ,它将删除前导/试用空格。(如果使用空格连接)。同样,其他类型的字符也可以被删除 (2认同)

Ing*_*ürk 30

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

有几种方法,你自己部分提到它们.简而言之,我能想到的唯一干净的方法是功能:

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

// usage
const message = "This is a " + Strings.orEmpty( test );
Run Code Online (Sandbox Code Playgroud)

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

真的,如果你没有封装,你只需要询问"最漂亮"的方式.你问自己这个问题,因为你已经知道自己会进入一个你无法改变实现的地方,所以你希望它能够立刻完美.但事情就是这样:需求,观点甚至环境都会发生变化.他们进化了.那么为什么不让自己改变实现只需要调整一行,也许一两次测试?

你可以称之为作弊,因为它并没有真正回答如何实现实际的逻辑.但这是我的观点:没关系.好吧,也许一点点.但实际上,没有必要担心,因为改变是多么简单.而且由于它没有内联,它看起来也更漂亮 - 无论你是以这种方式还是以更复杂的方式实现它.

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

  • 你重复代码.
  • 而且由于您复制了代码,因此将来很难进行维护和更改.

在高质量的软件开发方面,这些通常被认为是反模式的两点.

有人会说这太开销了; 他们会谈论表现.这是无意义的.首先,这几乎不会增加开销.如果这是你担心的,你选择了错误的语言.甚至jQuery也使用函数.人们需要克服微观优化.

另一件事是:你可以使用代码"compiler"= minifier.此区域中的好工具将尝试在编译步骤中检测要内联的语句.这样,您可以保持代码的清洁和可维护性,如果您仍然相信它或者确实有一个重要的环境,那么仍然可以获得最后一滴性能.

最后,对浏览器有一定的信心.他们将优化代码,这些天他们做得非常好.


Don*_*dle 16

使用合并 - 语法是??

示例,可以在浏览器控制台中测试:

  • "Hello " + (null ?? "")
  • "Hello " + (undefined ?? "")

两者都会产生:'Hello '

  • @Yunnosch我正在回答这个问题:“将字符串与潜在的空或未定义对象连接起来而不陷入这个问题的最漂亮的方法是什么”。我的回答不包含反问句 - ?? 从字面上看就是语法... (4认同)
  • 请查看 https://stackoverflow.com/editing-help 它可能会帮助您使(夸张的)散文标点符号和代码语法之间的区别更加明显。 (2认同)

Jak*_*obb 7

ECMA规范

只是为了充实它在规范方面表现如此的原因,这种行为自第一版开始就存在.那里和5.1中的定义在语义上是等价的,我将展示5.1定义.

第11.6.1节:加法运算符(+)

加法运算符执行字符串连接或数字加法.

生产AdditiveExpression:AdditiveExpression + MultiplicativeExpression的计算方法如下:

  1. lref成为评估AdditiveExpression的结果.
  2. lval为GetValue(lref).
  3. rref是评估MultiplicativeExpression的结果.
  4. rval为GetValue(rref).
  5. lprim为ToPrimitive(lval).
  6. rprim为ToPrimitive(rval).
  7. 如果Type(lprim)是String或Type(rprim)是String,那么
    a.返回串联ToString(lprim)后跟ToString(rprim)的结果的字符串
  8. 将添加操作的结果返回到ToNumber(lprim)和ToNumber(rprim).见11.6.3下面的注释.

因此,如果任一值最终为a String,则ToString在两个参数(第7行)上使用,并将它们连接起来(第7a行). ToPrimitive返回所有非对象值不变,因此null并且undefined不受影响:

第9.1节ToPrimitive

抽象操作ToPrimitive接受输入参数和可选参数PreferredType.抽象操作ToPrimitive将其输入参数转换为非Object类型...根据表10进行转换:

对于所有非Object类型,包括两者NullUndefined,[t]he result equals the input argument (no conversion). 所以ToPrimitive在这里什么都不做.

最后,第9.8节ToString

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

表13给出了"undefined"Undefined类型和"null"Null类型.

它会改变吗?它甚至是"古怪"吗?

正如其他人所指出的那样,这种情况不太可能发生变化,因为它会破坏向后兼容性(并没有带来真正的好处),甚至更多,因为这种行为与规范的1997版本相同.我也不会认为这很奇怪.

如果你要改变这种行为,你会改变的定义ToStringnull,并undefined或将你的特殊情况下这些值的加法运算符? ToString在整个规范中使用了很多很多地方, "null"似乎是一个无可争议的代表选择null.举几个例子,在Java中"" + null是字符串"null",在Python中str(None)是字符串"None".

解决方法

其他人都给予很好的解决方法,但我想补充一点,我怀疑你想使用entity || ""作为自己的战略,因为它解析true"true",但false"".该阵列加入这个答案有更多的预期的行为,或者你可以改变的执行这个答案检查entity == null(包括null == nullundefined == null为真).


Aym*_*ier 5

为什么不过滤以检查真实性然后加入?

const concatObject = (obj, separator) =>
    Object.values(obj)
        .filter((val) => val)
        .join(separator);


let myAddress = {
    street1: '123 Happy St',
    street2: undefined,
    city: null,
    state: 'DC',
    zip: '20003'
}

concatObject(myAddress, ', ')
Run Code Online (Sandbox Code Playgroud)
> "123 Happy St, DC, 20003"
Run Code Online (Sandbox Code Playgroud)