JavaScript比较中应该使用哪种等于运算符(== vs ===)?

5647

我使用JSLint检查JavaScript代码,它建议将比较语句中的==(两个等号)替换为===(三个等号),例如在if语句中比较idSele_UNVEHtype.value.length == 0

使用===代替==是否有性能优势?

任何性能提升都是受欢迎的,因为存在许多比较运算符。

如果不涉及类型转换,是否会比使用==更快?

48个回答

7165
严格相等运算符(===)与抽象相等运算符(==)的行为完全相同,除了不进行类型转换,并且必须具有相同的类型才能被视为相等。
参考:JavaScript 教程:比较运算符 == 运算符将在执行任何必要的类型转换后进行比较。 === 运算符将不会进行转换,因此如果两个值不是相同类型,则 === 将简单地返回 false。两者都同样快。
引用 Douglas Crockford 的优秀著作 JavaScript: The Good Parts 的话:

JavaScript 有两组相等运算符:===!==,以及它们的邪恶孪生兄弟 ==!=。好的运算符按照你的预期工作。如果两个操作数具有相同的类型并且具有相同的值,则 === 生成 true!== 生成 false。当操作数具有相同类型时,邪恶孪生兄弟也会做正确的事情,但是如果它们具有不同的类型,则会尝试强制转换值。它们执行此操作的规则是复杂而难以记忆的。以下是一些有趣的情况:

'' == '0'           // false
0 == ''             // true
0 == '0'            // true
false == 'false'    // false
false == '0'        // true
false == undefined  // false
false == null       // false
null == undefined   // true
' \t\r\n ' == 0     // true

Equality Comparison Table

缺乏传递性是令人担忧的。我的建议是永远不要使用邪恶的双胞胎。相反,总是使用 === !==。所有刚才展示的比较都使用 === 运算符产生 false

更新

@Casebash在评论和@Phillipe Laybaertanswer中提出了一个很好的观点,涉及对象。对于对象,=====之间始终一致(特殊情况除外)。

var a = [1,2,3];
var b = [1,2,3];

var c = { x: 1, y: 2 };
var d = { x: 1, y: 2 };

var e = "text";
var f = "te" + "xt";

a == b            // false
a === b           // false

c == d            // false
c === d           // false

e == f            // true
e === f           // true

特殊情况是当您将一个原始值与一个由于其toString或valueOf方法而评估为相同原始值的对象进行比较时。例如,考虑使用String构造函数创建的字符串对象与字符串基元之间的比较。
"abc" == new String("abc")    // true
"abc" === new String("abc")   // false

这里的==运算符检查两个对象的值并返回true,但===发现它们不是相同类型并返回false。哪一个是正确的?这取决于你想要比较什么。我的建议是完全绕过这个问题,不要使用String构造函数从字符串字面量创建字符串对象。

Reference
https://262.ecma-international.org/5.1/#sec-11.9.3


