简言
最近被 [ ] + { } 和 { } + [ ] 弄得有点晕,当时感觉理解了,过一段时间就会忘记。可能是没get到内部运行机制的原理,还是一知半解的水平。所以,又去重新啃了一下js的基础知识,终于,被我发现诀窍了。从此,js的数据类型转换不再晕头转向了。
正文开始
1. js的数据类型:
js的数据类型包含原始类型和引用类型
原始类型(7种):Number、String、Boolean、undefined、null、Symbol、BigInt
引用类型:Object
2、显式类型转换
- Number()
原始类型转换为Number类型的转换规则:
数值:转换后还是原来的值 字符串:如果可以被解析为数值,则转换为对应的数值,否则,转换为NaN,需要注意的是:空字符串转换为0 undefined:转换为NaN null:转换为0 布尔:true转换为1,false转换为0 Symbol:会报错,Symbol类型的值不能转成为Number,报类型错误 复制代码
引用类型转换为Number类型的转换规则:
如果要转换的是个对象,先调用对象的valueOf方法,如果该方法返回的是原始类型的值,那么依照上述规则,进行类型转换,如果方法返回的还是复合类型,那么调用该对象的toString方法,如果toString方法返回的是原始类型的值,按上述规则进行转换,如果不是,则报错
通过几个案例加深一下印象吧:
Number({a:1})
// valueOf() -> {a:1} -> toString() -> '[object Object]' -> Number('[object Object]')-> NaN
Number([1,2])
// valueOf() -> [1,2] -> toString() -> '1,2' -> Number('1,2')-> NaN
Number([1])
// valueOf() -> [1] -> toString() -> '1' -> Number('1') => 1
Number([])
// valueOf() -> [] -> toString() -> '' -> Number('') => 0
Number(function(){})
// valueOf() -> function(){} -> toString() -> 'function(){}' -> Number('function(){}') => NaN
复制代码
- String()
原始类型转换为String类型的转换规则:
数值:转换为数值字符串
字符串:字符串
undefined:转换为'undefined'
null: 转换为'null'
boolean:转换为'true'或'false'
Symbol:转换为'Symbol()'
引用类型转换为String类型的转换规则:
如果要转换的是个对象,那么先调用对象的toString方法,如果方法返回的结果是个原始类型,则按上述规则转换,如果结果返回的结果还是对象类型,那么会调用对象的valueOf方法,如果方法返回的是原始类型,依旧按照上述规则转换,如果返回的还是对象类型,那么转换报错
通过几个案例加深一下印象吧:
var a1 = { b : 1 };
a1.toString() // '[object Object]'
console.log(String(a1)) // '[object Object]'
var a2 = [1,2,3];
a2.toString() // '1,2,3'
console.log(String(a2)) // '1,2,3'
var a3 = function(){};
a3.toString() // 'function(){}'
console.log(String(a3)) // 'function(){}'
var a4 = [];
a4.toString() // ''
console.log(String(a4)) // ''
复制代码
- Boolean()
转换为Boolean类型就比较简单了,记住这些就行了:
undefined: false
null: false
NaN : false
'': false
0 : false
除了上述列出来的这些,其余的都转换为true
3、隐式类型转换
触发隐式类型转换的行为: 四则运算、判断语句、native调用(console.log和alert方法会内部调用String方法)。
四则运算中除了加法(+
),其他的运算符(-
、*
、/
、%
)都会把非Number类型转换为Number类型
+
:作为算术运算符,会把其他类型通过Number()转换为数值类型;当+两边有一边是字符串时,会被当做是字符串连接符,那么其他类型通过String()转换为字符串类型。
1 + 2 //3
// 两边都是Number类型
1 +'' // '1'
// 有字符串,其他类型转换为字符串
1+ false // 1
// 其他类型先转换为Number类型,再相加
1+ null //1
// 其他类型先转换为Number类型,再相加
1+ undefined //NaN
// 其他类型先转换为Number类型,再相加
1 + function(){} //'1function(){}'
//引用类型先转换为值类型,再根据值类型进行相加
1 + {} //'1[object Object]'
//引用类型先转换为值类型,再根据值类型进行相加
1 + [] //'1'
//同上
1 +[1,2] // '11,2'
//同上
复制代码
==
:
NaN不等于任何值
String和Number进行比较时,String转换为Number
Boolean和其他类型进行比较时,Boolean转换为Number
null和undefined进行比较时,相等
引用类型和值类型比较时,引用类型先转换为值类型(内部调用toPrimitive)
引用类型和引用类型比较时,判断是否指向同一个引用
复制代码
先看几个比较坑的例子:
[] == [] //false
// 引用类型比较,不指向同一个引用为false
[] == 0 // true
// [] 转换为值类型为'' ,''== 0时,Number('') -> 0,所以为true
![] == 0 // true
// ![]为false,Number(false) -> 0 ,所以为true
![] == [] //true
// ![]为false,[]转换为值类型为'',false转换为Number(false) -> 0,''转换为Number('') -> 0,所以为true
{} == !{} //false
//!{} 为false,{}转换为值类型为'[object Object]',false转换为Number(false) -> 0,'[object Object]'转换为NaN,所以为false
{} == {} //false
// 引用类型比较,不指向同一个引用为false
复制代码
4、经典面试题
{} + {} //'[obiect Obiect][obiect Obiect]'
{} + [] // 0
[] + {} //[obiect Obiect]
[] + [] //''
复制代码
对于{} + {}
,在火狐浏览器上打印得出的结果是NaN
5、不得不提的toPrimitive函数
toPrimitive(input,PreferredType):将一个对象转换为值类型
Symbol.toPrimitive 是一个内置的 Symbol 值,它是作为对象的函数值属性存在的,当一个对象转换为对应的原始值时,会调用此函数。
toPrimitive()的执行机制:其实就是将一个引用类型的值转化为原始值,可能是字符串和数值,转换规则如上文中引用类型转换为String类型的转换规则
和引用类型转换为Number类型的转换规则
一致。
input是要转换的对象,当input是一个原始值,直接返回input
PreferredType是期望转换为类型,可以是字符串或数值。
unction ToPrimitive(input,PreferredType) {
var hint = PreferredType || 'default';
// 如果是原始值,直接返回
if( isPrimitiveType(input)){
return input
}
if(typeof input === 'symbol'){
throw new Error('typeError')
}
if(input instanceof Date){
hint = 'string'
}
if (hint == "number" || hint == 'default') {
var res = input.valueOf();
!isPrimitiveType(res) && (res = res.toString());
if(isPrimitiveType(res)){
return res
}else{
throw new Error('typeError')
}
}
if (hint == "string") {
var res = input.toString();
!isPrimitiveType(res) && (res = res.valueOf());
if(isPrimitiveType(res)){
return res
}else{
throw new Error('typeError')
}
}
function isPrimitiveType(input){
var primitiveType = ['string','undefined','number','boolean'];
return (input === null || primitiveType.indexOf(typeof input) !== -1);
}
}
复制代码