风君子博客

  • 首页
  • 业界
  • 前端
  • 运维
  • 建站
  • 软件
  • 生活
  • 后端
  • 创投
  • 运营
  • 程序人生
    • 影视
    • 游戏
    • 句子
    • bootstrap4教程
    • 资源
    • 日期计算器
  • 其他
    • 说说
    • 关于本站
  1. 首页
  2. 前端
  3. 正文

JS的{} + {}与{} + []的结果是什么?js数据类型转换详解

2018年10月19日 847点热度 0人点赞 0条评论

原文链接:https://segmentfault.com/a/1190000008038678  作者:eyesofkids,结果分析可以直接跳到文章尾部查看

在JS中的运算符共同的情况中,(+)符号是很常见的一种,它有以下的使用情况:

  • 数字的加法运算,二元运算
  • 字符串的连接运算,二元运算,最高优先
  • 正号,一元运算,可延伸为强制转换其他类型的运算元为数字类型

当然,如果考虑多个符号一起使用时,(+=)与(++)又是另外的用途。

另一个常见的是花括号({}),它有两个用途也很常见:

  • 对象的字面文字定义
  • 区块语句

所以,要能回答这个问题,要先搞清楚重点是什么?

第一个重点是:

加号(+)运算在JS中在使用上的规定是什么。

第二个重点则是:

对象在JS中是怎么转换为原始数据类型的值的。

加号运算符(+)

除了上面说明的常见情况外,在标准中转换的规则还有以下几个,要注意它的顺序:

operand + operand = result

  1. 使用ToPrimitive运算转换左与右运算元为原始数据类型值(primitive)
  2. 在第1步转换后,如果有运算元出现原始数据类型是"字符串"类型值时,则另一运算元作强制转换为字符串,然后作字符串的连接运算(concatenation)
  3. 在其他情况时,所有运算元都会转换为原始数据类型的"数字"类型值,然后作数学的相加运算(addition)

ToPrimitive内部运算

因此,加号运算符只能使用于原始数据类型,那么对于对象类型的值,要如何转换为原始数据类型?下面说明是如何转换为原始数据类型的。

在ECMAScript 6th Edition #7.1.1,有一个抽象的ToPrimitive运算,它会用于对象转换为原始数据类型,这个运算不只会用在加号运算符,也会用在关系比较或值相等比较的运算中。下面有关于ToPrimitive的说明语法:

ToPrimitive(input, PreferredType?)

input代表代入的值,而PreferredType可以是数字(Number)或字符串(String)其中一种,这会代表"优先的"、"首选的"的要进行转换到哪一种原始类型,转换的步骤会依这里的值而有所不同。但如果没有提供这个值也就是预设情况,则会设置转换的hint值为"default"。这个首选的转换原始类型的指示(hint值),是在作内部转换时由JS视情况自动加上的,一般情况就是预设值。

而在JS的Object原型的设计中,都一定会有两个valueOf与toString方法,所以这两个方法在所有对象里面都会有,不过它们在转换有可能会交换被调用的顺序。

当PreferredType为数字(Number)时

当PreferredType为数字(Number)时,input为要被转换的值,以下是转换这个input值的步骤:

  1. 如果input是原始数据类型,则直接返回input。
  2. 否则,如果input是个对象时,则调用对象的valueOf()方法,如果能得到原始数据类型的值,则返回这个值。
  3. 否则,如果input是个对象时,调用对象的toString()方法,如果能得到原始数据类型的值,则返回这个值。
  4. 否则,抛出TypeError错误。

当PreferredType为字符串(String)时

上面的步骤2与3对调,如同下面所说:

  1. 如果input是原始数据类型,则直接返回input。
  2. 否则,如果input是个对象时,调用对象的toString()方法,如果能得到原始数据类型的值,则返回这个值。
  3. 否则,如果input是个对象时,则调用对象的valueOf()方法,如果能得到原始数据类型的值,则返回这个值。
  4. 否则,抛出TypeError错误。

PreferredType没提供时,也就是hint为"default"时

与PreferredType为数字(Number)时的步骤相同。

数字其实是预设的首选类型,也就是说在一般情况下,加号运算中的对象要作转型时,都是先调用valueOf再调用toString。

但这有两个异常,一个是Date对象,另一是Symbol对象,它们覆盖了原来的PreferredType行为,Date对象的预设首选类型是字符串(String)。

