【译】在JavaScript中{}+{}的结果是什么?

原文链接:What is {} + {} in JavaScript?

最近,Gary Bernhardt在一个名为‘Wat‘的闪电演讲中提到了一些有趣的JavaScript技巧。当你把一个object和一个object或者和一个数组相加时,会获得意想不到的结果。这篇文章将会对这些结果做解释。

在JavaScript中,对于+的普通规则很简单:你可以把数字和字符串相加,结果将会被转换成他们中的一种类型。为了理解这种类型转换时如何工作的,我首先需要了解一些其他东西。无论何时引用一个段落,它都引自ECMA-262语言标准。

让我们一起回顾下。在JavaScript中有两种变量:原始类型和引用类型。原始类型包括:null, undefined, booleans, numbers和字符串。 其他的变量都是引用类型,包括字符串和函数。

1.转换值

加号运算符表现为3种类型的转换:分别是转换为原始值,数字和字符串。

1.1. 通过ToPrimitive()将变量转换成原始值

内部方法ToPrimitive()如下:

ToPrimitive(input , PreferredType?)

可选参数PreferredType是字符串或者数字。 它只是表达一种倾向,结果可以是任何一个原始值。如果PreferredType是Number类型,它将会按照以下步骤转换:

1. 如果输入值是一个原始值,则返回它。

2. 否则, 输入值是一个对象, 调用obj.valueof()。如果结果是原始值,返回。

3.否则,调用obj.toString().如果结果是原始值,返回它。

4. 否则,抛出一个类型错误。

如果PreferredType 是一个字符串,步骤2和步骤3交换。如果PreferredType没有指明,对于日期的类型,将会被设置为String,对于其他类型的值将会被设置为Number。

1.2  通过ToNumber()将变量转换为数字

下面的表格解释了ToNumber是如何把原始值转换为number的


1.3 通过ToString()将变量转换为字符串

 一个对象通过调用ToPrimitive(obj, Number)转换为数字,然后对得到的primitive结果调用ToNumber。

下面的表格解释了ToString()是如何把原始值转换为字符串的。

一个对象通过调用ToPrimitive(obj, Number)转换为数字,然后对得到的primitive结果调用ToString。

1.4 Trying it out

下面的对象允许你观察转换的过程。

var obj = {

      valueOf: function () {

             console.log(‘valueOf’);

             return {};

       },

       toString: function() {

              console.log(‘toString’);

              return {};

       }

}

Number 作为一个函数调用(而不是作为一个构造函数)内部会调用ToNumber():

>Number(obj)

valueOf

toString

TypeError: Cannot convert object to primitive value

2. 相加

给出下面的一个加法.

value1 + value2

为了计算这个表达式, 会按照以下步骤执行:

1. 转换两个操作数为原始值(数学符号,而不是JavaScript运算符)

prim1 := ToPrimitive(value1)

prim2 := ToPrimitive(value2)

PreferredType参数被省略了,因此对于日期类型被设置为String,对于非日期类型设置为Number。

2. 如果prim1或者prim2是字符串, 那么将它们都转换为字符串,然后返回字符串连接的结果。

3. 否则, 将prim1和prim2转换为数字并返回结果的相加

2.1 意料内的结果

当你将两个数组相加, 所有的事情都会像预期一样:

[] + []

""

将[]转换为原始值,首先会尝试valueOf,她返回数组本身:

  var arr = [];

  arr.valueOf() === arr

  true 

结果不是原始值, 接下来调用toString并且返回空字符串(这个是原始值)。因此,[] + []的结果就是两个空字符串相加。

将一个数组和一个对象相加同样也符合我们的预期:

  [] + {}

  "[object object]" 

解释:将空动向转换为字符串得到如下结果。

 String({})

 "[object Object]"

因此前面的结果就是通过连接 "" 和 "[object Object]"。

看下更多的例子, 对象被转换为原始值:

5 + new Number(7)
12

6 + {valueOf: function () {return 2}}
8

"abc" + {toString: function () {return "def"} }
"abcdef"

2.2 意向不到的结果

