总结:
Javascript 是一种弱类型语言,这意味着变量是没有明确类型的,而是由 JavaScript 引擎在编译时隐式完成,类型转换是将一种数据类型转换为另一种数据类型。
常见的类型转换有2种:强类型转换和隐式类型转换;
① 强类型转换也叫显示类型转换,一般由开发人员手动类型转换,通过 JavaScript 内置的 API ,例如 Number() / toString() / Boolean() 等
② 弱类型转换一般由 JavaScript 编译器完成的自动类型转换,例如在使用算数运算符(加减乘除)或者判断宽松相等时。
一、隐式类型转换
凡是通过逻辑运算符( && || ! )、算数运算符(+ - * / )、关系操作符(< <= > >=) 和 == (相等运算符)、if while 条件的操作,如果类型不一样,就会出现隐式类型转换。
1、加号(+)
+号比较特殊,既可以当做算数运算符做加法,又可以当做字符串连接符
+ 的隐式类型转换规则:
① 如果两个是数字,则执行相加;
② 两个都是是字符串,则作为字符串拼接;
③ 一个是字符串,另一个为 undefined、null、布尔型,调用 toString() 进行拼接;④ 一个为数字,另一个 undefined、null、布尔型或数字,会将其转化为数字进行相加操作;
⑤ 一个字符串,一个数字,作为字符串拼接;
⑥ 一方为数字,一方为 BigInt 类型,不能执行相加,会报type Error错误;
① 算数运算符
除了 string 类型以外的原始数据类型进行加法运算时,非数字类型会转换为数字类型。
进行算术运算时,原始数据类型转为数字,使用 Number() 方法。
console.log(1 + 1) // 2
console.log(1 + undefined) // NaN
console.log(1 + null) // 1
console.log(null + undefined) // NaN
console.log(true + false) // 1
console.log(true + 1) // 2
② 字符串连接符
当含有string类型和引用数据类型时,+号会作为字符串连接符使用,非string类型会转换为string类型。
引用数据类型:会调用自身 toString 方法,如果返回不是原始值,会继续调用自身 valueOf 方法;
非引用数据类型:v.isString() 如果是true,它将调用v.toString()。否则,它将值转换为字符串
console.log(1 + '2') // '12'
console.log('2' + '2') // '22'
console.log('1' + true) // '1true'
console.log('1' + false) // '1false'
console.log('1' + undefined) // '1undefined'
console.log('1' + null) // '1null'
console.log('1' + NaN) // '1NaN'
console.log('2' + []) // '2'
console.log(2 + [1, 2]) // '21,2'
console.log(2 + { a: 1 }) // '2[object Object]'
console.log([] + {}) // '[object Object]'
console.log({} + []) // '[object Object]'
console.log(3 + {}) // '3[object Object]'
console.log({} + 3) // '[object Object]3'
console.log(true + []) // 'true'
console.log(true + {}) // 'true[object Object]'
console.log(undefined + {}) // 'undefined[object Object]'
比较特殊字符串和 BigInt 相加,BigInt 转换为字符串
'1' + 1n // 11
2、除加号以外的算数运算符(- * /)
非数字类型会转换为数字类型;
原始数据类型:调用 Number() 方法进行转换;
引用数据类型:调用自身 valueOf 方法进行转换,如果转换后不是原始值,则会调用 toString 方法进行转换,如果转换后不是数字,则会调用 Number() 进行转换,如果转换后不是数字则会返回NaN
console.log(10 - 1) // 9
console.log(10 - '1') // 9
console.log(10 - null) // 10
console.log(10 - undefined) // NaN
console.log(10 - NaN) // NaN
console.log(10 - true) // 9
console.log(10 - false) // 10
console.log(10 - {}) // NaN
console.log(10 - {name: '草莓'}) // NaN
console.log(10 - [1,2]) // NaN
3、逻辑运算符(&& || !!!)
非布尔类型会转换为布尔类型,引用数据类型转换为布尔值后总会是 true
① !
!a,如果a为布尔值,则直接取反;如果a为非布尔值,则会转换为布尔值然后取反
② &&
a && b,如果a为true,则会返回b;如果a为false,则会返回a
③ ||
a || b,如果a为true,则会返回a;如果a为false,则会返回b
④ !!
!!a,会直接将非布尔值转换为布尔类型的值
console.log(![]) // false
console.log(!{}) // false
console.log(!1) // false
console.log(!0) // true
console.log(!undefined) // true
console.log(!null) // true
console.log(!'') // true
console.log(!NaN) // true
console.log(1 && 2) // 2
console.log([] && 2) // 2
console.log(2 && {}) // {}
console.log(2 || {}) // 2
console.log(false || {}) // {}
console.log('' || {}) // {}
console.log(!!{}) // true
console.log(!![]) // true
console.log(!!0) // false
console.log(!!1) // true
console.log(!!'') // false
4、比较运算符(== > < 等)
1) == 隐式转换规则
① 判断类型是否相同,类型相同,不进行类型转换,只比较大小;
② 其中一个值为 null 和 undefined,另一个值也必须为 null 或者 undefined,才会返回true;
③ 如果有一个值为Symbol类型,直接返回false;
④ 如果两者是 string 和 numbel,将字符串转换为 number;
⑤ 一方为 Boolean,把 Boolean 值转换为 number 再判断;
⑥ 一方为 Object,且另一方为 string、number、symbol,就会把 object 转换为原始类型再进行(调用 object 的 valueOf() 或 toString()方法进行转换)
2)具体分析:
null 与 undefined 进行 == 比较时,不会进行转换,总返回 true
Javascript 规范中要比较相等性之前,不能将 null 和 undefined 转换成其他任何值,并且规定 null 和 undefined 是相等的。
null 和 undefined 都代表无效的值。而且全等于状态下,是false,因为它们不属于同一数据类型。
null 在 == 运算符下,只与 null 和 undefined 相等
console.log(undefined == null) // true console.log(undefined == '') // false console.log(null == null) // true console.log('' == null) // false console.log(undefined == undefined) // true
NaN 是唯一一个与自身不相等的值;
console.log(NaN == NaN) // false
console.log('' == NaN) // false
基本数据类型,除了 undefined、null、NaN ,其余的一般会先转换为数字类型,再比较是否相等
Number('')为0
console.log('' == '') // true console.log(1 == '1') // true
如果其中一个是引用数据类型,会先转换为string(先调用valueOf,后调用toString),再转换为number
[ ].valueof()是 [ ] ,而且 [ ].tostring()是 ' ' ,Number(' ')是0
console.log(0 == []) // true console.log('' == []) // true console.log('1,2' == [1,2]) // true
如果==左右都是引用数据类型,会进行地址比较;
因为[ ]属于引用类型,在两个[ ]分别指向不同的堆内存,地址不同;
引用类型数据判断是否相等是判断引用地址是否一致
console.log([] == []) // false console.log([1, 2] == [1, 2]) // false console.log({} == []) // false console.log({} == {}) // false
var a = {
value: 0,
valueOf: function() {
this.value++;
return this.value;
}
};
// 注意这里a又可以等于1、2、3
console.log(a == 1 && a == 2 && a ==3); // true
console.log(a)
console.log(a == 3) // false
console.log(a == 4) // false
console.log(a)
// 看一下 object 隐式转换
// 但是执行过3遍之后,再重新执行 a==3 或之前的数字就是 false,因为 value 已经加上去了(value值每次== 都在向上加)
5、Object 的转换规则
① 如果有 Symbol.toPrimitive 方法,优先调用再返回;
② 调用 valueOf(),如果转换为基础类型,则返回;
③ 调用 toString(),如果转换为基础类型,则返回;
④ 如果都没有返回基础类型,会报错;
Symbol.toPrimitive:如果对象为 Date 对象,转换为基本类型先调动 toString(),后调用 valueOf(),其他都为先调用 valueof(),后调用 toString()
Symbol.toPrimitive - JavaScript | MDN
Symbol.toPrimitive
是内置的 symbol 属性,其指定了一种接受首选类型并返回对象原始值的表示的方法。它被所有的 强类型转换制 算法优先调用。
let obj = {
[Symbol.toPrimitive]() {
return 200
},
valueOf() {
return 300
},
tostring() {
return 'Hello'
}
}
console.log(obj + 200) // 400
var obj = {
value: 1,
valueOf() {
return 2;
},
toString() {
return '3'
},
[Symbol.toPrimitive]() {
return 4
}
}
console.log(obj + 1); // 输出5
// 因为有Symbol.toPrimitive, 就优先执行这个;
// 如果Symbol.toPrimitive这段代码删掉, 则执行valueOf()打印结果为3;
// 如果valueOf也去掉, 则调用toString返回'31'(字符串拼接)
// 两个特殊的情况:
10 + {}
// "10[object object]"
// 注意: {}会默认调用 valueOf是{},不是基础类型继续转换,调用toString,返回结果"[object object]”,于是和10进行”+”运算,按照字符串拼接规则
[1,2,undefined,4,5] + 10
// "1,2,,4,510”
// 注意[1,2,undefined,4,5] 会默认先调用 valueOf 结果还是这个数组, 不是基础数据类型继续转换,也还是调用toString, 返回“1,2,,4,5”, 然后再和10进行运算, 还是按照字符串拼接规则
二、强类型转换
强制类型转换的方式包含了:Number()、String()、Boolean()、parselnt()、parseFloat()、toString() 等方法
1、Number 类型的转换
Number(-0X11); // -17
Number('0X11'); // //17
2、toString 类型的转换
① 基本数据类型
② 引用数据类型
③ 总结
3、Boolean 类型的转换
除了 false、0(+0、-0)、NaN、null、undefined、' ',这六种值为 false,其他都为 true
4、Parselnt() 方法的转换规则
console.log(parseInt('')) // NaN
// 如果字符串的第一个字符不能转化为数字(后面跟着数字的正负号除外),返回NaN
console.log(parseInt(true)); // NaN
console.log(parseInt(false)); // NaN
console.log(parseInt('0111')); // 111
console.log(parseInt(null)); // NaN
console.log(parseInt('1lalal')); // 1
console.log(parseInt(-0X11)) // -17
console.log(parseInt('0X11")) // 17