Java Script 中 ==(Equal) 和 === (Identity Equal) 的区别和比较算法逻辑

时间:2021-01-06 19:09:13

判断两个变量是否相等在任何编程语言中都是非常重要的功能。

JavaScript 提供了 == 和 === 两种判断两个变量是否相等的运算符,但我们开始学习的时候 JavaScript 的时候,就被一遍又一遍的告知:

  • === 要求变量的类型和值均相等,才能返回true。
  • 使用 === 来避免因JavaScript 类型转换带来的问题。

这样增加了 JavaScript 语法的灵活性但是也带来很多头疼的问题:

  • 使用 ==/!=是 ===/!== 来判断两个变量是否相等?
  • 为什么,JS 编码推荐使用 ===/!= 而不是 ==/!=,大部分的编程语言不都是使用==/!=么?

为了要回答这个问题,让我们看一下 JavaScript 所遵守的标准 ECMAScript 对于==和 === 是怎么描述的吧!

=== 详解

Identity Equal或 Strict Equal, 在 ECMAScript -- Java Script 所遵守的标准中,算法的定义为:The Strict Equality Comparison Algorithm, 规则如下:

  1. 如果 参数 x 的数据类型和 参数 y 的数据类型不一致,这返回 false
  2. 如果 参数 x 的数据类型为 undenfined, 则返回 true
  3. 如果 参数 x 的数据类型为 null, 则返回 true
  4. 如果 参数 x 的数据类型为 Number, 则:
    1. 如果 x 是  NaN 返回 false
    2. 如果 y 是  NaN 返回 false
    3. 如果 x 是 +0 并且 y 为 -0, 返回 true
    4. 如果 x 是 -0 并且 y 为 +0, 返回 true
    5. 如果 x 和 y 有着相同的数值,返回 true
    6. 返回 false
  5. 如果 x 的类型为 String, 且 x 与 y 有着相同的顺序排列的字符串, 返回 true
  6. 如果 x 的类型为 boolean, 且 x 与 y 拥有相同的布尔值,返回 true
  7. 如果 x 的类型为 Object, 且 x 与 y 指向相同的对象,返回 true

伪代码:

 function strictEqual(x, y) {
// If Type(x) is different from Type(y), return false.
if (!valueEqual(typeof (x), typeof (y))) {
return false;
} // If Type(x) is Undefined, return true.
// If Type(x) is Null, return true.
if (valueEqual(typeof (x), "undefined") || valueEqual(x, null)) {
return true;
} if (valueEqual(typeof (x), "number")) {
// If x is NaN, return false.
if (isNaN(x)) {
return false;
} // If y is NaN, return false.
if (isNaN(y)) {
return false;
} // If x is +0 and y is −0, return true.
if (valueEqual(x, +0) && valueEqual(y, -0)) {
return true;
} // If x is −0 and y is +0, return true.
if (valueEqual(y, +0) && valueEqual(x, -0)) {
return true;
} // If x is the same Number value as y, return true.
if (valueEqual(x, y)) {
return true;
} return false;
} if (valueEqual(typeof (x), "string")) {
// If Type(x) is String, then return true if x and y are exactly
// the same sequence of characters
// (same length and same characters in corresponding positions); otherwise, return false.
return hasSameChar(x, y);
} if (valueEqual(typeof (x), "boolean")) {
return valueEqual(x, y);
} if (valueEqual(typeof (x), "object")) {
// Return true if x and y refer to the same object. Otherwise, return false.
return hasSameReference(x, y);
} return false;
}

逻辑图:

Java Script 中 ==(Equal) 和 === (Identity Equal) 的区别和比较算法逻辑

== 详解

