JavaScript隐式转换

1,225 阅读4分钟

JavaScript中有两类数据类型:原始类型、对象类型:

原始类型(基础类型): Undefined、Null、String、Number、Boolean、Symbol(es6新推出的,暂不考虑)

对象类型:object

隐式转换中主要涉及到三种转换

1、将值转为原始值,ToPrimitive()。

2、将值转为数字,ToNumber()。

3、将值转为字符串,ToString()。

通过ToPrimitive将值转换为原始值

js引擎内部的抽象操作ToPrimitive有着这样的签名:

ToPrimitive(input, PreferredType?)

input是要转换的值,PreferredType是可选参数,仅可以是Number或String类型。 他只是一个转换标志,转化后的结果并不一定是这个参数值的类型,但是转换结果一定是一个原始值(或者报错)。

对于Date求原始值比较特殊,PreferredType是String,其他Object对象均为Number。

基本类型的转换

加减乘除:

1.字符串加数字,数字就会转成字符串。数字加数字或字符串加字符串不需要转换。

在加法的过程中,首先把等号左右两边进行了求原值ToPrimitive()操作,然后如果有两个或多个原始值只要有一个是String类型,就把两个或多个原始值都进行转化字符串toString()操作,进行字符串拼接;否则把两个或多个原始值都进行转化数字toNumber()操作,进行数字相加。

var a = 1 + 2 + '3';
console.log(a, typeof a); // 结果是  '33' string

var b = 1 + 2 + 3;
console.log(b, typeof b); //结果是   6 number

var c = '1' + 2 + 3;
console.log(c,typeof c);  //结果是  '123' string

2.数字减字符串,字符串转成数字。如果字符串不是纯数字就会转成NaN。字符串减数字也一样。两个字符串相减也先转成数字。(NaN 属于一种特殊的number型)

var a = 10 - '2';
console.log(a, typeof a);  //8  number

var b = 10 - '2a';  
console.log(b, typeof b);  //NaN  number

var c = 10 - 'one';
console.log(c, typeof c);  //NaN  number

3.乘,除,大于,小于跟减的转换也是一样。

var a = '10' * '20';
console.log(a, typeof a);  //200  number

var b = '20' / '10';
console.log(b, typeof b);  //2  number

var c = '10' / 'one';
console.log(c, typeof c);  //NaN  number
有关==的隐式转换

1.undefined等于null

2.字符串和数字比较时,字符串转数字

3.数字和布尔比较时,布尔转数字

4.字符串和布尔比较时,两者转数字

console.log([] == null); //false

console.log(null == 0); //false

console.log(undefined == null);  //true

console.log('0' == 0);  //true,字符串转数字

console.log(0 == false);  //true,布尔转数字

console.log('0' == false);  //true,两者转数字

引用类型的转换

基本类型间的比较相对简单。引用类型和基本类型的比较就相对复杂一些,先要把引用类型转成基本类型,再按上述的方法比较。

PreferredType转换策略
  • 如果PreferredType被标记为Number,则会进行下面的操作流程来转换输入的值。
  1. 如果输入的值已经是一个原始值,则直接返回它
  2. 否则,如果输入的值是一个对象,则调用该对象的valueOf()方法, 如果valueOf()方法的返回值是一个原始值,则返回这个原始值。
  3. 否则,调用这个对象的toString()方法,如果toString()方法返回的是一个原始值,则返回这个原始值。
  4. 否则,抛出TypeError异常。
  • 如果PreferredType被标记为String,则会进行下面的操作流程来转换输入的值。
  1. 如果输入的值已经是一个原始值,则直接返回它
  2. 否则,调用这个对象的toString()方法,如果toString()方法返回的是一个原始值,则返回这个原始值。
  3. 否则,如果输入的值是一个对象,则调用该对象的valueOf()方法, 如果valueOf()方法的返回值是一个原始值,则返回这个原始值。
  4. 否则,抛出TypeError异常。 注意: PreferredType的值会按照这样的规则来自动设置:
  5. 该对象为Date类型,则PreferredType被设置为String
  6. 否则,PreferredType被设置为Number 案例
console.log([] + []);  //'' (空字符串)

进行ToPrimitive,两个都是Array对象,不是Date对象,所以以Number为转换标准,所以先调用valueOf(),结果还是[ ],不是原始值,所以继续调用toString(),结果是“”(空字符串)原始值,将“”返回。第二个[ ]过程是相同的,返回“”。加号两边结果都是String类型,所以进行字符串拼接,结果是“”。

console.log([] + {});  //[object Object](字符串)

进行ToPrimitive,依然是以Number为转换标准。 [ ]的结果是“”。 { }先调用valueOf(),结果是{ },不是原始值,所以继续调用toString(),结果是“[object Object]”,是原始值,将“[object Object]”返回。 加号两边结果都是String类型,所以进行字符串拼接,结果是“[object Object]”。

console.log({} + []);
//在global里是[object Object]
//在控制台是0
console.log({} + {});  //[object Object][object Object](字符串)

在金丝雀版本的chrome浏览器和node中,结果符合预期。 结果是”object Object”。 在普通版本的chrome浏览器中结果是NaN。 这是为什么呢?原因是在node中会将以“{”开始,“}”结束的语句外面包裹一层( ),就变成了({ } + { }),结果就符合预期。而普通版本的chrome依然会解析成{};+{},结果就变成了NaN