Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

隐式转换 与 ”==“ 转换规则那点事儿 #6

Open
wangsiyuan0215 opened this issue Mar 14, 2019 · 4 comments
Open

隐式转换 与 ”==“ 转换规则那点事儿 #6

wangsiyuan0215 opened this issue Mar 14, 2019 · 4 comments

Comments

@wangsiyuan0215
Copy link
Owner

wangsiyuan0215 commented Mar 14, 2019

// 隐式转换 与 ”==“ 转换规则那点事儿

// 基础类型:undefined | null | string | Number | Boolean
// 引用类型:array | object | function

// 约定: ”=>“ 符号表示结果,”->“ 符号表示转换

/* 基础类型之间 == 比较 */
console.log(`
#----------- 基础类型之间 == 比较 -----------#
#----------- 基础类型之间 == 比较 -----------#
#----------- 基础类型之间 == 比较 -----------#

#----------- boolean == number -----------#
@: Boolean 与 Number 进行 ”==“ 比较时,会将 Boolean 转换为 Number;
@: Boolean -> Number: false -> 0, true -> 1
@: Number -> Boolean: 除 0 以外,均为 true,0 -> false

1 == true 		=> ${1== true}
0 == false 		=> ${0 == false}
10 == true 		=> ${10 == true}
-2 == false 	=> ${-2 == false}

!!(10) 			=> ${!!(10)}
!!(-1) 			=> ${!!(-1)}
!!(0) 			=> ${!!(0)}
Number(true)	=> ${Number(true)}
Number(false)	=> ${Number(false)}

#----------- number == string -----------#
@: Number 与 String 进行 ”==“ 比较时,String -> Number;

10 == '10'		=> ${10 == '10'}
10 == 'a'		=> ${10 == 'a'}
-10 == 'a'		=> ${-10 == 'a'}
1 == ''			=> ${1 == ''}
0 == ''			=> ${0 == ''}
-1 == ''		=> ${-1 == ''}

Number('')		=> ${Number('')}
Number('1')		=> ${Number('1')}
Number('0')		=> ${Number('0')}
Number('a')		=> ${Number('a')}

'a' * 1 		=> ${'a' * 1}
'a' / 1 		=> ${'a' / 1}
'a' | 0 		=> ${'a' | 0}
'a' & 0 		=> ${'a' & 0}
'10' * 1 		=> ${'10' * 1}
'10' / 1 		=> ${'10' / 1}
'10' | 0 		=> ${'10' | 0}
'10' & 0 		=> ${'10' & 0}

#----------- boolean == string -----------#
@: String 与 Boolean 进行 ”==“ 比较时,会将 Boolean -> Number;

true == 'a' 	=> ${'a' == true}
false == 'a'  	=> ${'a' == false}
true == '1' 	=> ${'1' == true}
false == '1'  	=> ${'1' == false}
true == '0' 	=> ${'0' == true}
false = '0'  	=> ${'0' == false}
true == ''		=> ${'' == true}
false == ’‘ 	=> ${'' == false}

#----------- null 与 undefined -----------#
@: null 与 undefined 在进行 ”==“ 比较时,不会转换为任何类型
@: 特性 numm == undefined => true

🎉 null == undefined  => ${null == undefined}
null == 1  		=> ${null == 1}
null == 0  		=> ${null == 0}
null == -1  	=> ${null == -1}
null == ''  	=> ${null == ''}
null == 'a'  	=> ${null == 'a'}
null == '1'  	=> ${null == '1'}
null == true  	=> ${null == true}
null == false  	=> ${null == false}

!!(null)		=> ${!!(null)}
🎉 Number(null)	=> ${Number(null)}

undefined == 1  	=> ${undefined == 1}
undefined == 0  	=> ${undefined == 0}
undefined == -1  	=> ${undefined == -1}
undefined == ''  	=> ${undefined == ''}
undefined == 'a'  	=> ${undefined == 'a'}
undefined == '1'  	=> ${undefined == '1'}
undefined == true  	=> ${undefined == true}
undefined == false  => ${undefined == false}

!!(undefined)	=> ${!!(undefined)}
Number(undefined)	=> ${Number(undefined)}
`);
@wangsiyuan0215
Copy link
Owner Author

