进入正题之前,首先介绍下对象的valueOf()和toString()方法,因为很多隐式转换的场景会用到valueOf和toString方法。
每个对象都有这两个方法。在控制台输出Object.prototype,其中就有valueOf和toString方法,而Object.prototype是所有对象原型链顶层原型,所有对象都会继承该原型的方法,故任何对象都会有valueOf和toString方法。js的常见内置对象有:Date, Array, Math, Number, Boolean, String, Array, RegExp, Function。
valueOf方法:
(1)Number、Boolean、String这三种构造函数生成的基础值的对象形式,通过valueOf转换后会变成相应的原始值。
var num = new Number('123');
num.valueOf(); // 123
var str = new String('12df');
str.valueOf(); // '12df'
var bool = new Boolean('fd');
bool.valueOf(); // true
(2)Date这种特殊的对象,其原型Date.prototype上内置的valueOf函数将日期转换为日期的毫秒的形式的数值。
var a = new Date(); a.valueOf(); // 1515143895500
(3)除此之外返回的都为this,即对象本身
var a = new Array();
a.valueOf() === a; // true
var b = new Object({});
b.valueOf() === b; // true
toString()方法:
(1)Number、Boolean、String、Array、Date、RegExp、Function这几种构造函数生成的对象,通过toString转换后会变成相
应的字符串的形式,因为这些构造函数上封装了自己的toString方法。
Number.prototype.hasOwnProperty('toString'); // true
Boolean.prototype.hasOwnProperty('toString'); // true
String.prototype.hasOwnProperty('toString'); // true
Array.prototype.hasOwnProperty('toString'); // true
Date.prototype.hasOwnProperty('toString'); // true
RegExp.prototype.hasOwnProperty('toString'); // true
Function.prototype.hasOwnProperty('toString'); // true
var num = new Number('123sd');
num.toString(); // 'NaN'
var str = new String('12df');
str.toString(); // '12df'
var bool = new Boolean('fd');
bool.toString(); // 'true'
var arr = new Array(1,2);
arr.toString(); // '1,2'
var d = new Date();
d.toString(); // "Wed Oct 11 2017 08:00:00 GMT+0800 (中国标准时间)"
var func = function () {}
func.toString(); // "function () {}"
(2)除以上这些对象及其实例化对象之外,其他对象返回的都是该对象的类型,都是继承的Object.prototype.toString方法。
var obj = new Object({});
obj.toString(); // "[object Object]"
Math.toString(); // "[object Math]"
接下来就是一些常见的隐式转换的场景:
1、加法操作符(+)
- 若有一个操作数为NaN,结果为NaN
- 若有一个操作数为字符串,则将另一个数转换为字符串然后拼接
- 若有一个操作数为对象,则调用对象的toString方法再执行上一步
// num: 数值操作数 str: 字符串 obj: 对象
num + 非字符串操作数(NaN/undefined/null/false/true) = num + Number(非字符串操作数)
num + str = 'numstr'; // 1 + 2 + '3' = '33'
str + NaN = 'strNaN'; // '1' + NaN = '1NaN'
str + null = 'strnull'; // '1' + null = '1null'
str + true = 'strture';
obj + num = obj.toString() + num; // Math + 123 = '[object Math]123'
obj + str = obj.toString() + str; // Math + '123' = '[object Math]123'
2、减法操作符(-)
- 若一个操作数为字符串、null、undefined、布尔值,就调用Number()将其转换为数值再运算
- 若一个操作数为对象则调用该对象valueOf(),没有valueOf()则调用toString()
1 - '2' = -1;
1 - '123a' = NaN; // 1 - Numer('123a')
1 - Math = NaN; // 1 - Math.valueOf()
1 - true = 0;
3、乘/除法操作符(*、/)
- 若有一个操作数为NaN,则结果为NaN
- 若其中一个操作数不为数字,则调用Number()将其转换为数值
2 * '5'; // 10
2 * '5a'; // 2 * Number('5a') -------->
2 * NaN ------------> NaN
4、布尔操作符(!、&&、||)
- 当使用 条件判断语句(if...else) 以及 布尔操作符(!、&&、||) 时,会调用Boolean()进行隐式类型转换
- 转换为false的有:0, null, undefined, NaN, '', false,其余都为true([], {}为true)
![]; // !Boolean([]); ------------> false
!2; // false
!null; // !Boolean(null); ------------> true
!undefined; // true
!0; // true
if([]) consolo.log('true'); // if(Boolean([])) ... -----------> true
5、关系操作符(>、<、>=、<=)
- 与NaN比较都会返回false
- 若两个操作数都为字符串,则比较字符串的编码值
- 若有一个操作数为数值,则对另一个操作数用Number()转换
- 若有一个操作数为对象,则调用该对象valueOf(),没有valueOf()则调用toString()
4 > '2'; // true
4 > '2a'; // false
4 > Math; // false
4 > true; // true
'ab' > 'a' // true
6、相等操作符(==、!=)
- 字符串、布尔类型和数值比较,现将前者用Number()转换为数值
- 若一个操作数是对象另一个不是,则调用该对象valueOf(),没有valueOf()则调用toString()
- 若两个操作数都是对象,则比较它们是不是同一个对象(地址是否相同)
- null和undefined是相等的
- null和undefined不会转换成任何值
- 任何数都不等于NaN,包括NaN自己
//特例:
NaN == NaN//false //NaN是唯一不等于自身的
// true
'123' == 123
[123] == 123
true == '1'
1 == true
'1' == true
'123' == new String(123);
null == undefined;
// false
2 == true
'2' == true
null == 0;
new String(123) == new String(123);
7、全等操作符(===、!==)
- 全等操作符不会转换操作数
123 === '123'; // false
null === undefined; // false