Equal, 在两个对比变量数据类型相同时, 和=== 有着一样的行为算法实现,但是当两个对比的变量数据类型不同时,ECMAScript/JavaScript 有着自定义的转换和比较逻辑:参考 The Abstract Equality Comparison Algorithm

    1. 如果 x 为 null, 且 y 为 undefined, 返回 true
    2. 如果 x 为 undefined, 且 y 为 null, 返回 true
    3. 如果 x 的数据类型为 Number, 且 y 的数据类型为 string, 则将 y 转换为 Number,然后进行比较
    4. 如果 x 的数据类型为 String, 且 y 的数据类型为 Number, 则将 x 转换为 Number,然后进行比较
    5. 如果 x 的数据类型为 Boolean, 将x 转换为数字类型,当 x 为 true 时转换为 1, 否则转换为 0 进行比较
    6. 如果 y 的数据类型为 Boolean, 将 y 转换为数字类型,当 y 为 true 时转换为 1, 否则转换为 0 进行比较
    7. 如果 x 的数据类型为 String 或者 Number, 且 y 为 Object, 则使用 valueOf 函数,将 y 转换为简单类型进行比较
    8. 如果 y 的数据类型为 String 或者 Number, 且 x 为 Object, 则使用 valueOf 函数,将 x 转换为简单类型进行比较
    9. 返回 false

       从上述定义不难总结出以下几点:

      1. 该算法为递归算法,转换后,继续调用其自身直到能比较且返回为止
      2. 该算法依赖于 Strict Equal 的实现
      3. 进行转换时,具体转换依赖于数据类型的定义的方法,如Number() 函数

      伪代码:

     function abstractEqual(x, y) {
    
         // if x and y has same type
    if (valueEqual(typeof (x), typeof (y))) {
    return strictEqual(x, y);
    } // If x is null and y is undefined, return true.
    if (valueEqual(x, null) && valueEqual(y, undefined)) {
    return true;
    } // If x is undefined and y is null, return true.
    if (valueEqual(x, undefined) && valueEqual(y, null)) {
    return true;
    } // Type(x) is Number and Type(y) is String,
    if (valueEqual(typeof (x), "number") && valueEqual(typeof (y), "string")) { var convertedY = Number(y); // return the result of the comparison x == ToNumber(y)
    return abstractEqual(x, convertedY);
    } // Type(x) is Number and Type(y) is String,
    if (valueEqual(typeof (x), "string") && valueEqual(typeof (y), "number")) { var convertedX = Number(x); // return the result of the comparison x == ToNumber(y)
    return abstractEqual(convertedX, y);
    } // Type(x) is Boolean, return the result of the comparison ToNumber(x) == y.
    if (valueEqual(typeof (x), "boolean")) {
    var convertedToIntX = Number(x); return abstractEqual(convertedToIntX, y);
    } // Type(x) is Boolean
    if (valueEqual(typeof (y), "boolean")) {
    var convertedToIntY = Number(y); // return the result of the comparison ToNumber(x) == y.
    return abstractEqual(x, convertedToIntY);
    } // If Type(x) is either String or Number and Type(y) is Object,
    if ((valueEqual(typeof (x), "string") || valueEqual(typeof (x), "number")) && valueEqual(typeof (y), "object")) {
    var toPrimitiveY = y.valueOf(); // return the result of the comparison x == ToPrimitive(y).
    return abstractEqual(x, toPrimitiveY);
    } // If Type(x) is either String or Number and Type(y) is Object,
    if ((valueEqual(typeof (y), "string") || valueEqual(typeof (y), "number")) && valueEqual(typeof (x), "object")) {
    var toPrimitiveX = x.valueOf(); // return the result of the comparison x == ToPrimitive(y).
    return abstractEqual(toPrimitiveX, y);
    } return false;
    }

    逻辑图:

    Java Script 中 ==(Equal) 和 === (Identity Equal) 的区别和比较算法逻辑

    附加上本例使用的判断相等的函数的代码,直接使用了 JavaScript 的 == 来实现,为了 demo 么!呵呵,这是一个很号的接口,实际上,我也实现不出来 :).

     function valueEqual(x, y) {
    return x === y;
    } function hasSameChar(x, y) {
    return x === y;
    } function hasSameReference(x, y) {
    return x === y;
    }

    总结

    现在,我们已经知道 == 和 === 在判断两个变量是否相等时所使用的算法的基本实现。帮助我们理解一些 JavaScript 中判断相等时一些"诡异“ 的行为。

    把我们写的 Script 放在一个 HTML 文件里,用 Chrome 代开,按 F12, 开始我们的调试吧:

    测试 JS 代码 运行结果           JS 代码   运行结果 备注
    var x = 1, y = "1";console.log(strictEqual(x,y)); console.log(abstractEqual(x,y)) false, true var x = 1, y = "1";console.log(x === y); console.log(x == y) false,true == 时,y 先转换为数字类型1
    var x = 1, y = "not a number";console.log(strictEqual(x,y)); console.log(abstractEqual(x,y)) false, falase var x = 1, y = "not a number";console.log(x === y); console.log(x == y) false, false  y 转换为数字类型失败,返回 NaN,NaN 不与任何值相等,包括 NaN 自身                  
    var x = undefined, y = null;console.log(strictEqual(x,y)); console.log(abstractEqual(x,y)) false,true var x = undefined, y = null;console.log(x===y); console.log(x == y)

    false,true

    === 时, null != undefined

    == 时,规定了 null 与 undefined 的相等

    var x = true, y = 2;console.log(strictEqual(x,y)); console.log(abstractEqual(x,y)) false,false var x = true, y = 2;console.log(x === y); console.log(x == y)

    false,false

    true 转换为数字 1    

    var x = false, y = 0;console.log(strictEqual(x,y)); console.log(abstractEqual(x,y)) false,true var x = false, y = 0;console.log(x === y); console.log(x == y)

    false,true  

    false 转换为数字 0

    var x = {name:'test',valueOf:function(){return 1;}},y = 1; console.log(strictEqual(x,y));console.log(abstractEqual(x,y)); false,true var x = {name:'test',valueOf:function(){return 1;}},y = 1; console.log(x === y);console.log(x == y);

    false,true

    x.valueOf()  返回数字 1,与 y 相等