# js数据类型转换

# 转换的条件

js在以下情况下,会进行数据类型的转换:

  1. if条件运算符;
  2. !(取反操作),转布尔类型;
  3. 加减乘除(四则运算符号),其中**+**还可以拼接字符串;
  4. '==' 或 '===' 比较,比较规则参考'==判断规则'


转换规则参考下表(来自红宝书):

对象类型的数据如何转字符串、数字,我们会在下面单独说明。

# 如何转换

# 1、取反运算符

转boolean值,规则参考上面的JavaScript类型转换参考表

# 2、条件运算符(if语句)

if语句,转boolean值,规则参考上面的JavaScript类型转换参考表

false、null、undefined、''(空字符串)、0(±0),NaN,都会转换为false,,其他为true。

# 3、四则运算符(+-*/)

注意:
1、**+**不仅是四则运算,还可以做字符串拼接
2、-, * ,/都是进行正常的四则运算,四则运算会将非Number类型的转换为Number类型的值。
3、加法+的特殊性,参考下方加号运算符章节,可以先看下这篇文章的介绍:JavaScript 加号运算符详解 (opens new window)

# 加号运算符

作为一元运算符:
+作为一元运算符时,执行ToNumber() (opens new window)操作,规则如下图:

参数类型 返回值
Undefiend NaN
Null 0
Boolean 如果是true,返回1。否则返回0
Number 返回值本身
String
1. 如果是纯数字字符串,返回转换后的数字
1. 如果是非纯数字,返回NaN
Symbol 报错TypeError,无法转换
Object
转换步骤如下:
1. 执行ToPrimitive(input, hint),得到rs;
1. 返回ToNumber(rs)的结果
// " + " 作为一元运算符

// Date 
var date = new Date();
console.log(+date); // 1619752121561
-> toPrimitive(date, number) 
-> date.valueOf()得到一个时间戳: 1619752121561,它是一个非Object类型,然后执行toNumber操作
-> 1619752121561

// Array
var arr = [];
console.log(+arr); // 0
-> toPrimitive(arr, number) 
-> arr.valueOf(),返回数组本身[],依然是一个对象
-> arr.toString(),返回空字符串,然后执行toNumber操作
-> 0

// 根据数组的ToPrimitive规则,数组元素为null或undefined时, 该元素被当做空字符串处理,所以[null]、[undefined]都会被转换为0。
console.log(+['123abc']); // NaN
console.log(+[null]); // 0
console.log(+[undefined]); // 0

// Object
var obj = {}, obj2 = {a: 1};
console.log(+obj, +obj2);// NaN
-> toPrimitive(obj, 'number')
-> arr.valueOf(),返回对象本身,依然是一个对象
-> arr.toString(),返回字符串"[object Object]", 然后执行toNumber操作
-> 非数字类的字符串,转数字,结果为NaN
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

作为二元运算符
+作为二元运算符时,转换规则如下:
1) 值进行GetValue (opens new window)()操作,获取对象的值。
2) 值进行ToPrimitive (opens new window)()操作,转为原始类型
3) 若一方为String类型,2个值都进行ToString (opens new window)()转换,最后进行字符串连接操作。
4) 若都不是String类型,2个值都进行ToNumber (opens new window)()转换,最后进行算数加法运算。

注意: +为一元运算符时: 执行ToPrimitive(input, PreferredType)时,PreferredType为"number"。 +为二元运算符时: 执行ToPrimitive(input, PreferredType)时,PreferredType为默认为"default"。 但是:Date类型内部重写了@@toPrimitive() (opens new window)方法,将 "default" 设置为 "string" ,而其他内置的对象都将 "default" 设置为 "number" 。

案例分析:

var obj = {
    valueOf(){
        return { a: 1 };
    },
    toString(){
        return '100';
    }
};
var res = 1 + obj;
// 双方都不是string,转number。
// obj转number -> 调用obj.valueOf(), 返回"{a:1}"  -> 调用obj.toString(), 返回'100'
console.log(res, typeof res);  // 1100, string

var d = new Date();
// 一方是string,都转string
// d转string时,由于Date重写了'@@primitive', 所以先调用toString(), 返回一个时间字符串 -> "1" + "字符串"
console.log("1" + d); // 1Fri Apr 30 2021 15:10:26 GMT+0800 (GMT+08:00)

// 双方都不是string,转number
// d转number时,由于Date重写了'@@primitive', 所以先调用toString(), 返回一个时间字符串 -> 1 + "字符串"
console.log(1 + d); // 1Fri Apr 30 2021 15:10:26 GMT+0800 (GMT+08:00)

var a = {}; b = [];
// 全部转基本类型:a->'[object Object]',b->'' --> '[object Object]'
console.log(a + b);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

# 关于ToPrimitive的说明

主要用于将一个对象转为一个原始值,js内部自动调用的。
**语法:**ToPrimitive (input[,PreferredType] )
参数:

  • ① 参 input:传入的值。
  • ② 参数 PreferredType:可选,需要被转换的类型。
    • '+'加号作为一元运算符时,此值为"number"
    • '+'作为二元运算符时,未传递此值,以默认的"default"代替。


1) 若input类型为原始值(如:Undefined、Null、Boolean、Number、String、Symbol),直接返回input的本身。
2) 若input类型为object(如:Array、Object、Date),将根据第②个参数PreferredType的值进行以下操作:
image.png

对于+运算符: 1、作为一元运算符,PreferredType为'number'。 2、作为二元运算符: 1. Date重写了@@toPrimitive() (opens new window)方法,PreferredType值为'string'。 2. 其他对象PreferredType值为'number'。

# 双等号与三等号

js中,判断一个表达式两边是否相等时,我们可以使用双等号与三等号,可以看下mdn文档 (opens new window)中关于此部分的介绍。

# 双等号

判断规则:

  1. 如果两边类型相同,进入===判断;
  2. 如果类型不同,则发生隐式转换
    1. NaN和其他任何类型比较永远返回false(包括与自己比较)。
    2. null == undefined比较结果是true,除此之外,null、undefined和其他任何结果的比较值都为false。
    3. Boolean和其他任何类型比较,Boolean首先被转换为Number类型。
    4. String和Number比较,先将String转换为Number类型。
    5. 原始类型和引用类型

当原始类型和引用类型做比较时,对象类型会依照ToPrimitive (opens new window)规则转换为原始类型后再比较。

// 第一题
var obj = {
  valueOf() {
    return 3;
  }
};
// 引用类型与原始类型比较
// -> obj转原始类型 -> obj.valueOf()返回3,
console.log(obj == 3); // 3== 3, true
console.log(obj == '3'); // 3 == '3'

1
2
3
4
5
6
7
8
9
10
11

我们看几个常见的面试考题:
1、[] == ![]的结果是什么?

// 第二题
[] == ![];
// 1. !优先级高于==, 相当于[] == false;
// 2. 对象与普通值比较,对象转普通值。
//			2.1 先调用[].valueOf(),返回[],依然是对象;
//			2.3 再调用[].toString(),返回空字符串'';
// 3. 比较 '' == false,出现Boolean值,此时Boolean值转Number,相当于 '' == 0;
// 4. 字符串与数字比,string转number,空字符串转number为0,此时相当于 0 == 0
// 5. 返回true
1
2
3
4
5
6
7
8
9

2、[undefined] == false的结果是什么?

- 第一步,对象转基本类型,[undefined]通过 valueOf ->toString 变成 '',
      题目变成  '' == false
- 第二步: 布尔值转数字,false转为数字,题目变成  '' == 0
- 第三步,string转number,''转为数字0 ,题目变成  0 == 0
- 答案是 true
1
2
3
4
5


3、再来看一道题目:如何让a==1 && a==2 && a==3成立?
我们可以利用js的隐式转换原理,重写对象的valueOf或者toString方法来实现。

// 可以这么做
var a = {
  value: 0,
  valueOf: function () {
    return this.value += 1;
  }
};

// 或者这么做
var a = {
   value:[3,2,1],
   valueOf: function () {
     return this.value.pop(); 
   }
};
console.log(a==1 && a==2 & a==3); // true
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# 三等号

判断规则:

  1. 如果两边类型不同,则不相等;
  2. 如果类型相同:
    1. 如果是普通值(非引用类型),字符串、数字、布尔,值一样时相等。如果是对象,比较的是引用地址,地址一样才相等;
    2. 如果都是null或undefined,才相等,否则不等;
    3. 判断一个值是否是NaN,只能用isNaN方法;
    4. 如果其中一个是NaN,则不相等;NaN不等于任何值,包括NaN自身
NaN === NaN  // false
// isNaN(NaN)  // true

undefined === undefined  // true
null === null	 // true
undefined === null // false
1
2
3
4
5
6