/* 引用类型之间 == 比较 */
console.log(`
#----------- 引用类型之间 == 比较 -----------#
#----------- 引用类型之间 == 比较 -----------#
#----------- 引用类型之间 == 比较 -----------#
@: 引用类型比较是基于其指针(内存地址)进行比较

[] == [] 							=> ${[] == []}
{} == {} 							=> ${{} == {}}
function() {} == function() {} 		=> ${function() {} == function() {}}
[] = {}								=> ${[] == {}}
function() {} = {}					=> ${function() {} == {}}
function() {} = []					=> ${function() {} == []}
`);

@wangsiyuan0215
Copy link
Owner Author

wangsiyuan0215 commented Mar 14, 2019

/* 引用类型与基础类型之间 == 比较 */
var emptyArray = [];
var emptyObject = {};
console.log(`
#----------- 引用类型与基础类型之间 == 比较 -----------#
#----------- 引用类型与基础类型之间 == 比较 -----------#
#----------- 引用类型与基础类型之间 == 比较 -----------#
@: 引用类型与基础类型之间会先调用引用类型的 toString 或 valueOf 的方法
@: 一般滴,会先调用引用类型的 valueOf 方法
		若其返回值的类型是基础类型,继续按照基础类型进行比较;
		若其返回值的类型仍然是引用类型,那么会调用 toString,根据其返回值进行比较。
@: 但是有个例外,Date 对象当其出现在 ”+“ 运算符中,调用其 toString 方法,当出现在其他运算中时,调用 valueOf 方法。

{ toString: () => 1, valueOf: () => [] } == 1 				=> ${{ toString: () => 1, valueOf: () => [] } == 1}
{ toString: () => 1, valueOf: () => [] } == 0 				=> ${{ toString: () => 0, valueOf: () => [] } == 0}
{ toString: () => true, valueOf: () => [] } == 1 			=> ${{ toString: () => true, valueOf: () => [] } == 1}
{ toString: () => false, valueOf: () => [] } == 0 			=> ${{ toString: () => false, valueOf: () => [] } == 0}
{ toString: () => '1', valueOf: () => [] } == 1 			=> ${{ toString: () => '1', valueOf: () => [] } == 1}
{ toString: () => '0', valueOf: () => [] } == 0 			=> ${{ toString: () => '0', valueOf: () => [] } == 0}
{ toString: () => 'a', valueOf: () => [] } == 1 			=> ${{ toString: () => 'a', valueOf: () => [] } == 1}
{ toString: () => 'a', valueOf: () => [] } == 'a' 			=> ${{ toString: () => 'a', valueOf: () => [] } == 'a'}
{ toString: () => null, valueOf: () => [] } == false 		=> ${{ toString: () => null, valueOf: () => [] } == false}
{ toString: () => undefined, valueOf: () => [] } == false 	=> ${{ toString: () => undefined, valueOf: () => [] } == false}

{ toString: () => undefined, valueOf: () => false } == true => ${{ toString: () => undefined, valueOf: () => '' } == true}
{ toString: () => undefined, valueOf: () => '1' } == true 	=> ${{ toString: () => undefined, valueOf: () => '1' } == true}
{ toString: () => undefined, valueOf: () => false } == true => ${{ toString: () => undefined, valueOf: () => 1 } == true}
{ toString: () => undefined, valueOf: () => 0 } == false 	=> ${{ toString: () => undefined, valueOf: () => 0 } == false}

{ toString: () => true, valueOf: () => false } == true 		=> ${{ toString: () => true, valueOf: () => false } == true}
{ toString: () => true, valueOf: () => '' } == true 		=> ${{ toString: () => true, valueOf: () => '' } == true}
{ toString: () => false, valueOf: () => 1 } == false 		=> ${{ toString: () => false, valueOf: () => 1 } == false}

@: 当引用类型与 null 和 undefined 进行比较时,由于 null 的类型是 Obejct (引用类型),因此属于引用类型之间进行比较
@: 当引用类型与 null 和 undefined 进行比较时,由于 undefined 的类型是 undefined,undefined 与除了 null 和它自身以外的类型均为 false

🎉 { toString: () => undefined, valueOf: () => [] } == null 			=> ${{ toString: () => undefined, valueOf: () => [] } == null}
🎉 { toString: () => null, valueOf: () => [] } == undefined 			=> ${{ toString: () => null, valueOf: () => [] } == undefined}
🎉 { toString: () => undefined, valueOf: () => [] } == undefined 		=> ${{ toString: () => undefined, valueOf: () => [] } == undefined}
🎉 { toString: () => null, valueOf: () => [] } == null 					=> ${{ toString: () => null, valueOf: () => [] } == null}
🎉 { toString: () => null, valueOf: () => [] } == null 					=> ${{ toString: () => null, valueOf: () => null } == null}
🎉 { toString: () => undefined, valueOf: () => null } == null 			=> ${{ toString: () => undefined, valueOf: () => null } == null}
🎉 { toString: () => null, valueOf: () => undefined } == undefined 		=> ${{ toString: () => null, valueOf: () => undefined } == undefined}
🎉 { toString: () => undefined, valueOf: () => [] } == undefined 		=> ${{ toString: () => undefined, valueOf: () => undefined } == undefined}

#-------  值得注意的是,[] 与 {}(空对象)的 valueOf 方法均返回自身 ------#

var emptyArray = [];
var emptyObject = {};
emptyArray == emptyArray.valueOf()			=> ${emptyArray == emptyArray.valueOf()}
emptyObject == emptyObject.valueOf()		=> ${emptyObject == emptyObject.valueOf()}

// from https://www.haorooms.com/post/js_yinxingleixing
@: 大多数对象隐式转换为值类型都是首先尝试调用valueOf()方法。
	但是Date对象是个例外,此对象的valueOf()和toString()方法都经过精心重写,
	默认是调用toString()方法,比如使用+运算符,
	如果在其他算数运算环境中,则会转而调用valueOf()方法。

var date = new Date();
console.log(date + "1"); 	//Sun Apr 17 2014 17:54:48 GMT+0800 (CST)1
console.log(date + 1);		//Sun Apr 17 2014 17:54:48 GMT+0800 (CST)1
console.log(date - 1);		//1460886888556
console.log(date * 1);		//1460886888557
`);

