JavaScriptstring串联行为,具有空值或未定义的值

正如你可能知道的,在JavaScript中'' + null = "null"'' + undefined = "undefined" (在大多数浏览器中我可以testing:Firefox,Chrome和IE)。 我想知道这个古怪的起源(Brendan Eich的头脑里到底是什么?),如果在未来的ECMA版本中有任何改变的话。 如果用'sthg' + (var || '')来连接string和variables,并使用像Underscore或其他的第三方框架那样使用锤子来敲击jelly指甲,那真是令人沮丧。

编辑:

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

  • 什么是古怪背后的历史,使JS转换为nullundefinedString串联的string值?
  • 在将来的ECMAScript版本中,是否有机会改变这种行为?
  • String与潜在的nullundefined对象连接起来而不会陷入这个问题(在String中间获得"null"一些"undefined" ,最简单的方法是什么? 我认为最主要的标准是:简短,干净,有效。 没必要说'' + (obj ? obj : '')不是很漂亮…

连接string与潜在的空或未定义的对象,而不会陷入这个问题最漂亮的方法是什么?

有几种方法,你自己部分提到了它们。 简而言之,我能想到的唯一一个简单的方法就是一个函数:

 var Strings = {}; Strings.orEmpty = function( entity ) { return entity || ""; }; // usage var message = "This is a " + Strings.orEmpty( test ); 

当然,你可以(也应该)改变实际的实现来满足你的需求。 这就是为什么我认为这种方法是优越的:它引入了封装。

真的,如果你没有封装,你只需要问一下“最漂亮”的方法。 你问自己这个问题是因为你已经知道你将要进入一个你不能改变实现的地方,所以你希望它马上变得完美。 但事情就是这样:需求,观点甚至环境都会改变。 他们在演变。 那么为什么不让自己改变实现,只需要调整一行或者一两个testing呢?

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

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

  • 你重复的代码。
  • 而且,由于您复制代码,您将很难在将来进行维护和更改。

这就是高质量软件开发中通常被称为反模式的两点。

有人会说这是太多的开销, 他们会谈论性能。 这是无意义的。 首先,这几乎没有增加开销。 如果这是你所担心的,那么你select了错误的语言。 即使jQuery使用函数。 人们需要克服微观优化。

另一件事是:你可以使用代码“compiler”= minifier。 这个领域的好工具将尝试在编译步骤中检测哪些语句要内联。 通过这种方式,您可以保持代码的清洁和可维护性,如果您仍然相信或确实有一个重要的环境,仍然可以获得最后一滴的性能。

最后,对浏览器有一定的信心。 他们会优化代码,而且这些日子他们做得相当不错。

你可以使用Array.prototype.join来忽略undefinednull

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

根据规范 :

如果元素 undefined或为null ,则让下一个为空string; 否则,让下一个 ToString( 元素 )。


鉴于,

  • 什么是古怪背后的历史,使JS转换为nullundefinedString串联的string值?

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

     function showSum(a,b) { alert(a + ' + ' + b + ' = ' + (+a + +b)); } 

    例如,如果上面的函数没有参数被调用, undefined + undefined = NaN可能比+ = NaN

    一般来说,我认为如果你想在string中插入一些variables,显示undefinednull是有意义的。 也许,艾希也是这么想的。

    当然,有些情况下忽略这些会比较好,比如将string连接在一起。 但是对于这些情况你可以使用Array.prototype.join

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

    很可能不是。

    既然已经有了Array.prototype.join ,修改string连接的行为只会造成缺点,但没有优势。 此外,它会破坏旧的代码,所以它不会向后兼容。

  • 将String与潜在的nullundefined连接起来最漂亮的方法是什么?

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

ECMA规范

只是为了充实它在规范方面performance出来的原因,这种行为从第一版开始就已经存在了 。 那里和5.1的定义在语义上是等价的,我将展示5.1的定义。

11.6.1节:加法运算符(+)

加法运算符可以执行string连接或数字加法。

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

  1. lref是评估AdditiveExpression的结果。
  2. lval成为GetValue( lref )。
  3. rref是评估MultiplicativeExpression的结果。
  4. rval是GetValue( rref )。
  5. lprim成为原文( lval )。
  6. rprim成为原始( rval )。
  7. 如果Type( lprim )是String或Type( rprim )是String,那么
    一个。 返回连接ToString( lprim )和ToString( rprim )的string,
  8. 将加法操作的结果返回给ToNumber( lprim )和ToNumber( rprim )。 见下面的注释11.6.3。

所以,如果任何一个值最终都是一个String ,那么两个参数(第7行)使用ToString ,而这些连接(第7a行)。 ToPrimitive返回所有非对象值不变,所以nullundefined是不变的:

第9.1节要原创

抽象操作ToPrimitive接受一个input参数和一个可选的参数PreferredType 。 抽象操作ToPrimitive将其input参数转换为非对象types…根据表10进行转换:

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

最后, 第9.8节ToString

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

表13给出了Undefinedtypes的"undefined"Nulltypes的"null"

会改变吗? 这是否是一个“怪异”?

正如其他人所指出的那样,这是不太可能改变的,因为它会破坏向后兼容性(并且不会带来真正的好处),甚至更多的是自从1997版规范以来这种行为是相同的。 我也不会认为这是一个怪事。

如果你要改变这种行为,你会改变ToStringnullundefined或者你会特殊情况下为这些值的加法运算符? ToString在整个规范中被使用了许多地方, "null"似乎是一个毫无争议的代表nullselect。 只是为了举几个例子,在Java中"" + null是string"null" ,在Python str(None)是string"None"

解决方法

其他人已经给出了很好的解决方法,但我会补充说,我怀疑你想要使用entity || "" entity || ""作为你的策略,因为它parsing为"true"而不是"" 。 在这个答案数组join有更多的预期的行为,或者你可以改变这个答案的实现来检查entity == nullnull == nullundefined == null都是true)。

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