如果+的第一个操作数是一个空对象,事情就变得奇怪了(结果正如你在Firefox的console中所见)

{} + {}
NaN 

在这为什么会这样?问题就是JavaScript把第一个{}解释成了一个空的代码块,并且忽略它。 因此NaN结果就是通过计算+{}得到的(加上第二个{})。你在这看的加不是一个二进制相加运算符,而是一个一元运算符,她将操作数转换为数字,和Number()是一样的。例如:

+ “3.65”
3.65

下面的表达式是等价的:

  + {}

  Number({})

  Number({}.toString()) // {}.valueOf() 不是一个原始值

  Number(“[object Object]”)

  NaN

为什么第一个{}会被解释成空代码块?因为完整的输入被解析为一个语句并且大括号在欲绝的开始被解释为一个代码块的开始。因此,你可以通过强制的将它变为一个表达式来解决。

({} + {})
"[object Object][object Object]"

函数或者方法的参数同样被解释为表达式:

console.log({} + {})
"[object Object][object Object]"

有了前面的解释, 你应该不会再对下面这个结果感到惊讶了:

{} + []
0 

再来一次, 这个被解释为+ []. 下面的表达式是等价的:

 + []

 Number([])

 Number( [].toString()) // [].valueOf() 不是一个原始值

 Number(“ “)

 0

有趣的是, 在Node.js的REPL中, 会和FireFox或者Chrome(它甚至和Node.js同样用v8引擎)有不同的解释. 下面的输入被解析为表达式,结果令人有点小吃惊

{} + {}

"[object Object][object Object]"

{} + []

"[object Object]"

它有一个优势就是结果更像是把表达式作为console.log的参数。但是它却不太像在程序中使用这个输入作为语句。

 3.  What does it all mean?

在大多数情况中, 在JavaScript中理解+是如何工作的并不困难: 你可以仅仅是把数字或者字符串相加。对象被转换为字符串(如果有一个操作数是字符串)或者数字。如果你想连接两个数组, 你需要使用方法:

[1, 2].concat([3, 4])

[1, 2, 3, 4]

在JavaScript中没有一个内部方法来连接(merge)对象。你需要使用一个库,例如Underscore:

var o1 = {eeny:1, meeny:2};

var o2 = {miny:3, moe: 4};

_.extend(o1, o2)

    { eeny: 1,

      meeny: 2,

      miny: 3,

      moe: 4 }

注释: 和Array.prototype.concat()相比,extend()改变了第一个参数:

o1

    { eeny: 1,

      meeny: 2,

      miny: 3,

      moe: 4 }

o2

{ miny: 3, moe: 4 }

如果你对操作符有更大的兴趣, 你可以阅读这篇文章"Fake operator overloading in JavaScript"

 (PS. 翻译可能会有个别错误或者不恰当, 还望指出,谢谢)

时间: 2024-07-30 13:22:29

【译】在JavaScript中{}+{}的结果是什么?的相关文章

【译】Javascript中的数据类型

这篇文章通过四种方式获取Javascript中的数据类型:通过隐藏的内置[[Class]]属性:通过typeof运算符:通过instanceof运算符:通过函数Array.isArray().我们也会看看原型对象的构造函数,可能会有意想不到的数据类型结果. [这篇文章是我在adobe发布的文章,我发布在这里只是为了存档.] 知识储备 在开始我们的话题之前,我们不得不复习一些所需的知识点 1.1 原始值和对象 Javascript中的数据,要么是原始值,要么是对象. 原始值.下面是原始值: und

[译]在Javascript中进行日期相关的操作

本文翻译youtube上的up主kudvenkat的javascript tutorial播放单 源地址在此: https://www.youtube.com/watch?v=PMsVM7rjupU&list=PL6n9fhu94yhUA99nOsJkKXBqokT3MBK0b 在Javascript里制造date对象的话要用到Date() constructor 以下的例子将当前的日期和时间显示在页面上 document.write(new Date()); 如果Date() construc