@wangsiyuan0215
Copy link
Owner Author

wangsiyuan0215 commented Mar 21, 2019

/* 隐式转换 */
var emptyArray = [];
var emptyObject = {};

console.log(`
#----------- 基础类型之间转换 -----------#
#----------- 基础类型之间转换 -----------#
#----------- 基础类型之间转换 -----------#

#----------- 转换为 Number 类型 -----------#
#----------- 转换为 Number 类型 -----------#

Number(true)			=> ${Number(true)}
+true				=> ${+true}
🎉 -true				=> ${-true} -> -(+true) -> -1
Number(false)			=> ${Number(false)}
🎉 -false				=> ${-false}
🎉 Number(null)		=> ${Number(null)}
Number(undefined)	=> ${Number(undefined)}
Number('123')			=> ${Number('123')}
Number('0')			=> ${Number('0')}
🎉 Number('')			=> ${Number('')}
Number('a')			=> ${Number('a')}
Number('1.34')		=> ${Number('1.34')}
Number('1.ab')		=> ${Number('1.ab')}
Number('1ab')			=> ${Number('1ab')}
+'1ab'				=> ${+'1ab'}

parseInt('1.2', 10)		=> ${parseInt('1.2', 10)}
parseInt('1cg', 10)		=> ${parseInt('1cg', 10)}
parseInt('cg', 10)		=> ${parseInt('cg', 10)}
parseInt(true, 10)		=> ${parseInt(true, 10)}
parseInt(false, 10)		=> ${parseInt(false, 10)}
parseInt([], 10)		=> ${parseInt([], 10)}
parseInt({}, 10)		=> ${parseInt({}, 10)}
parseInt(null, 10)		=> ${parseInt(null, 10)}
parseInt(undefined, 10)	=> ${parseInt(undefined, 10)}

parseFloat('1.2')		=> ${parseFloat('1.2')}
parseFloat('1.3cg')		=> ${parseFloat('1.3cg')}
parseFloat('1cg')		=> ${parseFloat('1cg')}
parseFloat('cg')		=> ${parseFloat('cg')}
parseFloat(true)		=> ${parseFloat(true)}
parseFloat(false)		=> ${parseFloat(false)}
parseFloat([])			=> ${parseFloat([])}
parseFloat({})			=> ${parseFloat({})}
parseFloat(null)		=> ${parseFloat(null)}
parseFloat(undefined)	=> ${parseFloat(undefined)}

@: 值得注意的是,null 转换为 Number 后是 0;
@: parseInt 与 parseFloat 会从参数的第一位开始,
	直到转换为 Number 后为 NaN 的位为止,将之前的部分转换为 Number 类型;
@: 转换为 Number 类型,除了用构造函数,还可以用 +、-、*、/、~~、|0 等方式。

#----------- 转换为 Boolean 类型 -----------#
#----------- 转换为 Boolean 类型 -----------#
@: 通常,除了使用 Boolean 的构造函数之外,可以使用 ! 和 !! 进行转换为布尔类型。

!!0					=> ${!!0}
!!1					=> ${!!1}
!!123				=> ${!!123}
!!(-23)				=> ${!!(-23)}
!!'123'				=> ${!!'123'}
!!'0'					=> ${!!'0'}
!!''					=> ${!!''}
!!' '					=> ${!!' '}
!!'abc'				=> ${!!'abc'}
!![]					=> ${!![]}
!!{}					=> ${!!{}}
!!(() => {})			=> ${!!(() => {})}
!!null				=> ${!!null}
!!undefined			=> ${!!undefined}

#----------- 转换为 String 类型 -----------#
#----------- 转换为 String 类型 -----------#
@: 通常,除了使用 String 的构造函数之外,可以使用 '' + 转换为字符串类型。

String(1)				=> ${String(1)}  & ${typeof String(1)}
String(0)				=> ${String(0)}  & ${typeof String(0)}
String(123)			=> ${String(123)} & ${typeof String(123)}
String(true)			=> ${String(true)} & ${typeof String(true)}
String(false)			=> ${String(false)} & ${typeof String(false)}
String([])				=> ${String([])} & ${typeof String([])}
'' + []				=> ${'' + []} & ${typeof ('' + [])}
String({})				=> ${String({})} & ${typeof String({})}
'' + {}				=> ${'' + {}} & ${typeof ('' + {})}
String(function () {})	=> ${String(function () {})} & ${typeof String(function () {})}
String(null) 			=> ${String(null)} & ${typeof String(null)}
'' + null				=> ${'' + null} & ${typeof ('' + null)}
String(undefined) 		=> ${String(undefined)} & ${typeof String(undefined)}
'' + undefined			=> ${'' + undefined} & ${typeof ('' + undefined)}


@@@: 需要注意的是
// String + Number 会将 Number -> String
1. 1 + '20' = ${1 + '20'}

// String 与 Number 相减 会将 String -> Number 
🎉  @@: 可以看做是 10 - (+‘20’) = 10 - 20 = -10
🎉  @@@: 当出现 - 符号时,只有 Number 才会有减操作,因此,会先将减号两边的类型转为 Number 再进行减法
2. 10 - '20' = ${10 - '20'} OR '10' - '20' = ${'10' - '20'} OR '10' - 20 = ${'10' - 20}

// Number 与 null 相加 会将 null -> Number(null) -> ${Number(null)}
3. 1 + null = ${1 + null} OR null + 1 = ${null + 1}

// Number 与 undefined 相加 会将 undefined -> Number(undefined) -> ${Number(undefined)}
4. 1 + undefined = ${1 + undefined} OR undefined + 1 = ${undefined + 1}

// String 与 null 相加 会将 null -> String(null) -> ${String(null)}
5. 'abc' + null = ${'abc' + null} OR null + 'abc' = ${null + 'abc'}

// String 与 undefined 相加 会将 undefined -> String(undefined) -> ${String(undefined)}
6. 'abc' + undefined = ${'abc' + undefined} OR undefined + 'abc' = ${undefined + 'abc'}

// [] 与 String 相加 会将 [] -> [].toString() -> [].join(',')
7. 'abc' + [1,2,3] = ${'abc' + [1,2,3]} OR [1,2,3] + 'abc' = ${[1,2,3] + 'abc'}

// {} 与 String 相加 会将 {} -> {}.toString()
8. 'abc' + { a: 1 } = ${'abc' + { a: 1 }} OR { a: 1 } + 'abc' = ${{ a: 1 } + 'abc'}

// [] 与 Number 相加 会先将 [] -> [].toString() -> [].join(',')
// 再 将 Number -> String
9. 1 + [1,2,3] = ${1 + [1,2,3]} & ${typeof (1 + [])} OR [1,2,3] + 1 = ${[1,2,3] + 1}

// {} 与 Number 相加 会将 {} -> {}.toString()
// 再 将 Number -> String
10. 1 + { a: 1 } = ${1 + { a: 1 }} OR { a: 1 } + 1 = ${{ a: 1 } + 1}

// Boolean 与 Number 相加 会将 Boolean -> Number
11. 1 + true = ${1 + true} OR true + 1 = ${true + 1}

// Boolean 与 String 相加 会将 Boolean -> String
12. 'abc' + true = ${'abc' + true} OR true + 'abc' = ${true + 'abc'}

// [] 与 Boolean 相加 会先将 [] -> [].toString() -> [].join(',')
// 再 将 Boolean -> String
13. true + [1,2,3] = ${true + [1,2,3]} & ${typeof (true + [1,2,3])} OR [1,2,3] + true = ${[1,2,3] + true}

// {} 与 Boolean 相加 会将 {} -> {}.toString()
// 再 将 Boolean -> String
14. true + { a: 1 } = ${true + { a: 1 }} OR { a: 1 } + 1 = ${{ a: 1 } + true}


15. true + { toString: () => ({}), valueOf: () => '3' }
	= ${true + { toString: () => ({}), valueOf: () => '3' }}

// error: Cannot convert object to primitive value
16. true + { toString: () => ({}), valueOf: () => ([]) }
	= ${true + { toString: () => ({}), valueOf: () => 'errrrrrrror' }}

@@@: 关于相加,引用类型先调用 toString 方法,若 toString 方法返回的不是基础类型则调用 valueOf 方法,
	若 valueOf 方法返回的也不是基础类型,则会报错;(可以试着将 16 的等式后面的 errrrrrrror 替换为 [])

`)

@wangsiyuan0215
Copy link
Owner Author

wangsiyuan0215 commented Apr 1, 2019

null 的类型是 object,但是其没有任何原型方法,比如 valueOftoString
与此同时,null 隐式转换会为 false,但是 null 与其他基本类型(除去 undefined)"==" 比较时,总为 false,仅仅与 undefined 相比较时,为 true

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant