「小记」JS 类型转换

739 阅读8分钟

一、显式类型转换

1. 原始值的转换

1.1 转布尔值 boolean

使用 Boolean() 函数:

Boolean() // false ,不传任何参数时,返回 false 
Boolean(false) // false
  
// undefined => boolean
Boolean(undefined) // false

// null => boolean
Boolean(null) // false

// number => boolean
Boolean(+0) // false
Boolean(-0) // false
Boolean(NaN) // false , typeof NaN // number

// string => boolean
Boolean('') // false

1.2 转数字 number

/* 根据规范
 * - Number() 函数不传参数,返回 0 ;
 * - 有参数,调用底层方法 ToNumber(value): 无法被转换为数字的参数,返回 NaN;
 */

// undefined
ToNumber(undefined) // NaN
// null
ToNumber(null) // 0
// boolean
ToNumber(true) // 1
ToNumber(false) // 0

使用 Number() 函数:

Number() // 0
// undefined => number
Number(undefined) // NaN
// null => number
Number(null) // 0

// bollean => number
Number(false) // 0
Number(true) // 1

// string => number
// - 视图转换成一个整数或浮点数;
// - 忽略所有前导的 0;
// - 如果有一个字符不是数字,返回 NaN;
Number('123') // 123
Number('-123') // -123
Number('1.2') // 1.2
Number('000123') // 123
Number('-000123') // -123
Number('0x11') // 17
Number('') // 0
Number(' ') // 0
// 无法转为数字的参数,返回 NaN
Number('123 123') // NaN
Number('foo') // NaN
Number('100a') // NaN

1.3 转字符 string

/* 根据规范
 * - String() 函数不传参数,返回空字符串;
 * - 有参数,调用 ToString(value);
 */

// undefined
ToString(undefined) // undefined
// null
ToString(null) // null
// boolean
ToString(true) // true
ToString(false) // false

使用 String() 函数:

String() // ''
// undefined => string
String(undefined) // 'undefined'
// null => string
String(null) // 'null'
// boolean => string
String(false) // 'false'
String(true) // 'true'
// number => string
String(0) // '0'
String(-0) // '0'
String(NaN) // 'NaN'
String(Infinity) // 'Infinity' , typeof Infinity // 'number'
String(-Infinity) // '-Infinity'
String(1) // '1'

1.4 转对象 object

原始值调用 String()、Number() 、Boolean() 构造函数,转换为它们各自的包装对象:

null 和 undefined 属于例外,当将它们用在期望是一个对象的地方都会造成一个类型错误(TypeError)异常,而不会执行正常的转换;

typeof 1 // 'number'
typeof new Number(1) // 'object'
typeof new String(1) // 'object'
typeof new Boolean(1) // 'object'

2. 对象的转换

2.1 转布尔值 boolean

对象转布尔值很简单,使用 Boolean() 函数,所有对象都转换为 true ,包括数组和对象:

Boolean([]) // true
Boolean(new String(1)) // true
Boolean(new Number(1)) // true
Boolean(new Boolean(1)) // true

2.2 转数字 number 和字符 string

  • 都是通过调用待转换对象的一个方法来完成的;
  • js对象有toString() 、valueOf()两个不同的方法;
  • 所有对象,除了null 、undefined 之外的任何值都具有 toString() 方法,和 String() 方法返回的结果一致;

调用 toString() 方法

  • 数组的 toString() 方法将每个数组元素转换成一个字符串,并在元素之间添加逗号后合并成结果字符串;
  • 函数的 toString() 方法返回源代码字符串;
  • 日期的 toString() 方法返回一个可读的日期和时间字符串;
  • 正则的 toString() 方法返回一个正则表达式直接量的字符串;
// 一般对象
({}).toString() // '[object Object]'
// 数组
[].toString() // ''
[0].toString() // '0'
[1,2,3].toString() // '1,2,3'
// 函数
(function(){var a = 1}).toString() // 'function(){var a = 1}'
// 日期
(new Date(2022,4,1)).toString() // 'Sun May 01 2022 00:00:00 GMT+0800 (中国标准时间)'
// 正则
(/\d+/g).toString() // '/\\d+/g'

