javaScript 深入学习(2)类型隐式转换

138 阅读4分钟

ES6 前,JavaScript 共有五种原始数据类型:Undefined、Null、Boolean、Number、String

先由一道传说的大厂面试题开始

如何让 (a==1&&a==2&a==3) 为true?

答案基本上都如下:

let a = {
	i:1,
	valueOf(){
		return a.i++
	},
    toString: function() {
		return a.i++
	}
}

console.log(a==1&&a==2&a==3);//true

这里就涉及:

  • 隐式转换
  • Object的valueOf函数,toString函数

object隐式转换规则

根据 ES5 9.8 的规范

object在发生到number和string的转换规则如下:

参数类型 设备文件名
Object 1. primValue = ToPrimitive(input, String/Number)
2. 返回ToString(primValue).

让我们看规范 9.1,函数语法表示如下:

ToPrimitive(input[, PreferredType])

第一个参数是 input,表示要处理的输入值。

第二个参数是 PreferredType,非必填,表示希望转换成的类型,有两个值可以选,Number 或者 String。

当不传入 PreferredType 时,如果 input 是日期类型,相当于传入 String,否则,都相当于传入 Number。

如果传入的 input 是 Undefined、Null、Boolean、Number、String 类型,直接返回该值。

接下来:

如果是 ToPrimitive(obj, Number),处理步骤如下:

  1. 如果 obj 为 基本类型,直接返回

  2. 否则,调用 valueOf 方法,如果返回一个原始值,则 JavaScript 将其返回。

  3. 否则,调用 toString 方法,如果返回一个原始值,则 JavaScript 将其返回。

  4. 否则,JavaScript 抛出一个类型错误异常。

如果是 ToPrimitive(obj, String),处理步骤如下:

  1. 如果 obj为 基本类型,直接返回

  2. 否则,调用 toString 方法,如果返回一个原始值,则 JavaScript 将其返回。

  3. 否则,调用 valueOf 方法,如果返回一个原始值,则 JavaScript 将其返回。

  4. 否则,JavaScript 抛出一个类型错误异常。

扩展:当是==== 的时候

利用Object.defineProperty,实现的方法如下:

let value = 1;

Object.defineProperty(window,'a',{
	get(){
		return value++
	}
})

其他操作符

当执行 + 运算的时候,会执行怎样的步骤呢?根据规范11.6.1 来捋一捋:

当计算 value1 + value2时:

  1. lprim = ToPrimitive(value1)

  2. rprim = ToPrimitive(value2)

  3. 如果 lprim 是字符串或者 rprim 是字符串,那么返回 ToString(lprim) 和 ToString(rprim)的拼接结果

  4. 返回 ToNumber(lprim) 和 ToNumber(rprim)的运算结果

	console.log(null + 1);

按照规范的步骤进行分析:

  1. lprim = ToPrimitive(null) 因为null是基本类型,直接返回,所以 lprim = null

  2. rprim = ToPrimitive(1) 因为 1 是基本类型,直接返回,所以 rprim = null

  3. lprim 和 rprim 都不是字符串

  4. 返回 ToNumber(null) 和 ToNumber(1) 的运算结果

接下来:

ToNumber(null) 的结果为0,ToNumber(1) 的结果为 1

所以,null + 1 相当于 0 + 1,最终的结果为数字 1。

对象与数组

console.log([] + {});

按照规范:

  1. lprim = ToPrimitive([]),lprim = ""

  2. rprim = ToPrimitive({}),相当于调用 ToPrimitive({}, Number),先调用 valueOf 方法,返回对象本身,因为不是原始值,调用 3. toString 方法,返回 "[object Object]"

  3. lprim 和 rprim 都是字符串,执行拼接操作

所以,[] + {} 相当于 "" + "[object Object]",最终的结果是 "[object Object]"。

== 相等

"==" 用于比较两个值是否相等,当要比较的两个值类型不一样的时候,就会发生类型的转换。

关于使用"=="进行比较的时候,具体步骤可以查看规范11.9.5:

当执行x == y 时:

1.如果x与y是同一类型:

x是Undefined,返回true
x是Null,返回true
x是数字:
	x是NaN,返回false
	y是NaN,返回false
	x与y相等,返回true
	x是+0,y是-0,返回true
	x是-0,y是+0,返回true
	返回false
x是字符串,完全相等返回true,否则返回false
x是布尔值,x和y都是true或者false,返回true,否则返回false
x和y指向同一个对象,返回true,否则返回false

2. x是null并且y是undefined,返回true 3. x是undefined并且y是null,返回true

  1. x是数字,y是字符串,判断x == ToNumber(y)

  2. x是字符串,y是数字,判断ToNumber(x) == y

  3. x是布尔值,判断ToNumber(x) == y

  4. y是布尔值,判断x ==ToNumber(y)

  5. x不是字符串或者数字,y是对象,判断x == ToPrimitive(y)

  6. x是对象,y不是字符串或者数字,判断ToPrimitive(x) == y

  7. 返回false




最后放上:

参考大佬文章:JavaScript深入之头疼的类型转换