本篇文章根据《你不知道的javascript 中卷》及ECMAScript标准总结而来,如有错误还请指正。
一、Toprimitive运算符
1、介绍
ToPrimitive 运算符接受一个值,和一个可选的 期望类型 作参数。ToPrimitive 运算符把其值参数转换为非对象类型。如果对象有能力被转换为不止一种原语类型,可以使用可选的 期望类型 来暗示那个类型
以上是ECMAScript规范中文版对于Toprimitive运算的介绍。通俗的说,js中的对象在进行类型转换的时候,会进行toptimitive操作转换成简单类型的值(除Symbol值)。所有看起来觉得奇怪的结果都是toprimitive运算带来的。
2、toprimitive运算规则
ToPrimitive 运算符接根据下表完成转换。
输入类型 | 结果 |
---|---|
Undefined | 结果等于输入的参数(不转换)。 |
Null | 结果等于输入的参数(不转换)。 |
Boolean | 结果等于输入的参数(不转换)。 |
Number | 结果等于输入的参数(不转换)。 |
String | 结果等于输入的参数(不转换)。 |
Object | 返回该对象的默认值。(调用该对象的内部方法[[DefaultValue]]一樣)。 |
所以,对于对象来说,toprimitive操作就是调用对象内部的[[DefaultValue]]方法。同时,toprimitive还有一个参数,会传递给[[DefaultValue]]方法。
3、[[DefaultValue]]方法
[[DefaultValue]]方法是对象内部的方法,接收两个参数,一个是要转换的对象,一个是类型(书中被称作上下文)。类型参数的可选值有两个:number和string。
如果参数是string,会先执行toString方法。如果返回对象,那么就会执行valueOf方法。如果valueOf方法仍返回对象,会报TypeError异常。如果在执行valueOf方法或者toString方法的过程中,返回非Symbol类型的简单值,会将该简单值返回,并调用String()方法,将调用String()的结果作为最终的返回值。如果返回Symbol值,那么也会报错。(记住,显示调用String()方法可以将Symbol值转换为字符串,但是这里是隐式调用,会报 Uncaught TypeError: Cannot convert a Symbol value to a string 异常)。
类似的,如果参数是number,那么会先执行对象的valueOf方法。如果返回对象,那么就会执行toString方法。如果toString方法仍返回对象,会报TypeError异常。如果在执行valueOf方法或者toString方法的过程中,返回非Symbol类型的简单值,会将该简单值返回,并调用Number()方法,将调用Number()的结果作为最终的返回值。如果返回Symbol值,那么也会报错。(这里稍微有点不同。显式调用Number()将Symbol值转换为数字也会报 Uncaught TypeError: Cannot convert a Symbol value to a number异常)。
除去自定义了valueOf方法的对象,对象的valueOf方法都返回自身。(Date对象返回自1970.1.1以来的毫秒数)。toString()方法类似(数组结果是成员拼接的、以,分割的字符串,Date对象结果是国际标准时间,其他对象结果是[object Object])。
用一张流程图表示:
图上的返回结果,最终还会调用String()或者Number()得到最终的结果。
二、显式类型转换
1、toString(转换成字符串)
null转换为"null",undefined转换为"undefined",true转换为"true",false转换为"false",常规数字转换为对应的字符串(无限大会得到"infinity",过大或过小会得到科学计数法的字符串,详细规则请参考ECMAScript中文版 9.8.1),Symbol值转换成对应的值。
重点需要说的是对象,会进行抽象化操作,即进行toprimitive运算,得到最终的字符串或者抛出异常。
调用String()方法将对象转换成字符串时进行了toprimitive操作,类型string。具体流程参考上面的流程图及文字说明。
简单的例子:
let obj = {
valueOf() {
return new Boolean(111);
},
toString() {
return "aaa";
}
};
let obj1 = {
valueOf() {
return 2;
},
toString() {
return [];
}
};
let obj2 = {
valueOf() {
return new Boolean(111);
},
toString() {
return [];
}
};
console.log(String(obj));
console.log(String(obj1));
console.log(String(obj2));
2、toNumber(转换成数字)
调用Number()方法,null结果是0,undefined结果是NaN,true结果是1,false结果是0。
字符串的话,如果是数字+空格组成的字符串且空格只出现在字符串开头或结尾,那么结果是该字符串对应的数字,否则结果是NaN。Number()的参数是Symbol值时会抛出异常。(与String()不同)
Number()参数为对象时,进行toprimitive操作,类型是number,流程参考上面的流程图和文字说明。
几个简单的例子:
let obj = {
valueOf() {
return [];
},
toString() {
return 2;
}
};
let obj1 = {
valueOf() {
return 2;
},
toString() {
return [];
}
};
let obj2 = {
valueOf() {
return new Boolean(111);
},
toString() {
return [];
}
};
console.log(Number(obj));
console.log(Number(obj1));
console.log(Number(obj2));
、
3、toBoolean(转换成布尔值)
显式转换成布尔值时,只有七个值会转换成成false,其他均转换为true,所以只需要记这六个值就可以了。方式包括!取反、Boolean()方法
这六个值分别为:0(包括+0和-0)、false本身、NaN、null、undefined、“”(空字符串)和NaN。
三、隐式类型转换
通常,隐式类型转换指不调用如Number()、String()等方法而发生的类型转换,包括操作符、判断等等。
1、string与number的转换
隐式转换成字符串通常发生在+操作符的时候。但是,并不是+操作符就一定会发生隐式类型转换成字符串。根据ECMAScript规范,如果+操作符两边的值至少有一个是字符串或者能够通过toprimitive转换成字符串时,+操作符执行的是字符串的拼接,即+两边的值都会转换成字符串,其中对象执行toprimitive操作,类型是number(Date对象除外。Date对象的toprimitive操作的默认类型是string)。否则+执行的是数字的相加操作。
下面是几个简单的例子:
let obj = {
valueOf() {
return "1";
},
toString() {
return 2;
}
};
let obj1 = {
valueOf() {
return 2;
},
toString() {
return [];
}
};
let obj2 = {
valueOf() {
return new Boolean(111);
},
toString() {
return [];
}
};
console.log(1 + obj);//验证普通对象toprimitive操作的类型是number
console.log(1 + obj1);//验证+操作符的规则
console.log(111 + new Date());//验证Date对象的toprimitive操作的类型是string
除此之外,一些数学操作符会将其他类型转换成number类型。例如,单独的+操作符,其他-、*、/,位运算符等等。与Number()的过程一样。true转换成1,false转换成0。注意位运算符的一些特殊表现。
let obj = {
valueOf() {
return 1;
},
toString() {
return 0;
}
};
let obj1 = {
valueOf() {
return 2;
},
toString() {
return [];
}
};
let obj2 = {
valueOf() {
return new Boolean(111);
},
toString() {
return [];
}
};
console.log(-obj);
console.log(~~obj);
2、转换成boolean
隐式类型转换成boolean的情况主要包括:
(1)if语句的条件判断表达式;
(2)for循环中的条件判断表达式(第二个);
(3)while和do while的条件判断表达式;
(4)三目运算符(?:)的条件判断表达式;
(5)逻辑运算符&&、||左边的表达式
隐式类型转换成boolean的规则与显示转换一致,即只有那六个值会被转换成false。
四、结语
以上就是关于js中类型转换的介绍,类型转换也带来另一个问题,即==操作符中也会发生类型转换。限于篇幅,下一篇文章再介绍==操作符的转换规则。地址在这 戳我
参考资料:ECMAScript 中文版 《你不知道的javascript 中卷》