调用 valueOf() 方法

  • 默认的 valueOf() 方法返回对象本身;
  • 日期列外,返回它的一个内容:1970.1.1 以来的毫秒数;
new Date(2022,4,1).valueOf() // 1651334400000

调用 ToPrimitive() 方法

都要先调用 ToPrimitive(obj [,type]) 方法:输入一个值,返回一个一定是基本类型的值;

  • 第一个参数是 obj :表示要处理的输入值;
  • 第二个参数是 type ,非必填,表示希望转换成的类型,Number 或 String;
  • 当不传 type 时,如果 input 是日期类型,相当于传入了 String ,否则都相当于传入了 Number;
  • 如果传入的 input 是 undefined、null、boolean、number、string 类型,直接返回该值;

如果是 ToPrimitive(obj,Number),即转换成数字 number,处理步骤如下:

  • 如果 obj 是基本类型,直接返回;
  • 否则,调用 valueOf() 方法,如果返回一个原始值,则 js 将其返回;
  • 否则,调用 toString() 方法,如果返回一个原始值,则 js 将其返回;
  • 否则,js 抛出类型错误异常;

如果是 ToPrimitive(obj,String),即转换成字符 string,处理步骤如下:

  • 如果 obj 是基本类型,直接返回;
  • 否则,调用 toString() 方法,如果返回一个原始值,则 js 将其返回;
  • 否则,调用 valueOf() 方法,如果返回一个原始值,则 js 将其返回;
  • 否则,js 抛出类型错误异常;

🌰

// 一般对象
Number({}) // NaN
Number({a:1}) // NaN

// 数组
// - Number([]):
// - ①对象转原始值:先调用 [].valueOf() ,返回 [];不是原始值,继续调用 [].toString() ,返回空字符串;
// - ②原始值转数字:js 底层调用 ToNumber(value)
Number([]) // 0
Number([0]) // 0
Number([1,2,3]) // NaN

Number(function(){var a = 1}) // NaN
Number(/\d+/g) // NaN
Number(new Date(2022,4,1)) // 1651334400000
Number(new Error('a')) // NaN

3. JSON.stringify

JSON.stringify() 可以将一个 js 值转换为一个 JSON 字符串:调用 toString() 方法;

1. 基本类型

结果与使用 String()(底层调用 toString())基本相同,结果都是字符串,除了 undefined;

JSON.stringify(null) // 'null'
JSON.stringify(undefined) // undefined ,非字符串
JSON.stringify(true) // 'true'
JSON.stringify(18) // '18'
JSON.stringify("18") // '"18"'
JSON.stringify('18') // '"18"'

String(null) // 'null'
String(undefined) // 'undefined'
String(true) // 'true'
String(18) // '18'
String("18") // '18' 
String('18') // '18' 

2. Boolean、Number、String 的构造函数

在序列化过程中会自动转换成对应的原始值:

JSON.stringify([new Boolean(1), new Number(2), new String(3)]) // '[true,2,"3"]'

3. undefined、Symbol、任意函数

在序列化过程中:

  • 会转换成 null (出现在数组对象中);
  • 会被忽略(出现在非数组对象的属性值中时);
// 数组对象中:null
JSON.stringify([undefined, Object, Symbol("")]) // '[null,null,null]'
// 非数组对象中:忽略
JSON.stringify({a: undefined, b: Object, c: Symbol("")}) // '{}'

二、隐式类型转换

js 自动将数据类型进行了转换;

1. 操作符 +

1.1 一元操作符 +

当 + 运算符作为一元操作符的时候,会调用 ToNumber() 处理该值;

+'1' // 1
/* 对象转数字:
 * - ①对象转原始值:先调用 valueOf() ,如果返回不是原始值,继续调用 toString();
 * - ②原始值转数字:js 底层调用 ToNumber(value)
 */
+[] // 0
+['1'] // 1
+['1','2','3'] // NaN
+{} // NaN

1.2 二元操作符 +

当 + 运算符作为二元操作符时(例:a+b) :

  • ① 如果 a、b 存在对象,先调用 ToPrimitive(value) 转为原始值;
  • ② 如果 a、b 存在字符串,返回字符串 a 和字符串 b 的拼接结果;
  • ③ 返回 ToNumber(a) 和 ToNumber(b) 的运算结果;

1. null 和数字

null+1 // 1
// 解析:
// - ① 都是基本类型,调用 ToPrimitive(null|1, Number) ,都是直接返回 null 、1;
// - ③ 不需要拼接字符串,最终结果为:null + 1 = ToNumber(null) + ToNumber(1) = 0 + 1 = 1 ;

2. 数组和数组

[]+[] // ''
// 解析:
[].valueOf() // []
[].toString() // ''
// - ① 都是数组对象,调用 ToPrimitive([], Number) 转换成原始值 :
//   - 先调用 valueOf()  ,均返回对象本身 [];
//   - 非原始值,再调用 toString() ,均返回空字符串;
// - ② 都是字符串,拼接字符串,即最终结果为:[] + [] =''+'' = '';

3. 数组与对象

([]+{}) // '[object Object]'
({}+[]) // '[object Object]'
// 解析:
[].valueOf().toString() // ''
({}.valueOf()) // {}
({}.valueOf()).toString() // '[object Object]'
// - ① 都是对象,调用 ToPrimitive([]|{}, Number) 转换成原始值: 
//   - 先调用 valueOf() ,返回对象本身: [] 、{} ;
//   - 非原始值,再调用 toString() ,均返回字符串:''、'[object Object]';
// - ② 都是字符串,拼接字符串,即最终结果为:[] + {} ='' + '[object Object]' = '[object Object]' ;

更多🌰

// 举例一
1+true // 2
Number(true) // 1
Number(1) // 1

// 举例二
{}+{} // '[object Object][object Object]'
({}.valueOf()).toString() // '[object Object]'

// 举例三
new Date(2022,4,1)+1 // 'Sun May 01 2022 00:00:00 GMT+0800 (中国标准时间)1'
// 解析:
new Date(2022,4,1).toString() // 'Sun May 01 2022 00:00:00 GMT+0800 (中国标准时间)'
// - ① 存在 date 对象,调用 ToPrimitive(value, String) 转换成原始值: 
//   - 先调用 toString() , 返回原始值字符串 ;
// - ② 存在字符串,拼接字符串,即最终结果为:'Sun May 01 2022 00:00:00 GMT+0800 (中国标准时间)' + 1 = 'Sun May 01 2022 00:00:00 GMT+0800 (中国标准时间)1' ;

2. 操作符 ==

'=='用于比较两个值是否相等,当要比较的两个值类型不一样的时候,就会发生类型转换(例如:x==y);

1. null 和 undefined

  • x 是 null,y 是 undefined,返回 true;
  • x 是 undefined,y 是 null,返回 true;
null==undefined // true

2. 字符串和数字

均转换成数字进行比较:

  • x 是数字,y 是字符串,返回 x == ToNumber(y);
  • x 是字符串,y 是数字,返回 ToNumber(x) == y;
1=='1' // true
'1'==1 // true

3. 布尔值和其他类型

  • x 是布尔值,返回 ToNumber(x) == y;
  • y 是布尔值,返回 x == ToNumber(y);
true=='1' // true
true=='2' // false
true=='3' // false
// 解析
Number(true) // 1
Number('1') // 1

4. 对象与非对象

  • x 是数字或非字符串,y 是对象,返回 x == ToPromitive(y);
  • x 是对象,y 是数字或非字符串,返回 ToPromitive(x) == y;
18==['18'] // true
// 解析
['18'].valueOf() // ['18']
['18'].valueOf().toString() // '18'

5. 其他

console.log( false == undefined ) // false

console.log(false == []) // true

console.log([] == ![]) // true

console.log(false == "0") // true
console.log(false == 0) // true
console.log(false == "") // true

console.log("" == 0) // true
console.log("" == []) // true

console.log([] == 0) // true

console.log("" == [null]) // true
console.log(0 == "\n") // true
console.log([] == 0) // true

参考链接:github.com/mqyqingfeng…