本文重点介绍 3 个问题
1.<,>等类似比较运算符的数据类型转化规则 如 1>'2' 结果为什么
2.== 运算符类型转化规则 1=='2' 结果是什么
3.+ 运算符的类型转化规则 let obj={};let test= obj==3;test 是什么.
要了解这些知识, 有些基础知识要掌握
1. 要知道数据类型转化的标准是什么 , 其实就是转化为各个运算符期望的操作数类型
例如乘法运算符 * 要求两边操作数是数值, 如果不是数值就要转化为数值, 如果不能转化为数值 代码就报错
2. 知道期望的数据类型后, 就要知道各个数据类型如何转化为期望的数据类型
3. 如果操作数两边是引用类型, 引用类型如何转换为原始值数据类型.
如 let obj={};let test=obj+1,test 等于多少, 这个时候就看对象类型 {} 转换为什么了.
带着问题我们开始正式介绍
一. 各个数据类型彼此的转化关系
请看下图 出自 <<JS 权威指南>> 第 49 页
通过这个表可以简单知道 undefined 转化为数值是 NaN, 字符串是'undefined', 以后显式转化查表就可以了
二. 各运算符期望的数据类型
下图是我从 <<JS 权威指南>> 上的截图, 通过这图 能够对隐式转化了解很多
A 结合性 L 从左到右, R 从右到左;
N 标识操作数的个数
类型 标识运算符期望的数据类型, 不是期望的时会转化期望的
→ 标识运算结果类型
1. 参考这个表 很容易得出
- let a=''*2;//a 等于 0,''转化为数值 0, 乘号期望两边是数值类型 所以转化为数值
- let b=2 in {2:2}// b 等于 true ,2 转化为字符串'2',in 要求左是字符串, 右是对象, 所以左操作数转为字符串'2'
2.+ ,>, == 有点复杂 需要单独说明. 他们对操作数类型的期望就比较复杂 ,
3. 从图中就可以看出 +,>操作数类型 数值与字符串都可以 , 那问题来了 什么时候转数字什么时候转字符串呢;
4.== 运算符更加复杂 他的操作数类型可以是任何类型, 都是肯定要求两边的操作数类型一样 ; 要满足这条规律 就必须知道
两边操作数步一致时 , 应该谁转成谁.
三. 引用类型转化为原始值的规则
1. 引用类型通过调用 toString 或 valueOf 转原始值. 如果这两个方法都不能转原始值就会报错
列如 let obj={
- toString:function(){
- console.log('字符串被执行')
- return {
- };
- },
- valueOf:function(){
- console.log('数值被执行')
- return {
- };
- }
- }
- let test=obj+1;// 结果是 报错
列如 let obj={
- toString:function(){
- console.log('字符串被执行')
- return '123';
- },
- valueOf:function(){
- console.log('数值被执行')
- return 456;
- }
- }
- let test=obj+1;// 结果是 test 等于 457 ,valueOf 被执行, 为什么结果不是调用 toString 呢 看下面规律
2.. 参与运算符时 优先使用 valueOf, 如果得不到原始值 才调用 toString.
3. 显示转换为字符串时 才优先调用 toString, 列如
- let obj={
- toString:function(){
- return '123';
- },
- valueOf:function(){
- console.log('数值被执行')
- return 456;
- }
- };
- String(obj); // 结果为'123' 显示转换为字符串 结果就调用 toString 结果就是'123'
基础知识介绍完成, 现在开始进入难点
四. 比较运算符(>,< )
有一条基本规律 比较运算符的两边类型只能是数值或字符串, 所以最后的转换就是要么转换为字符串 要么转换为数值. 下面是具体的规律
1. 如果有一个操作数是数值或转换后是数值, 则必定把另外一个操作数值转换为数值 然后进行数值比较
列如
2. 如果有一个操作是字符串, 另外一个操作数只有是字符串或引用类型转换后是字符串 才能进行字符串的比较
例如
obj2>'a122'是 abj2 转换为'abcd'了, 然后进行了字符串的比较
列如 let obj35={
- toString:function(){
- return 'string';
- },
- valueOf:function(){
- return 123;
- }
- };
obj35>='123.00' 结果为 true; 说明 obj35 调用 valueOf 转换为数值 123 了, 后面的字符串'123.00'转换为数值 123 了, 所以结果才为 true. 这里特别看出 , 转换后只有两边都是字符串才能进行字符串的比较, 否则就转换为数值进行比较
3. 如果两个数值都不是数值或字符串, 而是其他的原始值, 则这两个操作数会转换为数值后进行比较
总结: 参与比较运算的 , 两个操作数中只要有一个是数值或是类型转换后能够成为数值, 都会把另外一个操作数转换为数值后进行比较. 两边类型不相同时 优先转换为数值进行比较. 只有两个类型都是字符串或对象类型转换后能够满足两个都是字符串时才按照字符串比较
五.+ 运算符类型转化
1. 已知操作数中有一个是字符串, 另外一个操作数就一定转化为字符串
例如 123+'123' 结果是'123123';
let b={};let c=b+'123'; //c 结果是 "[object Object]123"
2. 已知一个操作数是数值时,
a. 如果另外一个操作数是原始值类型, 就一定转化为数值进行运算.
例如 true+1 结果就是 2,undefined+1 等于 NAN; null+1 等于 1;
b. 如果是引用类型 结果就看引用类型通过调用 valueOf 或 toString 转化为什么类型的原始值, 如果转换为字符串 就符合第 1 条规则, 如果转换为 非字符串类型的原始值就是 2.a 条规则, 如果不能转化为原始值就报错.
例如 let obj={
- toString:function(){
- return 'string';
- },
- valueOf:function(){
- return 123;
- }
- }
- obj+1;// 结果就是 124;obj 调用 valueOf 转化为 123.
列如 let obj2={
- toString:function(){
- return 'string';
- },
- valueOf:function(){
- return {
- };
- }
- }
- obj2+1;// 结果就是'string1';obj 调用 toString 转化为'string'
列如 let obj3={
- toString:function(){
- return 'string';
- },
- valueOf:function(){
- return true;
- }
- }
- obj3+1;// 结果就是 2;obj 调用 valueOf 转化为 true,true 转换为数值 1, 所以结果是 2
3.+ 与 symbol 类型数据运算就报错, 因为 symbol 类型数据无法转化为原始值类型.
4. 两个操作数中既不是数值类型也不是字符串类型时 且引用类型转换后也不是时, 两个操作数都将转换为数值类型进行计算.
列如 true+true=2; // 两边的 true 都转换为 1 所以结果是 2
- false+null=0;//false 转换为数值 0,null 转换为数值 0 所以结果是 0;
- undefined+true=NaN //undefined 转换为数值 NaN true 转换为数值 1,NaN 加任何数都是 NaN 所以结果是 NaN
总结 + 运算符 操作数中没有字符串或也无法转换为字符串时, 两边的操作数就都转换为数值类型进行计算. 如果有一个操作数是字符串或能够转换为字符串 都会按照字符串进行连接
六. == 运算符
有一个总规律 , 类型的规律一定是让两边的操作数类型相同再比较.
6.1 . 如果两边操作数类型相同, 就采用绝对相等的规则比较.
6.2 . 如果两边操作数类型不相同, 优先转换为数值进行比较.
列如 true=='1' 为 true 就是 true 和'1'都转换为数值 1 了 所以他们相等.
6.2.1 这个规律里有一条要排除, 就是有一边的操作数为 null 或 undefined 时, null 只和本身及 undefined 相同, 即 null==undefined 为 true. 如果另外一个操作数是对象类型时, 对象类型不会调用 valueOf 或 toString 转换为原始值进行比较,
列如 let obj4={valueOf:()=>{console.log('我被执行了');return null;}};
obj4==null;// 结果只为 false 不会输出 '我被执行了' 的日志, 说明引用类型不会转换为原始值.
6.2.2 另外还有一个特殊情况 如果有一个操作数是对象类型, 他转换后为 null 或 undefined, 他也符合 5.2.1 的规律 , 且不会再转换.
列如 let obj4={valueOf:()=>{console.log('我被执行了');return null;}};
obj4==0 结果为 false 输出 '我被执行了' , 什么 obj4 转换为 null 了 但是 null 没有再转换为 0 否则就会是相等. 但是有些情况他是可以再转换的
列如 let obj4={valueOf:()=>{console.log('我被执行了');return true;}};
obj4=='1' 输出结果为 true 我被执行了 . 什么 obj4 转换为 true 后, true 再转换为数值 1 然后字符串'1' 也转换为数值 1 了 所以结果相同.
6.2.3 总结 如果有一个操作数是对象类型, 另外一个操作数为不是 null 或 undefined 的原始值时, 对象类型都会转换为原始值, 如果两个原始值经过第一次转换后类型就相同了 就不会再转换了, 如果不相同就会再转换, 直到两边类型相同停止, 两边不相同是 一般都是转换为数值.
6.3 只有两边操作数都是字符串时, 才会按照字符串比较是否相等 (这条其实很多余 但是又感觉需要说明一下)
总结一下 其实写这个蛮难的, 主要是不好归类, 比较的分支太多了, 现在做几个题让大家感受一下作为总结应用
- a.1=='true' // 结果 false, 'true' 转换为数值 NaN 所以不等于 1.
- b. 1=='1' // 为 true 字符串'1' 转换为数值 1 所以相等,
c. '1'==true 为 true 说明两边都转换为数值 1 进行比较 这里总结就是其他类型优先转换为数值
- d. let obj4={
- valueOf:()=>{
- console.log('我被执行了');return null;
- }
- };
- null==obj4// 结果 false 说明对象类型遇到 null 或 undefined 时不在转换为原始值.
- obj4==0 // 结果为 false 输出 我被执行了 说明对象类型转换为原始值了, 但是 null 不再转换为数值 0 所以不相等
- e. let obj4={
- valueOf:()=>{
- console.log('我被执行了');return '1';
- }
- };
obj4==true 结果为 true 说明对象转换为原始值'1' 如何两边都再次转换为数值 1 了
通过 d e 大家应该感受到一点巨大区别了, 有一个操作数为 null 或 undefined 或转换后为这个, 也会应用 6.2.1 的规律.
7. 测试题
看完这些 希望大家都明白 JS 里类型转换. 下面留几个考题给大家小试牛刀, 要答案的请评论
1+'3' 结果是什么
1+3+'3' 结果是什么
let obj={};let test=obj+1;test 结果是什么
undefined+1 结果是什么
true+null 结果是什么
6.true>='1' 结果是什么 答案是 true, 比较运算符优先转换为数值进行比较. true 转换为数值 1 , 字符串'1'转换为数值 1 所以结果为 true;
7. let obj4={valueOf:()=>{console.log('我被执行了');return '1';}}; let test=obj4==true; 问题 test 为 true 还是 false
来源: http://www.jianshu.com/p/e5fae6304493