zoukankan      html  css  js  c++  java
  • Javascript 中 == 和 === 区别是什么?

    Javascript 中 == 和 === 区别是什么?

    作者:Belleve
    链接:https://www.zhihu.com/question/31442029/answer/77772323
    来源:知乎
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

    谁告诉你 == 不会有副作用的
    var x = 1;
    var obj = {valueOf: function(){ x = 2; return 0 }}
    console.log(obj == 0, x) // true, 2
    
    甚至还会产生异常呢
    var x = 1;
    var obj = {valueOf: function(){ return {} }, toString: function(){ return {}}}
    console.log(obj == 0) // Error: Cannot convert object to primitive value
    

    ————————————————————————————————————————————
    这是 ==这是 ===完整比较图:
    • 红色:===
    • 橙色:==
    • 黄色:<= 和 >= 同时成立,== 不成立
    • 蓝色:只有 >=
    • 绿色:只有 <=
    (参:JS Comparison Table, Edit fiddle - JSFiddle) 顺便,有人说这图像现代艺术,感觉旋转完更像,删减重排换成 S1 配色之后做成头像了。
    不免费帮人做头像(也就是换个顺序的事情)
    作者:林建入
    链接:https://www.zhihu.com/question/20348948/answer/19601270
    来源:知乎
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

    很多时候我们会对某个语言的某个特性争论不休,通常都只是因为我们不知道它是怎么实现的。其实解决这个疑惑的最好的方法,就是弄清楚 == 和 === 内部的实现机制。
    例如 Google v8 就是开源的,只要看看里面的代码,就能知道到底是怎么一回事了。但是我相信绝大多数人都是没有这个耐心的——我知道大家都在忙着挣钱养家——好吧,那咱就不看 Google v8 了,看看 ECMA-262 吧,里面已经把实现算法描述得很清楚了,只要看懂了下面的内容,以后就再也不会有此类疑问了。
     
     

    一、严格相等运算符 === 的实现

    === 被称为 Strict Equals Operator,假设有表达式 a === b,则它的实际运算过程如下
    1. 计算出表达式 a 的结果,并存入 lref 变量
    2. 将 GetValue(lref) 的结果存入 lval 变量
    3. 计算出表达式 b 的结果,并存入 rref 变量
    4. 将 GetValue(rref) 的结果存入 rval 变量
    5. 执行 Strict Equality Comparison 算法判断 rval === lval 并将结果直接返回

    这里的 Strict Equality Comparison 算法很关键,假设要计算的是 x === y,则过程如下

    1. 如果 Type(x) 和 Type(y) 不同,返回 false

    2. 如果 Type(x) 为 Undefined,返回 true

    3. 如果 Type(x) 为 Null,返回 true

    4. 如果 Type(x) 为 Number,则进入下面的判断逻辑

    4.1. 如果 x 为 NaN,返回 false

    4.2. 如果 y 为 NaN,返回 false

    4.3. 如果 x 的数字值和 y 相等,返回 true

    4.4. 如果 x 是 +0 且 y 是 -0,返回 true

    4.5. 如果 x 是 -0 且 y 是 +0,返回 ture

    4.6. 返回 false

    5. 如果 Type(x) 为 String,则当且仅当 x 与 y 的字符序列完全相同(长度相等,每个位置上的字符相同)时返回 true,否则返回 false

    6. 如果 Type(x) 为 Boolean,则若 x 与 y 同为 true 或同为 false 时返回 true,否则返回 false

    7. 如果 x 和 y 引用的是同一个对象,返回 true,否则返回 false


    二、相等运算符 == 的实现

    好了,当你明白了 === 的实现之后,我们再来看看 == 的实现,比较一下他们有何不同?
    == 被称为 Equals Operator (注意看没有 Strict 了),假设有表达式 a == b,则它的实际运算过程如下
    1. 计算出表达式 a 的结果,并存入 lref 变量
    2. 将 GetValue(lref) 的结果存入 lval 变量
    3. 计算出表达式 b 的结果,并存入 rref 变量
    4. 将 GetValue(rref) 的结果存入 rval 变量
    5. 执行 Abstract Equality Comparison 算法判断 rval == lval 并将结果直接返回

    注意,其中的前 4 个步骤是和 === 完全相同的。唯独 5 不同。对于 === 来说,调用的是 Strict Equality Comparison 算法,但是 == 则调用的是 Abstract Equality Comparison 算法。虽然仅一词之差,但是却有质的不同,我们下面就来看看到底它是怎么实现的

    假设要计算的是 x == y,Abstract Equality Comparison 计算的过程如下(很冗长,但是每个步骤都很简单)

    1. 如果 Type(x) 和 Type(y) 相同,则

    1.1. 如果 Type(x) 为 Undefined,返回 true

    1.2. 如果 Type(x) 为 Null,返回 true

    1.3. 如果 Type(x) 为 Number,则

    1.3.1. 如果 x 是 NaN,返回 false

    1.3.2. 如果 y 是 NaN,返回 false

    1.3.3. 如果 x 的数值与 y 相同,返回 true

    1.3.4. 如果 x 是 +0 且 y 是 -0,返回 true

    1.3.5. 如果 x 是 -0 且 y 是 +0,返回 true

    1.3.6. 返回 false

    1.4. 如果 Type(x) 为 String,则当且仅当 x 与 y 的字符序列完全相同(长度相等,每个位置上的字符相同)时返回 true,否则返回 false

    1.5. 如果 Type(x) 为 Boolean,则若 x 与 y 同为 true 或同为 false 时返回 true,否则返回 false

    1.6. 如果 x 和 y 引用的是同一个对象,返回 true,否则返回 false

    2. 如果 x 是 null 且 y 是 undefined,返回 true

    3. 如果 x 是 undefined 且 y 是 null,返回 ture

    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


    三、总结


    从上面的算法流程可以看出,a === b 是最简单的。如果 a 和 b 的类型不同,那么一定会返回 false。而 a == b 则要灵活得多。JavaScript 会尝试调整 a 和 b 的类型,例如若 a 为字符串 b 为数字,则将字符串先转化为数字再与 b 比较,等等。这在很多时候简化了程序员的代码量。


    一些程序员害怕 == 而提倡使用 === 的根本原因是,他们不知道在 == 的内部具体发生了什么。而这就导致误用和出错。


    很多人认为,当我们不了解一个 feature 的时候,我们就不用它就行了。这也许是一种习惯性逻辑。但尴尬的是你不用这个 feature 没问题,别人却可能还是会用。为了能读懂别人的代码,我们又必须实际上搞清楚这内在的逻辑。所以最终还是得付出这方面的学习成本。

    "==="叫做严格运算符,"=="叫做相等运算符。

    严格运算符的运算规则如下,
    (1)不同类型值
    如果两个值的类型不同,直接返回false。
    (2)同一类的原始类型值

    同一类型的原始类型的值(数值、字符串、布尔值)比较时,值相同就返回true,值不同就返回false。

    (3)同一类的复合类型值

    两个复合类型(对象、数组、函数)的数据比较时,不是比较它们的值是否相等,而是比较它们是否指向同一个对象。

    (4)undefined和null

    undefined 和 null 与自身严格相等。

    null === null  //true
    undefined === undefined  //true
    

    相等运算符在比较相同类型的数据时,与严格相等运算符完全一样。

    在比较不同类型的数据时,相等运算符会先将数据进行类型转换,然后再用严格相等运算符比较。类型转换规则如下:

    (1)原始类型的值

    原始类型的数据会转换成数值类型再进行比较。字符串和布尔值都会转换成数值,所以题主的问题中会有第二个string输出。

    (2)对象与原始类型值比较

    对象(这里指广义的对象,包括数值和函数)与原始类型的值比较时,对象转化成原始类型的值,再进行比较。

    (3)undefined和null

    undefined和null与其他类型的值比较时,结果都为false,它们互相比较时结果为true。

    (4)相等运算符的缺点

    相等运算符隐藏的类型转换,会带来一些违反直觉的结果。

    '' == '0'           // false
    0 == ''             // true
    0 == '0'            // true
    
    false == 'false'    // false
    false == '0'        // true
    
    false == undefined  // false
    false == null       // false
    null == undefined   // true
    
    ' 	
     ' == 0     // true
    

    这就是为什么建议尽量不要使用相等运算符。

    至于使用相等运算符会不会对后续代码造成意外影响,答案是有可能会。

    var a = undefined;
    if(!a){
        console.log("1"); //1
    }
    
    var a = undefined;
    if(a == null){
        console.log("1"); //1
    }
    
    var a = undefined;
    if(a === null){
        console.log("1"); //无输出
    }
    

    也就是说当a为undefined时,输出的值会有变化,而在编程中对象变成undefined实在是太常见了。

    <canvas id="drawCanvas" width="1600" height="1600" style="height:800px;800px"/>
    var cmp = function(v1, v2) { return v1 == v2; };
    var vals = [
            ["null", function() { return null; }],
        ["undefined", function() { return undefined; }],
        ["false", function() { return false; }], 
        ['"false"', function() { return "false"; }],
        ["Boolean(false)", function() { return new Boolean(false); }],
        ["[]", function() { return []; }], 
        ["[[]]", function() { return [[]]; }], 
        ['""', function() { return ""; }],
        ['String("")',function(){return new String('')}],
        
        ["0", function() { return 0; }],
        ["Number(0)",function(){return new Number(0)}],
        ['"0"', function() { return "0"; }], 
        ['String("0")',function(){return new String('0')}],
        
        
        ["[0]", function() { return [0]; }], 
        
            ["true", function() { return true; }],
        ['"true"', function() { return "true"; }],
        ["Boolean(true)", function() { return new Boolean(true); }],
        
        
        ["1",function() { return  1; }],
        ["Number(1)",function(){return new Number(1)}],
        ['"1"', function() { return "1"; }],
        ['String("1")',function(){return new String('1')}],
        
        
        ["[1]", function() { return [1]; }],
        
            
        ["-1", function() { return -1; }],
        ["Number(-1)",function(){return new Number(-1)}],
        ['"-1"', function() { return "-1"; }],
        ['String("-1")',function(){return new String('-1')}],
        ["[-1]", function() { return [-1]; }],
        
        ["Infinity", function() { return Infinity; }],
        ["-Infinity", function() { return -Infinity; }],
        
        
        ["{}", function() { return {}; }], 
        ["NaN", function() { return NaN; }]
    ];
    
    var canvas = document.getElementById("drawCanvas");
    var ctx = canvas.getContext("2d");
    var n = vals.length;
    var r = 40; // diameter of grid squares
    var p = 160; // padding space for labels
    
    // color grid cells
    for (var i = 0; i < n; i++) {
        var v1 = vals[i][1]();
        for (var j = 0; j < n; j++) {
            var v2 = vals[j][1]();
            var eq = cmp(v1, v2);
            ctx.fillStyle = eq ? "orange" : "white";
            ctx.fillRect(p+i*r,p+j*r,r,r);
        }
    }
    
    // draw labels
    ctx.fillStyle = "black";
    var f = 24;
    ctx.font = f + "px Helvetica";
    for (var i = 0; i < n; i++) {
        var s = vals[i][0];
        var w = ctx.measureText(s).width;
        ctx.save();
        ctx.translate(p+i*r+r/2-f*0.4,p-w-2);
        ctx.rotate(3.14159/2);
        ctx.fillText(s, 0, 0);
        ctx.restore();
    }
    for (var i = 0; i < n; i++) {
        var s = vals[i][0];
        var w = ctx.measureText(s).width;
        ctx.fillText(s, p-w-2, p+i*r+r/2+f*0.4);
    }
    
    // draw grid lines
    ctx.beginPath();
    ctx.strokeStyle = "black";
    for (var i = 0; i <= n; i++) {
        ctx.moveTo(p+r*i, p);
        ctx.lineTo(p+r*i, p+r*n);
        ctx.moveTo(p, p+r*i);
        ctx.lineTo(p+r*n, p+r*i);
    }
    ctx.stroke();
     
  • 相关阅读:
    【Python学习】读取Excel文件,并写入Excel
    异步编程
    LINQ入门
    [Leetcode Weekly Contest]207
    [Leetcode Weekly Contest]203
    VsCode插件,自动生成注释koroFileHeader
    vue样式穿透 ::v-deep的具体使用
    springcloud,springboot,springcloud-alibaba 之间版本关系
    sharding-sphere 单库分表实例
    CDN动态加速技术
  • 原文地址:https://www.cnblogs.com/lsgxeva/p/7967379.html
Copyright © 2011-2022 走看看