JS强制转换

82 阅读4分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第6天,点击查看活动详情

强制转换

1. 隐含强制转换/明确强制转换

var a = 42;
var b = a + "";			// 隐含强制转换
var c = String( a );	// 明确强制转换

2. 抽象操作

2.0 ToPrimitive (转为原始值)

规范:

ToPrimitive:tc39.es/ecma262/#se…

参考:

zh.javascript.info/object-topr…

juejin.cn/post/704009…

这里有三种类型(hint):

  • "string"(对于 alert 和其他需要字符串的操作)
  • "number"(对于数学运算)
  • "default"(少数运算符,以和 "number" 相同的方式实现转换,除非指定toPrimitive方法)

defalut: 如+和== number和string都适用

转换过程:

  1. 非Object类型(Number、Symbol等) 不转换
  2. 调用 obj[Symbol.toPrimitive](hint) 如果这个方法存在 (覆盖的话 必须返回基本类型)
  3. 否则,如果 hint 是 "string"
    • 尝试 obj.toString()obj.valueOf(),拿到基本类型值
  4. 否则,如果 hint 是 "number" 或者 "default"
    • 尝试 obj.valueOf()obj.toString(),拿到基本类型值
  5. 否则,如果 hint 是"default"
    • 如果是Date 对象,按照hint=string处理
    • 如果是其他对象,按照hint=number处理
  6. 拿不到基本类型值
    • Throw a TypeError exception

2.0.1 string例子

var a = { valueOf () { return 1 } }
String(a) // '[object Object]'
var a = { toString () { return 1 } }
String(a) // '1'
var a = {
  valueOf () { return 1 },
  toString () { return 2 }
}
String(a) // '2'

2.0.2 number/defualt例子

var a = {
  valueOf () { return 1 },
  toString () { return 2 }
}
Number(a) // 1   number
a + '' // '1'  default

{}放前面写,被当成空代码块了,返回的是+''的值

{} + '' // 0
'' + {} // [object Object]

2.1 ToString

2.1.1 基本类型

String(null) 			// 'null'
String(undefined) // 'undefined'
String(true) // 'true'
String(1) // '1'
String(1.07 * 1000 * 1000 * 1000 * 1000 * 1000 * 1000 * 1000)	// "1.07e21"

2.1.2 对象

一个对象被强制转换为一个 string 要通过 ToPrimitive 抽象操作。参见ToPrimitive

262.ecma-international.org/5.1/#sec-8.…

2.2 ToNumber

任何非 number 值,以一种要求它是 number 的方式被使用,比如数学操作,就会发生ToNumber 抽象操作。

2.2.1 基本类型

// 常规
Number(undefined) // NaN
Number(false) // 0
Number('') // 0
Number('0xa') // 10  十六进制
Number('0o123') // 83  八进制

// 特别
Number('0123') // 123   0开头的八进制会被当做十进制转换
Number(Symbol(1)) // 报错
Number(10n) // 10 ?报错?

2.2.2 对象

一个对象被强制转换为一个 number 要通过 ToPrimitive 抽象操作 , 参见ToPrimitive

262.ecma-international.org/5.1/#sec-8.…

Number( [] );			// 0  过程,valueOf返回[]不是基本类型,调用toString返回'' 空字符串最后转成0
Number( [ "abc" ] );	// NaN
Number( {} ); // NaN  过程,valueOf返回{}不是基本类型,toString返回'[object Object]',转成NaN
// 特别
Number( null ) 		// 0

2.3 ToBoolean

2.3.1 Falsy 值

被强制转换为 boolean,将成为 false 的值

任何没有明确地存在于 falsy 列表中的东西,都是 truthy

所有Falsy值:

  • undefined
  • null
  • false
  • +0, -0, and NaN
  • ""

2.3.2 Falsy 对象

这几个是普通对象

var a = new Boolean( false );
var b = new Number( 0 );
var c = new String( "" );

var d = Boolean( a && b && c );
d; // true

Falsy对象

// 历史原因,document.all用来判断是否为老版本ie, if(document.all) {老ie},也就是非老ie里是falsy
document.all // HTMLAllCollection(1208)
docuemnt.all && console.log('1') // HTMLAllCollection(1208)
new Boolean(docuemnt.all) // Boolean {false}

2.4 JSON.stringify (不是强制转换)

stringnumberboolean、和 null 值在 JSON 字符串化时,与它们通过 ToString 抽象操作的规则强制转换为 string 值的方式基本上是相同的。

覆盖toJSON

var a = {
	val: [1,2,3],
	toJSON: function(){
		return this.val.slice( 1 );
	}
};
JSON.stringify(a) // '[2,3]'

3. 明确强制转换

var a = 42;
var b = String( a );

var c = "3.14";
var d = Number( c );

var a = 42;
var b = a.toString();

var c = "3.14";
var d = +c;

var c = "3.14";
var d = 5+ +c;
d; // 8.14

1 + - + + + - + 1;	// 2 

Date

var d = new Date( "Mon, 18 Aug 2014 08:53:06 CDT" );
+d; // 1408369986000

var d1 = new Date('2020-01-01')
var d2 = new Date('2020-01-02')
d2 - d1 // 86400000 == 1000*60*60*24

4. 抽象等价性

规范:tc39.es/ecma262/#se…

x == y

  1. 类型相同
NaN == NaN // false
+0 == -0 // true
object1 == object2 // false , object 不发生转换,比较引用
  1. 类型不同
null == undefined  // true
1 == '1' // true 一个数字(Number、BigInt)和一个字符串比较,字符串转成数字
false == '1' // false 一个是boolean一个是其他基础类型,boolean转成数字比较
null == document.all
undefined == document.all
1 == object // 一个是object 一个是String, Number, BigInt, or Symbol, 对象转成原始值 ToPrimitive(y) (没传hint, 为default,和number一样转)

奇葩例子

Number.prototype.valueOf = function() {
	return 3;
};
new Number( 2 ) == 3;	// true
var i = 2;
Number.prototype.valueOf = function() {
	return i++;
};
var a = new Number( 42 );
a == 2 && a == 3 // true
[] == ![];		// true
0 == "\n";		// true   各种空格的组合都会转成0

5. 抽象关系比较

规范:tc39.es/ecma262/#se…

x < y

步骤:

  • 如果是x > y 转成 y < x
  • x、y转成原始值,ToPrimitive(x, number),ToPrimitive(y, number)
  • 如果x、y都是String
    • 两个字符串逐位比较unicode码值(charCodeAt(i))
    • 码值不同,返回码值的比较结果
    • 码值相同,继续循环比较
    • 循环结束,length小的 就小
  • 如果x、y一个是String一个是BigInt
    • String转成BigInt,转不成功是undefined,返回 undefined
    • 转成功,比较bigInt
  • 否则,都转成数字ToNumeric(Number,或者 BigInt)
    • Symbol会抛出错误
    • 其中一个是NaN,返回undefined
    • 否则按值比较

这是按照最新规范2023整理的,实测返回undefined的情况都返回false