因此你会看到在一些教程文件上会区分为两大类对象,一类是 Date 对象,另一类叫 非Date(non-date) 对象。因为这两大类的对象在进行转换为原始数据类型时,首选类型恰好相反。

模拟代码说明

以简单的模拟代码来说明,加号运算符(+)的运行过程就是像下面这个模拟码一样,我想这会很容易理解:

a + b:
    pa = ToPrimitive(a)
    pb = ToPrimitive(b)

    if(pa is string || pb is string)
       return concat(ToString(pa), ToString(pb))
    else
       return add(ToNumber(pa), ToNumber(pb))

步骤简单来说就是,运算元都用ToPrimitive先转换为原始数据类型,然后其一是字符串时,使用ToString强制转换另一个运算元,然后作字符串连接运算。要不然,就是都使用ToNumber强制转换为数字作加法运算。

而ToPrimitive在遇到对象类型时,预设调用方式是先调用valueOf再调用toString,一般情况数字类型是首选类型。

上面说的ToString与ToNumber这两个也是JS内部的抽象运算。

valueOf与toString方法

valueOf与ToString是在Object中的两个必有的方法,位于Object.prototype上,它是对象要转为原始数据类型的两个姐妹方法。从上面的内容已经可以看到,ToPrimitive这个抽象的内部运算,会依照设置的首选的类型,决定要先后调用valueOf与toString方法的顺序,当数字为首选类型时,优先使用valueOf,然后再调用toString。当字符串为首选类型时,则是相反的顺序。预设调用方式则是如数字首选类型一样,是先调用valueOf再调用toString。

JS对于Object与Array的设计

在JS中所设计的Object纯对象类型的valueOf与toString方法,它们的返回如下:

  • valueOf方法返回值: 对象本身。
  • toString方法返回值: "[object Object]"字符串值,不同的内建对象的返回值是"[object type]"字符串,"type"指的是对象本身的类型识别,例如Math对象是返回"[object Math]"字符串。但有些内建对象因为覆盖了这个方法,所以直接调用时不是这种值。(注意: 这个返回字符串的前面的"object"开头英文是小写,后面开头英文是大写)

你有可能会看过,利用Object中的toString来进行各种不同对象的判断语法,这在以前JS能用的函数库或方法不多的年代经常看到,不过它需要配合使用函数中的call方法,才能输出正确的对象类型值,例如:

> Object.prototype.toString.call([])
"[object Array]"

> Object.prototype.toString.call(new Date)
"[object Date]"

所以,从上面的内容就可以知道,下面的这段代码的结果会是调用到toString方法(因为valueOf方法的返回并不是原始的数据类型):

> 1 + {}
"1[object Object]"

一元正号(+),具有让首选类型(也就是hint)设置为数字(Number)的功能,所以可以强制让对象转为数字类型,一般的对象会转为:

> +{} //相当于 +"[object Object]"
NaN

当然,对象的这两个方法都可以被覆盖,你可以用下面的代码来观察这两个方法的运行顺序,下面这个都是先调用valueOf的情况:

let obj = {
  valueOf: function () {
      console.log('valueOf');
      return {}; // object
  },
  toString: function () {
      console.log('toString');
      return 'obj'; // string
  }
}
console.log(1 + obj);  //valueOf -> toString -> '1obj'
console.log(+obj); //valueOf -> toString -> NaN
console.log('' + obj); //valueOf -> toString -> 'obj'

先调用toString的情况比较少见,大概只有Date对象或强制要转换为字符串时才会看到:

let obj = {
  valueOf: function () {
      console.log('valueOf');
      return 1; // number
  },
  toString: function () {
      console.log('toString');
      return {}; // object
  }
}
alert(obj); //toString -> valueOf -> alert("1");
String(obj); //toString -> valueOf -> "1";

而下面这个例子会造成错误,因为不论顺序是如何都得不到原始数据类型的值,错误消息是"TypeError: Cannot convert object to primitive value",从这个消息中很明白的告诉你,它这里面会需要转换对象到原始数据类型:

let obj = {
  valueOf: function () {
      console.log('valueOf');
      return {}; // object
  },
  toString: function () {
      console.log('toString');
      return {}; // object
  }
}

console.log(obj + obj);  //valueOf -> toString -> error!