14
我还要指出0 === -0和NaN !== NaN,有时可能会令人困惑。如果您想区分±0并认为NaN相等,请使用Object.is(ES2015)。 - Ovinus Real
16
软类型是一种特性。显然,Crockford指出了该设计决策的一些“副作用”,但软类型仍然是一种特性。如果使用正确,它是完全可以使用的。不要因噎废食。 - aross
1
你为什么需要使用“-0”呢? - Adrian Larson
4
这是 IEEE-754 浮点数的一个特性。虽然并没有实际用途,但因为 IEEE-754 的“双精度”是一种“有符号数”表示方法,所以负零会“存在”。但为了避免出现意外情况,它被定义为等于正零。JavaScript(或 ECMAScript)(-0).toString() 定义为 "0",但并不是每种编程语言都是这样(例如在 C# 中,Math.Round(-0.1).ToString()"-0")。这可能会导致奇怪的修复方式,如 x == 0 ? 0 : x - Cole Tobin
1
@Cole:在 (-0).toString() 中,你确定 (-0) 会创建一个负零吗?(可能与符号字符是否是Javascript字面量语法语法的一部分有关,或者像许多其他语言一样,-0 是应用于仅由数字组成的文字的一元否定) - Ben Voigt
1
发现像大多数语言一样,(-0)是对仅由数字组成的文字面量应用一元减运算符:https://tc39.es/ecma262/#sec-literals-numeric-literals 但尽管如此,它确实形成负零 - Ben Voigt

1253

使用==运算符(相等性

true == 1; //true, because 'true' is converted to 1 and then compared
"2" == 2;  //true, because "2" is converted to 2 and then compared

使用 === 运算符 (恒等性判断)

true === 1; //false
"2" === 2;  //false

==操作符会进行类型转换,这意味着解释器在比较值之前会尝试隐式地将它们转换为相同类型。

另一方面,===操作符不会进行类型转换,在比较时不会将值转换为相同类型。


5
我认为“身份(identity)”在这里不是正确的术语。===用于检查相等性和类型(称为“严格”比较)。通常,“身份(identity)”指的是“相同性(sameness)”,可以通过ES2015中的Object.is函数来实现。 - BanksySan

890

这里有一份关于 ===== 相等性比较的有趣可视化。

来源: https://github.com/dorey/JavaScript-Equality-Table (演示, 统一演示)


var1 === var2

在 JavaScript 中使用 === 进行相等性测试时,一切都是原样的。
在进行求值之前不会进行任何转换。

JS中===的相等性求值

var1 == var2

在 JavaScript 中使用 == 进行相等性测试时,会发生一些奇怪的转换。

JS中==的相等性求值

Javascript中相等性的总结

Javascript中的相等性


结论:

始终使用 ===,除非您完全理解 == 中发生的奇怪转换(funky conversions)


10
一个更好的'=='对比表格:https://algassert.com/visualization/2014/03/27/Better-JS-Equality-Table.html - rocketsarefast
至少 == 比较是可交换的(即 (a==b) === (b==a))XD - imkzh
@Feuermurmel:你能给出一个==不是传递的例子吗? - SNag
哦,抱歉回复晚了。@SNag 当然没问题。取a = []b = falsec = [0] - Feuermurmel
@SNag 这背后有一个明显的(但我认为不好的)原因:在JS中,有值类型(例如数字、字符串;“更改它”通常意味着更改变量的值)和引用类型(例如对象、数组;“更改它”通常意味着修改引用实例)。==比较值类型的值和引用类型的引用(同一实例)。这很好而且有用(能够比较引用通常是有帮助的)。但当==强制转换引用类型进行比较时,它就会变得混乱不堪,隐式地将它们视为值类型。 - Feuermurmel
显示剩余4条评论

659
在这里的答案中,我没有看到任何关于等于意味着什么的内容。有些人会说===表示相等且类型相同,但这并不是真的。它实际上意味着两个操作数引用相同的对象,或者在值类型的情况下具有相同的值。

那么,让我们看一下以下代码:

var a = [1,2,3];
var b = [1,2,3];
var c = a;

var ab_eq = (a === b); // false (even though a and b are the same type)
var ac_eq = (a === c); // true

同样的情况也发生在这里:

var a = { x: 1, y: 2 };
var b = { x: 1, y: 2 };
var c = a;

var ab_eq = (a === b); // false (even though a and b are the same type)
var ac_eq = (a === c); // true

或者甚至:

var a = { };
var b = { };
var c = a;

var ab_eq = (a === b); // false (even though a and b are the same type)
var ac_eq = (a === c); // true

这种行为并不总是显而易见。与相等和类型相同还有更多的故事。

规则如下:

对于值类型(数字):
a === b 如果 ab 具有相同的值且类型相同,则返回 true

对于引用类型:
a === b 如果 ab 引用完全相同的对象,则返回 true

对于字符串:
a === b 如果 ab 都是字符串且包含完全相同的字符,则返回 true


字符串:特殊情况...

字符串不是值类型,但在Javascript中它们的行为类似于值类型,所以当字符串中的字符相同时且长度相同时(如第三条规则所述),它们将是“相等”的。

现在变得有趣了:

var a = "12" + "3";
var b = "123";

alert(a === b); // returns true, because strings behave like value types

不过这个怎么办呢?:

var a = new String("123");
var b = "123";

alert(a === b); // returns false !! (but they are equal and of the same type)

我以为字符串像值类型一样?好吧,这要看你问谁了... 在这种情况下,ab 不是相同的类型。a 是一个 Object 类型,而 b 是一个 string 类型。只需记住,使用 String 构造函数创建字符串对象会创建一个行为大多数情况下像字符串的 Object


288
让我补充一点建议: 如果有疑问,请阅读规范 ECMA-262是一种脚本语言的规范,而JavaScript是它的方言。当然,在实践中,最重要的浏览器如何处理比一个玄学的定义更重要。但了解为什么new String("a") !== "a"很有帮助。
请让我解释一下如何阅读规范以澄清这个问题。我看到在这个非常古老的主题中,没有人能够回答这个非常奇怪的效果。因此,如果您能够阅读规范,这将极大地帮助您的职业发展。这是一项习得的技能。所以,让我们继续。
在PDF文件中搜索===将我带到规范的第56页:11.9.4.严格等于运算符(===),经过规范的探究后,我找到了以下内容:

11.9.6严格相等比较算法
比较x === y,其中x和y是值,产生truefalse。这样的比较按如下方式执行:
  1.如果Type(x)与Type(y)不同,则返回false
  2.如果Type(x)是Undefined,则返回true
  3.如果Type(x)为Null,则返回true
  4.如果Type(x)不是Number,则转到步骤11。
  5.如果x是NaN,则返回false
  6.如果y是NaN,则返回false
  7.如果x和y是相同的数字值,则返回true
  8.如果x是+0,y是-0,则返回true
  9.如果x是-0,y是+0,则返回true
  10.返回false
  11.如果Type(x)是String,则如果x和y是完全相同的字符序列(长度相同且对应位置上的字符相同),则返回true;否则,返回false
  12.如果Type(x)是Boolean,则如果x和y都是true或都是false,则返回true;否则,返回false
  13.如果x和y引用同一个对象,或者它们引用相互连接的对象(参见13.1.2),则返回true。否则,返回false

有趣的是第11步。是的,字符串被视为值类型。但这并不能解释为什么“new String(“a”)!==“a””。我们有没有一个不符合ECMA-262标准的浏览器?
不要着急!
让我们检查操作数的类型。通过将它们包装在typeof()中自行尝试。我发现“new String(“a”)”是一个对象,并且使用了步骤1:如果类型不同,则返回false。
如果你想知道为什么“new String(“a”)”没有返回一个字符串,那就读一下规范吧!玩得开心!
Aidiakapi在下面的评论中写道:
从规范中可以看出: 11.2.2 new运算符: 如果构造函数的类型不是Object,则抛出TypeError异常。 换句话说,如果String不是Object类型,就不能与new运算符一起使用。
即使对于String构造函数,“new”始终返回一个对象。可悲的是!字符串的值语义(请参见第11步)已经丢失了。
这最终意味着:“new String(“a”)!==“a””。

112

我使用类似下面的代码在Firefox浏览器中通过Firebug进行了测试:

console.time("testEquality");
var n = 0;
while (true) {
  n++;
  if (n == 100000)
    break;
}
console.timeEnd("testEquality");

并且

console.time("testTypeEquality");
var n = 0;
while (true) {
  n++;
  if (n === 100000)
    break;
}
console.timeEnd("testTypeEquality");

我的结果(每个测试都进行了五次并进行了平均):

==: 115.2
===: 114.4

所以我会说微小的差异(请记住这是超过100000个迭代)可以被忽略。性能不是使用 === 的理由。类型安全(尽管只能在JavaScript中保证相对的安全)和代码质量才是。


6
当实际进行 == 运算符的类型强制转换时,它们是如何进行比较的呢?记住,这时会有性能提升。 - Hubert OG
3
在进行适当测试时,快速检查类型不等性的主要区别。https://jsfiddle.net/4jhuxkb2/ - Doug Morrow
我认为我们看到的时间差是因为n是一个数字,100000也是数字,你应该在字符串“1”上检查它,我假设会发生一些解析,时间差将增加。 - Avishay

109
在PHP和JavaScript中,它是严格相等运算符。这意味着它将比较类型和值。

102
在JavaScript中,它表示相同的值和类型。
例如,
4 == "4" // will return true

但是

4 === "4" // will return false 

99

为什么==是如此不可预测的?

当你将空字符串""与数字零0进行比较时,会得到什么?

true

没错,按照==的说法,空字符串和数字零是相同的。

而且问题并不止于此,以下是另一个例子:

'0' == false // true

数组会让事情变得非常奇怪。

[1] == true // true
[] == false // true
[[]] == false // true
[0] == false // true

然后与字符串更奇怪

[1,2,3] == '1,2,3' // true - REALLY?!
'\r\n\t' == 0 // true - Come on!

情况还会变得更糟:

什么时候等于不等于?

let A = ''  // empty string
let B = 0   // zero
let C = '0' // zero string

A == B // true - ok... 
B == C // true - so far so good...
A == C // **FALSE** - Plot twist!

让我再说一遍:

(A == B) && (B == C) // true
(A == C) // **FALSE**

这只是原始类型的疯狂表现。

当你使用==与对象一起使用时,它会变得更加疯狂。

此时您可能会想...

为什么会这样呢?

这是因为与“三个等号”(===)不同,==执行许多其他操作

它对函数有特殊处理,对null、undefined、字符串等也有特殊处理。

它变得非常疯狂。

实际上,如果您尝试编写一个执行==操作的函数,它看起来会像这样:

function isEqual(x, y) { // if `==` were a function
    if(typeof y === typeof x) return y === x;
    // treat null and undefined the same
    var xIsNothing = (y === undefined) || (y === null);
    var yIsNothing = (x === undefined) || (x === null);

    if(xIsNothing || yIsNothing) return (xIsNothing && yIsNothing);

    if(typeof y === "function" || typeof x === "function") {
        // if either value is a string 
        // convert the function into a string and compare
        if(typeof x === "string") {
            return x === y.toString();
        } else if(typeof y === "string") {
            return x.toString() === y;
        } 
        return false;
    }

    if(typeof x === "object") x = toPrimitive(x);
    if(typeof y === "object") y = toPrimitive(y);
    if(typeof y === typeof x) return y === x;

    // convert x and y into numbers if they are not already use the "+" trick
    if(typeof x !== "number") x = +x;
    if(typeof y !== "number") y = +y;
    // actually the real `==` is even more complicated than this, especially in ES6
    return x === y;
}

function toPrimitive(obj) {
    var value = obj.valueOf();
    if(obj !== value) return value;
    return obj.toString();
}

那么这意味着什么?

这意味着==是很复杂的。

因为它很复杂,所以当你使用它时很难知道会发生什么。

这意味着你可能会遇到错误。

所以故事的寓意是...

让你的生活不那么复杂。

使用===代替==

结尾。


92
===运算符被称为严格比较运算符,它与==运算符是不同的。
假设有两个变量a和b。
对于"a == b" 的计算结果为true,则a和b需要具有相同的值
"a === b" 的情况下,a和b必须具有相同的值,并且相同的类型才能计算结果为true。
请看以下示例。
var a = 1;
var b = "1";

if (a == b) //evaluates to true as a and b are both 1
{
    alert("a == b");
}

if (a === b) //evaluates to false as a is not the same type as b
{
    alert("a === b");
}
总之,使用==运算符可能会在您不希望的情况下评估为true,因此使用===运算符会更安全。
在90%的使用场景中,无论使用哪种运算符都没有关系,但是有一天当您遇到一些意外行为时,了解它们之间的区别很方便。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接