博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
前端进阶系列(第2期):深入理解JavaScript数据类型转换
阅读量:6702 次
发布时间:2019-06-25

本文共 6409 字,大约阅读时间需要 21 分钟。

上一期中我们主要是了解了JavaScript中存在两大数据类型:基本类型引用类型以及其存储的方式(堆和栈)。

本期我们将重点谈谈JavaScript数据类型转换过程出现的各种“奇葩”的问题。

写在前面

在JavaScript中当运算符在运算时,如果两边数据不统一,CPU就无法计算,这时我们编译器会自动将运算符两边的数据做一个数据类型转换,转成一样的数据类型再计算,这种无需程序员手动转换,而由编译器自动转换的方式就称为隐式转换

在JavaScript中“一切皆是对象”,在我们具体了解隐式转换前先了解一下对象的两个方法:toString()valueOf()

toString()

toString() 方法返回一个表示该对象的字符串。

// 数字转字符串(123).toString() // '123'// 布尔值转字符串(true).toString() // 'true'// 数组转字符串['hello', 'world'].toString() // 'hello,world'// 对象转字符串({
name: 'hello world'}).toString() // '[object Object]'//日期对象转字符串Date().toString() // 'Wed Jan 23 2019 21:10:42 GMT+0800 (China Standard Time)'//JSON对象转字符串JSON.toString() // '[object JSON]'// Function转字符串Function.toString() // 'function Function() { [native code] }'// 函数转字符串(function(){ return 1; }).toString() // 'function () { return 1; }'复制代码

valueOf()

valueOf() 方法返回指定对象的原始值。

JavaScript调用valueOf方法将对象转换为原始值。你很少需要自己调用valueOf方法;当遇到要预期的原始值的对象时,JavaScript会自动调用它。

默认情况下,valueOf方法由Object后面的每个对象继承。 每个内置的核心对象都会覆盖此方法以返回适当的值。如果对象没有原始值,则valueOf将返回对象本身。

JavaScript的许多内置对象都重写了该函数,以实现更适合自身的功能需要。因此,不同类型对象的valueOf()方法的返回值和返回值类型均可能不同。

// 数字的原始值(123).valueOf() // 123// 布尔值的原始值(true).valueOf() // true// 数组的原始值['hello', 'world'].valueOf() // [ 'hello', 'world' ]// 对象的原始值({
name: 'hello world'}).valueOf() // { name: 'hello world' }//日期对象的原始值Date().valueOf() // 'Wed Jan 23 2019 21:10:42 GMT+0800 (China Standard Time)'//JSON的原始值JSON.valueOf() // 'Object [JSON] {}'// Function的原始值Function.valueOf() // '[Function: Function]'// 函数的原始值(function func(){ return 1; }).valueOf() // '[Function: func]'复制代码

隐式转换规则

  1. 转成string类型:+(字符串连接符)
  2. 转成number类型:++/--(自增或自减运算符)、+ - * / % (算术运算符)、> < >= <= == != === !== (关系运算符)
  3. 转成boolean类型:!(逻辑非运算符)

字符串 VS 加号连接符

字符串 + 基本类型 = 字符串 + String(基本类型)

// 字符串 + 数字console.log('hello' + 123) // 'hello' + '123'// 字符串 + 布尔console.log('hello' + true) // 'hello' + 'true'// 字符串 + nullconsole.log('hello' + null) // 'hello' + 'null'// 字符串 + undefinedconsole.log('hello' + undefined) // 'hello' + 'undefined'复制代码

数字 VS 加号连接符

数字 + 基本类型(非字符串) = 数字类型 + Number(基本类型)

// 数字 + 布尔console.log(1 + true) // 2// 等同于console.log(1 + Number(true)) // 1 + 1// 数字 + undefinedconsole.log(1 + undefined) // NaN// 等同于console.log(1 + Number(undefined)) // 1 + NaN// 数字 + nullconsole.log(1 + null) // 1// 等同于console.log(1 + Number(null)) // 1 + 0复制代码

数字 + 引用类型 = 数字 + 引用类型.toString()