Array(数组)很常用到,虽然它是个对象类型,但它与Object的设计不同,它的toString有覆盖,说明一下数组的valueOf与toString的两个方法的返回值:

  • valueOf方法返回值: 对象本身。(与Object一样)
  • toString方法返回值: 相当于用数组值调用join(',')所返回的字符串。也就是[1,2,3].toString()会是"1,2,3",这点要特别注意。

Function对象很少会用到,它的toString也有被覆盖,所以并不是Object中的那个toString,Function对象的valueOf与toString的两个方法的返回值:

  • valueOf方法返回值: 对象本身。(与Object一样)
  • toString方法返回值: 函数中包含的代码转为字符串值

Number、String、Boolean三个包装对象

包装对象是JS为原始数据类型数字、字符串、布尔专门设计的对象,所有的这三种原始数据类型所使用到的属性与方法,都是在这上面所提供。

包装对象的valueOf与toString的两个方法在原型上有经过覆盖,所以它们的返回值与一般的Object的设计不同:

  • valueOf方法返回值: 对应的原始数据类型值
  • toString方法返回值: 对应的原始数据类型值,转换为字符串类型时的字符串值

toString方法会比较特别,这三个包装对象里的toString的细部说明如下:

  • Number包装对象的toString方法: 可以有一个传参,可以决定转换为字符串时的进位(2、8、16)
  • String包装对象的toString方法: 与String包装对象中的valueOf相同返回结果
  • Boolean包装对象的toString方法: 返回"true"或"false"字符串

另外,常被搞混的是直接使用Number()、String()与Boolean()三个强制转换函数的用法,这与包装对象的用法不同,包装对象是必须使用new关键字进行对象实例化的,例如new Number(123),而Number('123')则是强制转换其他类型为数字类型的函数。

Number()、String()与Boolean()三个强制转换函数,所对应的就是在ECMAScript标准中的ToNumber、ToString、ToBoolean三个内部运算转换的对照表。而当它们要转换对象类型前,会先用上面说的ToPrimitive先转换对象为原始数据类型,再进行转换到所要的类型值。

不管如何,包装对象很少会被使用到,一般我们只会直接使用原始数据类型的值。而强制转换函数因为也有替换的语法,它们会被用到的机会也不多。

实例

字符串 + 其他原始类型

字符串在加号运算有最高的优先运算,与字符串相加必定是字符串连接运算(concatenation)。所有的其他原始数据类型转为字符串,可以参考ECMAScript标准中的ToString对照表,以下为一些简单的例子:

> '1' + 123
"1123"

> '1' + false
"1false"

> '1' + null
"1null"

> '1' + undefined
"1undefined"

数字 + 其他的非字符串的原始数据类型

数字与其他类型作相加时,除了字符串会优先使用字符串连接运算(concatenation)的,其他都要依照数字为优先,所以除了字符串之外的其他原始数据类型,都要转换为数字来进行数学的相加运算。如果明白这项规则,就会很容易的得出加法运算的结果。

所有转为数字类型可以参考ECMAScript标准中的ToNumber对照表,以下为一些简单的例子:

> 1 + true //true转为1, false转为0
2

> 1 + null //null转为0
1

> 1 + undefined //undefined转为NaN
NaN

数字/字符串以外的原始数据类型作加法运算

当数字与字符串以外的,其他原始数据类型直接使用加号运算时,就是转为数字再运算,这与字符串完全无关。

> true + true
2

> true + null
1

> undefined + null
NaN

空数组 + 空数组

> [] + []
""

两个数组相加,依然按照valueOf -> toString的顺序,但因为valueOf是数组本身,所以会以toString的返回值才是原始数据类型,也就是空字符串,所以这个运算相当于两个空字符串在相加,依照加法运算规则第2步骤,是字符串连接运算(concatenation),两个空字符串连接最后得出一个空字符串。

空对象 + 空对象

> {} + {}
"[object Object][object Object]"

两个空对象相加,依然按照valueOf -> toString的顺序,但因为valueOf是对象本身,所以会以toString的返回值才是原始数据类型,也就是"[object Object]"字符串,所以这个运算相当于两个"[object Object]"字符串在相加,依照加法运算规则第2步骤,是字符串连接运算(concatenation),最后得出一个"object Object"字符串。

但是这个结果有异常,上面的结果只是在Chrome浏览器上的结果(v55),怎么说呢?

