===和==的区别


1. ==和===区别:

首先它们两个引用数据类型都会比较地址值

区别如下:

  • === 是严格相等 == 代表相等
  • == 数据类型存在隐式转换,如数组转成字符串

简要分析:

//基本数据类型
//1. 存在隐式转换
let arr = [1,2]
let str = "1,2"
arr == str //true 因为arr调用了toString返回"1,2"
//2. toString
arr.toString = ()=>{}
arr == str //false 因为toString函数为空了

//3. valueOf(跳过第二步)
arr.valueOf = ()=>{}
arr == str //false 因为要先经过valueOf返回才能到toString

//引用数据类型
let arr1 = [1,2,3]
let arr2 = [1,2,3]
arr1 == arr2 //false 因为会比较地址值

2. 原始值

隐式转换会牵扯到原始值

原始值,也就是基本数据类型 string number … 除了引用类型Object Array …

如下:

let num = 12//这是原始值

let numObj = new Number(12); //这是对象 需要使用valueOf转换成原始值

3. 隐式转换

以下代码会成功打印 hello world! 这便是隐式转换

var a = {
  i: 1,
  toString: function () {
    return a.i++;
  }
}
if (a == 1 && a == 2 && a == 3) {
  console.log('hello world!');
}

隐式转换会经过三个内部函数 ToPrimitive ToNumber ToString (我们无法直接操作)

2.1 返回原始值ToPrimitive

ToPrimitive(input, PreferredType?)会尝试调用valueOf方法和toString方法返回原始值,它有以下两种情况调用:

一般情况 PreferredType自动设置为Number

1、如果输入的值已经是一个原始值,则直接返回它
2、否则,如果输入的值是一个对象,则调用该对象的valueOf()方法,
如果valueOf()方法的返回值是一个原始值,则返回这个原始值。
3、否则,调用这个对象的toString()方法,如果toString()方法返回的是一个原始值,则返回这个原始值。
4、否则,抛出TypeError异常。

特殊(Date对象) PreferredType自动设置为String

1、如果输入的值已经是一个原始值,则直接返回它
2、否则,调用这个对象的toString()方法,如果toString()方法返回的是一个原始值,则返回这个原始值。
3、否则,如果输入的值是一个对象,则调用该对象的valueOf()方法,
如果valueOf()方法的返回值是一个原始值,则返回这个原始值。
4、否则,抛出TypeError异常。

因为valueOf函数会将Number、String、Boolean基础类型的对象类型值转换成 基础类型,Date类型转换为毫秒数,其它的返回对象本身,而toString方法会将所有对象转换为字符串。显然对于大部分对象转换,valueOf转换更合理些,因为并没有规定转换类型,应该尽可能保持原有值,而不应该想toString方法一样,一股脑将其转换为字符串。

上面的例子改成这样

var a = {
  i: 1,
  toString: function () {
    alert("不输出原始值,没有隐式转换")
  }
}
if (a == 1 && a == 2 && a == 3) {
  console.log('hello world!');
}

不会隐式转换。因为a是一个引用类型,那么它必须先转成原始值。它首先会调用valueOf,发现不行还是引用类型,那么调用toString,发现还是不行,那么便存在内部报错,无法隐式转换。

2.1 ToNumber 和 ToString

对于原始值,js会直接调用这两内部方法隐式转换;对于引用类型,js会经过toPrimitive获取到原始值后再隐式转换。

通过ToNumber将值转换为数字

参数 结果
undefined NaN
null +0
布尔值 true转换1,false转换为+0
数字 无须转换
字符串 有字符串解析为数字,例如:‘324’转换为324,‘qwer’转换为NaN
对象(obj) 先进行 ToPrimitive(obj, Number)转换得到原始值,在进行ToNumber转换为数字

通过ToString将值转换为字符串

参数 结果
undefined ‘undefined’
null ‘null’
布尔值 转换为’true’ 或 ‘false’
数字 数字转换字符串,比如:1.765转为’1.765’
字符串 无需转换
对象(obj) 先进行 ToPrimitive(obj, String)转换得到原始值,在进行ToString转换为字符串

注意,以上转换并不适用任何情况,例如null == 0 为false 因为这样并不会进行隐式转换,而是 null >= 0 为true 因为比较运算符会进行隐式转换,==的情况请往下看

3. == 运算符隐式转换

比较运算 x==y, 其中 x 和 y 是值,返回 true 或者 false。这样的比较按如下方式进行:

1、若 Type(x)Type(y) 相同, 则

    1* 若 Type(x) 为 Undefined, 返回 true。
    2* 若 Type(x) 为 Null, 返回 true。
    3* 若 Type(x) 为 Number, 则
  
        (1)、若 x 为 NaN, 返回 false。
        (2)、若 y 为 NaN, 返回 false。
        (3)、若 x 与 y 为相等数值, 返回 true。
        (4)、若 x 为 +0 且 y 为 −0, 返回 true。
        (5)、若 x 为 −0 且 y 为 +0, 返回 true。
        (6)、返回 false。
        
    4* 若 Type(x) 为 String, 则当 x 和 y 为完全相同的字符序列(长度相等且相同字符在相同位置)时返回 true。 否则, 返回 false。
    5* 若 Type(x) 为 Boolean, 当 x 和 y 为同为 true 或者同为 false 时返回 true。 否则, 返回 false。
    6*  当 x 和 y 为引用同一对象时返回 true。否则,返回 false。
  
2、若 x 为 null 且 y 为 undefined, 返回 true。
3、若 x 为 undefined 且 y 为 null, 返回 true。
4、若 Type(x) 为 Number 且 Type(y) 为 String,返回比较 x == ToNumber(y) 的结果。
5、若 Type(x) 为 String 且 Type(y) 为 Number,返回比较 ToNumber(x) == y 的结果。
6、若 Type(x) 为 Boolean, 返回比较 ToNumber(x) == y 的结果。
7、若 Type(y) 为 Boolean, 返回比较 x == ToNumber(y) 的结果。
8、若 Type(x) 为 String 或 Number,且 Type(y) 为 Object,返回比较 x == ToPrimitive(y) 的结果。
9、若 Type(x) 为 Object 且 Type(y) 为 String 或 Number, 返回比较 ToPrimitive(x) == y 的结果。
10、返回 false。

上面主要分为两类,x、y类型相同时,和类型不相同时。
类型相同时,没有类型转换,主要注意NaN不与任何值相等,包括它自己,即NaN !== NaN。
类型不相同时,
1、x,y 为null、undefined两者中一个 // 返回true
2、x、y为Number和String类型时,则转换为Number类型比较。
3、有Boolean类型时,Boolean转化为Number类型比较。
4、一个Object类型,一个String或Number类型,将Object类型进行原始转换后,按上面流程进行原始值比较。

参考: https://juejin.cn/post/6844903557968166926


文章作者: iamfugui
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 iamfugui !
评论