前端进阶系列(第2期):深入理解JavaScript数据类型转换
栏目: JavaScript · 发布时间: 5年前
内容简介:上一期中我们主要是了解了JavaScript中存在两大数据类型:本期我们将重点谈谈JavaScript数据类型转换过程出现的各种“奇葩”的问题。在JavaScript中当运算符在运算时,如果两边数据不统一,CPU就无法计算,这时我们编译器会自动将运算符两边的数据做一个数据类型转换,转成一样的数据类型再计算,这种无需程序员手动转换,而由编译器自动转换的方式就称为
上一期中我们主要是了解了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]' 复制代码
隐式转换规则
- 转成string类型:+(字符串连接符)
- 转成number类型:++/--(自增或自减运算符)、+ - * / % (算术运算符)、> < >= <= == != === !== (关系运算符)
- 转成boolean类型:!(逻辑非运算符)
字符串 VS 加号连接符
字符串 + 基本类型 = 字符串 + String(基本类型)
// 字符串 + 数字 console.log('hello' + 123) // 'hello' + '123' // 字符串 + 布尔 console.log('hello' + true) // 'hello' + 'true' // 字符串 + null console.log('hello' + null) // 'hello' + 'null' // 字符串 + undefined console.log('hello' + undefined) // 'hello' + 'undefined' 复制代码
数字 VS 加号连接符
数字 + 基本类型(非字符串) = 数字类型 + Number(基本类型)
// 数字 + 布尔 console.log(1 + true) // 2 // 等同于 console.log(1 + Number(true)) // 1 + 1 // 数字 + undefined console.log(1 + undefined) // NaN // 等同于 console.log(1 + Number(undefined)) // 1 + NaN // 数字 + null console.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 不等于 NaN console.log(NaN == NaN) // false // undefined vs null console.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 复制代码
引用类型的隐式转换
规则:
- 当引用类型的valueOf()调用时返回的值是一个基本类型时,则直接进行运算。
- 当引用类型的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]' 复制代码
案例一:
目的:验证非自定义对象的隐式转换过程
// 申明一个对象obj1 var 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(),来验证对象的隐式转换过程
// 申明一个对象obj2 var 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()) // string console.log(obj2.valueOf()) // 18 console.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) // object console.log(null instanceof Object) // false console.log(NaN == NaN) // false console.log(null == undefined) // true console.log(Number(null)) // 0 console.log(Number(undefined)) // NaN console.log(0 == '') // true console.log(0 == ' ') // true console.log('' != ' ') //true console.log(null != 0) // true console.log(undefined != 0) // true 复制代码
写在最后
通过上面对JavaScript中的数据类型的隐式转换可以总结出以下结论:
- JavaScript中运算符在运算时,最终都将转换成相同类型进行运算(字符串类型、数字类型)
- 字符串与加号连接符运算时转换成String类型
- 非字符串加号连接符的运算都将转换成Number类型
- 特别注意引用类型的隐式转换是先判断valueOf()返回的类型,如果返回是引用类型则调用toString()返回对应的字符串值,最终都是按照1,2,3的规则进行运算。
以上内容虽然有进行验证,但不知道描述上是否存在歧义,有些点表述的不是很清楚,望谅解。
如果有发现任何问题或者有更好的建议,欢迎直接给我留言。
交流
更多精彩内容请关注我的github博客,如果你觉得还不错请给个star,非常感谢。
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- 深入前端之replaced element
- 【2019 前端进阶之路】深入 Vue 响应式原理,活捉一个 MVVM(超详细!)
- 从 IIFE 聊到 Babel,带你深入了解前端模块化发展体系
- 深入解析 Node.js 的 console.log[每日前端夜话0x73]
- 【1】JavaScript 基础深入——数据类型深入理解与总结
- 深入理解 Java 函数式编程,第 5 部分: 深入解析 Monad
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。