有些浏览器例如Firefox、Edge浏览器会把{} + {}直译为相当于+{}语句,因为它们会认为以花括号开头({)的,是一个区块语句的开头,而不是一个对象字面量,所以会认为略过第一个{},把整个语句认为是个+{}的语句,也就是相当于强制求出数字值的Number({})函数调用运算,相当于Number("[object Object]")运算,最后得出的是NaN。

特别注意: {} + {}在不同的浏览器有不同结果

如果在第一个(前面)的空对象加上圆括号(()),这样JS就会认为前面是个对象,就可以得出同样的结果:

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

或是分开来先声明对象的变量值,也可以得出同样的结果,像下面这样:

> let foo = {}, bar = {};
> foo + bar;

注: 上面说的行为这与加号运算的第一个(前面)的对象字面值是不是个空对象无关,就算是里面有值的对象字面,例如{a:1, b:2},也是同样的结果。

注: 上面说的Chrome浏览器是在v55版本中的主控台直接运行的结果。其它旧版本有可能并非此结果。

空对象 + 空数组

上面同样的把{}当作区块语句的情况又会发生,不过这次所有的浏览器都会有一致结果,如果{}(空对象)在前面,而[](空数组)在后面时,前面(左边)那个运算元会被认为是区块语句而不是对象字面量。

所以{} + []相当于+[]语句,也就是相当于强制求出数字值的Number([])运算,相当于Number("")运算,最后得出的是0数字。

> {} + []
0

> [] + {}
"[object Object]"

特别注意: 所以如果第一个(前面)是{}时,后面加上其他的像数组、数字或字符串,这时候加号运算会直接变为一元正号运算,也就是强制转为数字的运算。这是个陷阱要小心。

Date对象

Date对象的valueOf与toString的两个方法的返回值:

  • valueOf方法返回值: 给定的时间转为UNIX时间(自1 January 1970 00:00:00 UTC起算),但是以微秒计算的数字值
  • toString方法返回值: 本地化的时间字符串

Date对象上面有提及是首选类型为"字符串"的一种异常的对象,这与其他的对象的行为不同(一般对象会先调用valueOf再调用toString),在进行加号运算时时,它会优先使用toString来进行转换,最后必定是字符串连接运算(concatenation),例如以下的结果:

> 1 + (new Date())
> "1Sun Nov 27 2016 01:09:03 GMT+0800 (CST)"

要得出Date对象中的valueOf返回值,需要使用一元加号(+),来强制转换它为数字类型,例如以下的代码:

> +new Date()
1480180751492

Symbols类型

ES6中新加入的Symbols数据类型,它不算是一般的值也不是对象,它并没有内部自动转型的设计,所以完全不能直接用于加法运算,使用时会报错。

总结

{} + {}的结果是会因浏览器而有不同结果,Chrome(v55)中是[object Object][object Object]字符串连接,但其它的浏览器则是认为相当于+{}运算,得出NaN数字类型。

{} + []的结果是相当于+[],结果是0数字类型。

参考文章

  • What is {} + {} in JavaScript?

 

标签: js 前端 数据类型
最后更新:2019年6月6日

风君子

独自遨游何稽首 揭天掀地慰生平

点赞
< 上一篇
下一篇 >

猜你喜欢

  • 基金a类b类c类什么意思

  • 深港通和沪港通的区别是什么

  • 前端小哥玩 HTML 复选框上瘾:能画 logo 做视频,还开源成 JS 库

  • 货币基金abc的区别

  • 华为 HarmonyOS 开源鸿蒙方舟编译器前端工具“方舟 JS 运行时”正式开源

  • 前融是什么意思

  • 禁止鼠标右键代码 禁止按键F5和禁止鼠标右键菜单 js代码

  • 什么是房地产前端融资

  • 国内首家!飞骧科技推出完整5G射频前端解决方案

  • JS基础测试: typeof delete window.name 的返回结果是什么?​

  • SA:射频前端元件市场增长停滞

  • 极致延伸的5G频谱正在改写射频前端价值

文章评论

取消回复

风君子

独自遨游何稽首 揭天掀地慰生平

最新 热点 随机
最新 热点 随机
分析师:苹果 iPhone 需求仍强劲,iPhone 14 / Pro 系列初期量产计划会高于 iPhone 13 / Pro 国内首发锐炫 A370M 显卡,雷神推出新款 T-Book 轻薄创作本 苹果委托的研究称:App Store 让小企业和独立开发者受益,收入比大企业增长更快 美股科技股暴跌让卖空者收益颇丰:34% 的利润来自 IT 股 苹果斥资数百万美元安置无家可归者,9 个月期满后仅安置 8 人 机械革命预热水冷游戏本旷世 X:12 代酷睿 HX + RTX 3080 Ti,17 英寸 2.5K 240Hz 屏 苹果新专利:未来 Apple Pencil 配备全触控式控制 RTX 40 系列守口如瓶?NVIDIA 台北电脑展官宣 12 款游戏支持 DLSS,500Hz G-SYNC 显示器问世 《电子竞技产业报告》研讨会召开,腾讯、网易、完美等多家相关企业参加 F1 赛车手通过苹果“Find My”追踪盗窃 AirPods 耳机的小偷
抖音IP属地准确吗e地跑:全国线路覆盖率达92.5%,将升级智能扫码加油功能完成加油监控米哈游《原神》全球用户支出将突破 27 亿美元:打破最高季度纪录,中美日韩德支出最多索尼 QD-OLED 画谛系列电视 A95K 国行即将发布格力电器 2021 年营收 1878.69 亿元同比增长 11.69%施耐德电气顾俊:5G赋能,制造业智能化转型“加速”苹果将 iPad Air 2 和 iPad Mini 2 加入过时产品名单IDC:2021 年中国网络市场规模达 102.4 亿美元,路由器市场同比下滑 2.6%波导股份第一季度净利润 552.94 万元,同比增长 75.79%我们常用谁言寸草心报得三春晖歌颂母爱,它的作者是
工信部:一季度我国手机产量 3.6 亿台,同比下降 0.8% 我们如何发现悄悄靠近地球的小行星 中兴通讯5G助力中国移动实现江西省首个5G远程会诊 台积电:11 月 8 日前提交商业数据给美国 安徽省 2022 年重点项目投资计划公布,京东方、维信诺等项目在列 为什么你应该从现在开始就写博客 房贷还完了还需要办什么手续吗 魏世忠委员:建议将网络弹窗平台纳入失信名单 存储系列业务爆发:国科微上半年营收约 9.52 亿元,同比增长 393% 中国银行数字信用卡怎么用
标签聚合
腾讯 借款人 小米 三星 华为 信用卡 银行卡 手机 贷款 微软 芯片 秘籍 iphone 汽车 利息 股票 谷歌 京东 银行 苹果 利率 额度 快科技 支付宝 特斯拉 美国 IT资讯 科技 马斯克 业界
Tags
酒 胡萝卜 食物 私房菜 有什么好处 水果 能吃 鸡蛋 身体 会胖吗 副作用 吃什么 相克 好处 饮食 作用 土豆食谱 方法 牛肉 豆腐 糖 热量 茄子 好吃 营养 皮肤过敏 区别 菜谱 牛肉食谱 功效 怎么做 营养价值 豆腐食谱 维生素 土豆 热菜 食用方法 常吃 健康 排骨 汤类 高血压 牛奶 糖尿病人 做法 吃法 下饭菜 海鲜 一起吃 排骨食谱
最近评论
九九 发布于 3 周前(05月06日) 也可能是 IP2LOCATION 数据
风君子 发布于 5 个月前(12月20日) 直播这么赚钱的吗?
wanif 发布于 5 个月前(12月18日) 采用你提供的pe制作u盘后无法识别mac硬盘,如何解决?
robotunl 发布于 5 个月前(12月18日) 可以用了,感谢
robotunl 发布于 5 个月前(12月17日) DISM++链接下不了了啊,能再发一个么。 还有怎么制作和博主一样的PE u盘。
tiantian 发布于 10 个月前(08月08日) 谢谢分享!
欣姐 发布于 1 年前(02月06日) 很有道理啊
我在東莞上班睡覺工資一千五六 发布于 1 年前(12月16日) 东莞台商都开始搬迁河源,梅州包括赣州一些地方要么就是内地城市,因为电子厂需要大量人力所以基本上都是中...
我在東莞上班睡覺工資一千五六 发布于 1 年前(12月16日) 这个怎么有意思了,是不是太真实了 :smile: :smile:
东莞石碣维信五金制品有限公司睡觉一千五六 发布于 1 年前(12月15日) 台商大陆模式搞习惯了去了印度未必成功 :idea:

COPYRIGHT © 2021 风君子博客. ALL RIGHTS RESERVED.

豫ICP备14018241号