(译)JavaScript 中的正则表达式(RegEx)实操——快速掌握正则表达式,伴有随手可练的例子————(翻译未完待续)

(原文:https://blog.bitsrc.io/a-beginners-guide-to-regular-expressions-regex-in-javascript-9c58feb27eb4) 当你第一次看到正则,它们就像随意堆放的字符,看起来毫无意义.不过尽管他们看起来很棘手(因为复杂的语法规则),他们却极其有用. 事实是,正确地理解了正则表达式,能让你成为一个更加高明的程序员.为了完全了解正则表达式的世界,你需要先学习一些基本概念,在此基础上才能有所作为. 废话不多说,让我们开始吧

[译]JavaScript中,{}+{}等于多少?

[译]JavaScript中,{}+{}等于多少? 原文:http://www.2ality.com/2012/01/object-plus-object.html 最近,Gary Bernhardt在一个简短的演讲视频“Wat”中指出了一个有趣的JavaScript怪癖:在把对象和数组混合相加时,会得到一些你意想不到的结果.本篇文章会依次讲解这些计算结果是如何得出的. 在JavaScript中,加法的规则其实很简单,只有两种情况:你只能把数字和数字相加,或者字符串和字符串相加,所有其他类型的值

译:理解并掌握 JavaScript 中 this 的用法

原文链接:http://javascriptissexy.com/understand-javascripts-this-with-clarity-and-master-it/ 文章描述:本文原文来自 Javascript.isSexy 这个网站.这篇文章和文中提到的另一篇文章解决了我一直以来对 this 和 apply, call, bind 这三个方法的困惑.我看过很多国内相关的技术文章,没有一篇能让我彻底理解这些概念的.因此我决定把它译过来,不要让更多的初学者像我一样在这个问题上纠结太长时

javaScript中的严格模式 (译)

“use strict”状态指示浏览器使用严格模式,是javaScript中一个相对少且安全的特征集. 特征列表(非完全列举) 不允许定义全局变量.(捕获没有用var声明的变量和变量名的拼写错误) 在严格模式下引起静默失败的声明将会抛出异常(声明 NaN  =  5) 试图删除不能删除的属性将会抛出异常(delete  object . prototype) 在一个对象中的所有属性名要唯一(var x = {x1 :  “1”,  x1 : “2”}) 函数的参数名必须唯一(function s

【转】【译】JavaScript魔法揭秘--探索当前流行框架中部分功能的处理机制

推荐语: 今天推荐一篇华为同事的同事翻译的一篇文章,推荐的主要原因是作为一个华为员工居然晚上还能写文章,由不得小钗不佩服!!! 其中的jQuery.angular.react皆是十分优秀的框架,各有特点,各位可以看看 编辑:github 原文链接:Revealing the Magic of JavaScript jnotnull发布在 JavaScript译文 我们每天都在使用大量的工具,不同的库和框架已经成为我们日常工作的一部分.我们使用他们是因为我们不想重新造轮子,虽然我们可能并不知道这些

JS函数式编程【译】4.在Javascript中实现函数式编程的技术

?? Functional Programming in Javascript 主目录上一章 建立函数式编程环境 第四章 在Javascript中实现函数式编程的技术 扶好你的帽子,我们现在要真正进入函数式的思想了. 这章我们继续下面的内容: 把所有的核心概念放到一个集中的范式里 探索函数式编程之美 一步步跟踪函数式模式相互交织的逻辑 我们将贯穿整章建立一个简单的应用做一些很酷的事情 你可能已经注意到,在上一章我们介绍Javascript的函数式库的时候引入了一些概念, 而不是在第二章<函数式编

[译]Javascript中的错误信息处理(Error handling)

本文翻译youtube上的up主kudvenkat的javascript tutorial播放单 源地址在此: https://www.youtube.com/watch?v=PMsVM7rjupU&list=PL6n9fhu94yhUA99nOsJkKXBqokT3MBK0b 在Javascript中使用try/catch/finally来处理runtime的错误.这些runtime错误被称为exceptions.各种各样的原因都可能导致exception.比如,使用没有申明的变量或者方法都可