// 数字 + 数组console.log(1 + []) // '1'// 等同于console.log(1 + [].toString()) // 1 + '' = '1'// 数字 + 对象console.log(1 + {}) // '1[object Object]'// 等同于console.log(1 + ({}).toString()) // 1 + '[object Object]'复制代码

数字类型 + 函数 = 数字类型 + String(函数)

// 数字 + 函数var func = function() { var a = 2; return 2; }console.log(1 + func); // 1function () {var a = 2;  return 2;}复制代码

关系运算符的隐式转换

规则:将其他数据类型转换成数字类型之后再比较关系

// 字符串 vs 数字 = Number(字符串) vs 数字console.log('2' > 10); // flase// 等同于console.log(Number('2') > 10) // 2 > 10// 字符串(数字) vs 字符串(数字) = ASCII码(对应值) vs ASCII码(对应值)console.log('2' > '10'); // true// 等同于console.log('2'.charCodeAt() > '10'.charCodeAt) // 50 > 49// 字符串(字母) vs 字符串(字母) = ASCII码(对应值) vs ASCII码(对应值)console.log('abc' > 'b'); // false// 等同于console.log('abc'.charCodeAt() > 'b'.charCodeAt()) // 97 > 98// NaN 不等于 NaNconsole.log(NaN == NaN) // false// undefined vs nullconsole.log(undefined == null) // true//注意:undefined == null 不等同于console.log(Number(undefined) == Number(null)) // false NaN == 0// 这里JavaScript的特殊约定的结果,undefined == null,详情可以查看更多资料// https://codeburst.io/javascript-null-vs-undefined-20f955215a2复制代码

逻辑非与关系运算符的隐式转换

// 数字 vs 数组 = 数字 vs Number(数组)console.log([] == 0); // true// 等同于console.log(Number([]) == 0) // 0 == 0// 数字 vs 布尔 = 数字 vs Number(布尔)console.log(![] == 0); //true// 等同于console.log(Number(![]) == 0) // Number(false) == 0 // 引用类型 != 引用类型console.log([] == []); // false// 逻辑非隐式转换console.log([] == ![]); // true// 等同于console.log(Number([]) == Number(!Boolean([]))) // 0 == 0// 逻辑非隐式转换console.log({} == !{}); // false// 等同于console.log(Number({}) == Number(!Boolean({}))) // NaN == 0 复制代码

引用类型的隐式转换

规则:

  1. 当引用类型的valueOf()调用时返回的值是一个基本类型时,则直接进行运算。
  2. 当引用类型的valueOf()调用时返回的值不是一个基本类型时,则引用类型的toString()将会被调用并返回转换后的字符串,然后再进行运算。
// 字符串 + 数组console.log('hello' + []) // 'hello' + [].toString()// 等同于console.log('hello' + [].toString()) // 'hello' + ''// 字符串 + 对象console.log('hello' + {}) // 'hello[object Object]'// 等同于console.log('hello' + ({}).toString()) // 'hello' + '[object Object]'复制代码

案例一:

目的:验证非自定义对象的隐式转换过程

// 申明一个对象obj1var obj1 = { age: 18 }console.log(10 + obj1) // '10[object Object]'复制代码

第一步:判断对象的valueOf()返回值是否是基本类型

console.log(obj1.valueOf()) // { age: 18 }console.log(typeof obj1.valueOf()) // object 返回的是一个对象复制代码

第二步:调用对象的toString()返回一个表示该对象的字符串

console.log(obj1.toString()) // '[object Object]'复制代码

第三步:根据运算规则进行运算(非字符连接操作都转换成Number进行运算)

// 因为obj1.toString() 返回的是字符串,所以进行字符串连接操作console.log(10 + obj1.toString()) // 10 + '[object Object]'复制代码

案例二:

目的:通过自定义对象的valueOf()和toString(),来验证对象的隐式转换过程

// 申明一个对象obj2var obj2 = {     age: 18    toString: function() {        return '' + this.age;       },    valueOf: function() {        return this.age;    }}console.log(10 + obj2) // 10 + 18 = 28复制代码

第一步:判断对象的valueOf()返回值是否是基本类型

console.log(obj2.toString()) // '18'console.log(typeof obj2.toString()) // stringconsole.log(obj2.valueOf()) // 18console.log(typeof obj2.valueOf()) // number复制代码

第二步:如果第一步能正确返回基本类型,则直接跳到第三步,否则将调用对象的toString()返回一个表示该对象的字符串

// 如果对象的valueOf()返回的是基本类型console.log(10 + obj2) // 10 + obj2.valueOf()// 如果对象的valueOf()返回的是引用类型console.log(10 + obj2) // 10 + obj2.toString()复制代码

第三步:根据运算规则进行运算(非字符连接操作都转换成Number进行运算)

// 如果obj2的返回值是字符串,都进行字符串 VS 加号规则console.log(10 + '18') // 10 + String(obj2)// 如果obj2的返回值是非字符串,都进行数字 VS 加号规则console.log(10 + obj2) // 10 + Number(obj2)复制代码

特殊说明

JavaScript中存在几个特殊的原始值:null、undefined、''、0、NaN。

console.log(typeof null) // objectconsole.log(null instanceof Object) // falseconsole.log(NaN == NaN) // falseconsole.log(null == undefined) // trueconsole.log(Number(null)) // 0console.log(Number(undefined)) // NaNconsole.log(0 == '') // trueconsole.log(0 == '     ') // trueconsole.log('' != ' ') //trueconsole.log(null != 0) // trueconsole.log(undefined != 0) // true复制代码

写在最后

通过上面对JavaScript中的数据类型的隐式转换可以总结出以下结论:

  1. JavaScript中运算符在运算时,最终都将转换成相同类型进行运算(字符串类型、数字类型)
  2. 字符串与加号连接符运算时转换成String类型
  3. 非字符串加号连接符的运算都将转换成Number类型
  4. 特别注意引用类型的隐式转换是先判断valueOf()返回的类型,如果返回是引用类型则调用toString()返回对应的字符串值,最终都是按照1,2,3的规则进行运算。

以上内容虽然有进行验证,但不知道描述上是否存在歧义,有些点表述的不是很清楚,望谅解。

如果有发现任何问题或者有更好的建议,欢迎直接给我留言。

交流

更多精彩内容请关注我的github博客,如果你觉得还不错请给个star,非常感谢。

转载地址:http://pizlo.baihongyu.com/

你可能感兴趣的文章
台湾云端运算产业协会副理事长刘瑞隆:智能制造的发展离不开云计算人才
查看>>
BI的体系架构及相关技术
查看>>
微金时代:小额贷款公司如何做好“小额贷款贷前风险管控”
查看>>
大数据将在今后改变智能手机的应用方式
查看>>
SDN,这一年都经历了什么?
查看>>
财富杂志公布最佳雇主排名 NetApp高居榜首
查看>>
2017云栖大会开源峰会预告
查看>>
CEA开源性能测试工具N2D2 人工智能芯片竞赛开始了
查看>>
红帽公司即将进军OpenStack网络融合工作
查看>>
警告:非智能手机可入侵核电站的物理隔绝设备
查看>>
CTO下午茶:化繁为简,面面俱到
查看>>
游戏安全资讯精选 2017年 第七期:游戏账号窃取日益猖獗,Struts2 REST插件远程执行命令漏洞全面分析,2017世界物联网博览会IoT安全观点...
查看>>
项立刚:FDD牌照发放 难改行业大格局
查看>>
移动广告作弊流量超过30%?你中招了吗
查看>>
CentOS 6.5环境 MongoDB 3.2.8 单实例安装部署
查看>>
基于阿里云MaxCompute实现复杂事件检测
查看>>
一键部署自动感知服务的Docker集群(一)
查看>>
【D3.js 学习总结】17、D3布局-分区图(矩形)
查看>>
《C语言及程序设计》实践项目——数组与指针
查看>>
MySQL中char和varchar有啥区别?优缺点是啥?
查看>>