我觉得写的还蛮清楚的,JS数据类型转换从来都是一把梭
wulang8353 opened this issue · comments
数据类型转换
每日一话:我知道为什么恋人之间喜欢接吻,不然喜欢就会流出来了
前言
先来个问题: [] === ![] 这个表达式的结果是true还是false呢?
这个问题是校招时对于基础知识考察中的一个很常见的问题,当时死记硬背下来为了过关,但其大致原理也是得过且过了。在此写下这篇,虽然校招已过,但也算是了曾经的一个盲区了。
JS是一种动态型语言,变量定义时无需声明类型,可以在运算时自动转换数据类型。
var m = n ? 'hello' : 1
变量 m 的类型由变量 n 来确定, 若 n 为true时,m 是一个字符串;若 n 为false时,m 是一个数值。
虽然变量的数据类型是不确定的,但是各种运算符对数据类型是有要求的。
当运算符发现元素类型不符,就会自动转换数据类型。最为常见的就是减法运算符,若两侧有一方不为数值,则自动转为数值。
JS类型转换强制转换也叫显性转换,指调用函数去转换数据类型。还要一种类型转换叫自动转换,也叫做隐形转换。顾名思义,这种转换是看不见的,是代码在运算时为了统一类型自动转换的。
强制转换
强制转换主要指使用Number、String和Boolean三个函数,将各种类型的值转换成数值、字符串或者布尔值。
JS数据类型分为原始类型与对象类型
Number()
| 原始类型
// <数值>
// 转换后还是原来的值
Number(123) // 123
// <字符串>
// 如果可以被解析为数值,则转换为相应的数值
Number('123') // 123
// 如果不可以全部被解析为数值,返回 NaN
Number('123abc') // NaN
// 空字符串转为0
Number('') // 0
// <布尔值>
// true -> 1,false -> 0
Number(true) // 1
Number(false) // 0
// <undefined>
// 转成 NaN
Number(undefined) // NaN
// <null>
// null转成0,这个是重点
Number(null) // 0
// parseInt函数不似Number整体转换,而逐个解析直到不能解析
parseInt('1 cats') // 1
Number('2 cats') // NaN
| 对象
简单的规则是,Number方法的参数是对象时,将返回NaN,除非是包含单个数值的数组
Number({a: 1}) // NaN - 对象返回NaN
Number([1, 2, 3]) // NaN - 多值数组返回NaN
Number([5]) // 5 单值数组返回该值
之所以会这样,是因为Number背后的转换规则是分三步走的
1、调用对象自身 valueOf
方法。若能返回原始数据类型的值,则直接使用Number函数(规则如上)。若返回对象看2
2、调用对象自身toString
方法。若能返回原始数据类型的值,则直接使用Number函数(规则如上)。若返回对象看3
3、都返回对象了,都报错了,就直接返回NaN
var obj = {name: 'Tim'};
Number(obj) // NaN
// 等同于
if (typeof obj.valueOf() === 'object') {
Number(obj.toString());
// obj为对象,obj.toString() = "[object Object]"
} else {
Number(obj.valueOf());
}
首先调用obj.valueOf方法, 结果返回对象本身。继续调用obj.toString方法,这时返回字符串[object Object]。对这个字符串使用Number函数,得到结果NaN
对象的valueOf方法一般直接返回对象或者本身,而toString方法返回对象的类型字符串(比如[object Object]),返回数组的字符串形式
Number({}) // NaN
Number([1])// [1].toString() -> '1' -> Number('1') = 1
// valueOf和toString方法都可以自定义
Number({
valueOf: function () {
return 2;
},
toString: function () {
return 3;
}
})
// Number({a:1}) 2
// 对象先调用valueOf再调用toString,所以返回2
String()
String函数可以将任意类型的值转化成字符串
| 原始类型
String(123) // "123"
String('abc') // "abc"
String(true) // "true"
String(undefined) // "undefined"
String(null) // "null"
| 对象
如果是对象,返回一个类型字符串;如果是数组,返回该数组的字符串形式
String({a: 1}) // "[object Object]"
String([1, 2, 3]) // "1,2,3"
String([1]) // "1"
转换原理与Number()刚好相反,先调用toString()再调用valueOf():
1、调用对象自身toString
方法。若能返回原始数据类型的值,则直接使用String函数(规则如上)。若返回对象看2
2、调用对象自身 valueOf
方法。若能返回原始数据类型的值,则直接使用String函数(规则如上)。若返回对象看3
3、都返回对象了,都报错了,就直接返回NaN
String({a: 1})
// "[object Object]"
// 等同于
String({a: 1}.toString())
// "[object Object]" 直接返回原始类型,不用再调用valueOf方法
Boolean()
Boolean函数可以将任意类型的值转为布尔值
它的转换规则相对简单:除了以下五个值的转换结果为false,其他的值全部为true。所有对象(包括空对象)的转换结果都是true。
Boolean(undefined) // false
Boolean(null) // false
Boolean(0) // false
Boolean(NaN) // false
Boolean('') // false
自动转换
自动转换的基础就是默认强制转换,遇到以下情况时,JavaScript 会自动转换数据类型
- 1 不同类型的数据进行逻辑运算
加法运算: 当一个值为字符串,另一个值为非字符串,则后者转为字符串
'1' + 2 // '12'
'1' + true // "1true"
'1' + false // "1false"
'1' + {} // "1[object Object]"
'1' + [] // "1"
'1' + function (){} // "1function (){}"
'1' + undefined // "1undefined"
'1' + null // "1null"
非加法运算符:自动转成数值
'5' - '2' // 3
'5' * '2' // 10
true - 1 // 0
false - 1 // -1
'1' - 1 // 0
'5' * [] // 0
false / '5' // 0
'abc' - 1 // NaN
null + 1 // 1
undefined + 1 // NaN
- 2 对非布尔值类型的数据求布尔值
// 写法1
if ('abc') {
console.log('hello')
}
// 写法2
![1] === false
// 写法3
!! expression
// 写法4
expression ? true : false
- 3 对非数值类型的值使用一元运算符(即+和-)变为数值
+ {foo: 'bar'} // NaN
- [1, 2, 3] // NaN
+ true // 1
- false // 0
回到[] === ![]这个问题,答案为 true
首先了解一下运算符的优先级:
解析过程:
1、由上图可以看到 ! 的优先级较 === 高。首先计算 ![],因为 [] 属于对象,所以 Boolean([])返回true,![] 返回 false
2、[] === false, 一方操作数为对象,另一方为布尔值。 对于数组而言,除了日期对象,都是对象到数字的转换。
3、[].valueOf() -> [] -> [].toString() -> '' -> Boolean('') -> false
这里等式左侧之所以不直接理解成 Boolean([]),是因为除了求布尔值的四种写法外,对象的转换都是要调用toString和valueOf方法
3、 false === false // true
若 [] === [] 或者 {} === {} 比较的是内存地址,所以都返回 false
思考一下: {} === !{}
1、! 的优先级较 === 高,首先计算 !{},因为 {} 属于对象,所以 Boolean({}) 返回true,!{}返回 false
2、{} === false, 一方操作数为对象,另一方为布尔值。 对于对象而言,都是对象到字符串的转换。
3、{}.toString() -> "[objext obejct]" === false // false
总结
两个操作数进行运算,无论如何转换最终目标都是转换成原始类型数据进行运算
我们总结一下类型转换的规则:
1、 undefined == null,结果是true
2、 加法运算: 当一个值为字符串,另一个值为非字符串,则后者转为字符串; 非加法的运算转为数值
3、 对4种类型的数据求布尔值,直接使用Boolean方法。且!优先级高
4、 当有一方操作数为对象类型时,若是数组,一般预期转为数值,调用顺序valueOf() -> toString();若是对象,一般预期转为字符串,调用顺序toString() -> valueOf();