我会说这个机会非常渺茫。 有几个原因:

我们已经知道ES5和ES6是什么样子了

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

只要尝试估计持续时间。 即使是大多数浏览器也不支持ES5,它可能还需要几年的时间。 ES6还没有完全规定。 出于蓝色,我们至less还要看五年。

浏览器做自己的事情

已知浏览器在某些主题上做出自己的决定。 你不知道所有的浏览器是否完全支持这个function。 当然,一旦它成为标准的一部分,你就会知道,但是到现在为止,即使它被宣布成为ES7的一部分,也只能是猜测。

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

这个变化正在打破

标准最重要的一点是他们通常试图向后兼容。 对于相同代码必须运行在各种环境中的networking,情况尤其如此。

如果这个标准引入了一个新的function,并且在旧的浏览器中不支持,那是一回事。 告诉您的客户更新浏览器以使用该网站。 但是,如果你更新你的浏览器,突然有一半的互联网为你打破,这是一个错误呃不。

当然,这个特殊的改变不太可能破坏很多脚本。 但这通常是一个糟糕的论点,因为一个标准是普遍的,必须考虑到每一个机会。 考虑一下

 "use strict"; 

作为切换到严格模式的指令。 它显示了huw努力使一切兼容的一个标准付出了很大的努力,因为他们可以使严格的模式默认(甚至只有模式)。 但是,通过这个巧妙的指令,您可以让旧的代码在没有改变的情况下运行,并且仍然可以利用新的更严格的模式。

另一个向后兼容的例子是===运算符。 ==基本上是有缺陷的(虽然有些人不同意),它可能刚刚改变了它的意思。 相反, ===被引入,允许旧代码仍然运行而不会中断。 同时允许使用更严格的检查来编写新的程序。

而对于打破兼容性的标准,必须有一个很好的理由。 哪个把我们带到

没有什么好的理由

是的,它让你感到害怕。 这是可以理解的。 但最终,这是什么也不能很容易地解决。 使用|| ,写一个函数 – 不pipe。 你几乎可以免费使用它。 那么投入大量的时间和精力来分析我们所知道的这个变化, 真的有什么好处呢? 我只是不明白这一点。

Javascript的devise有几个不足之处。 随着语言变得越来越重要和强大,它越来越成为一个更大的问题。 但是,虽然有很多很好的理由来改变它的devise,但其他的东西并不意味着要改变


免责声明:这个答案部分是基于意见的。

要添加null和'',他们需要满足一个最小公共types标准,在这种情况下是一个stringtypes。

由于这个原因,null被转换为“null”,并且由于它们是串联的,所以它们被连接起来。

数字也是一样:

4 +''='4'

因为在那里有一个string不能转换为任何数字,所以4将被转换为string。