详解一下 javascript 中的比较

justjavac
  • 47.5k

第一次提问。本来想写篇文章,不过在写之前想听听大神们的分析,所以来此提问一下,抛砖引玉。

代码1:

[] == [];
[] === [];
{} == {};
{} === {};

代码2:

var n0 = 123;
var n1 = new Number(123);
var n2 = new Number(123);
var n3 = Number(123);
var n4 = Number(123);

n0 == n1;
n0 == n3;
n0 === n1;
n0 === n3

n1 == n2;
n1 === n2
n1 == n3;
n1 === n3;

n3 == n4;
n3 === n4;

问:比较结果是什么?以及为什么会是这个结果?

回复
阅读 15.8k
8 个回答
✓ 已被采纳

http://www.ecma-international.org/ecma-262/5.1/#sec-11.9.3

11.9.3 抽象相等比较算法

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

  1. Type(x)Type(y) 相同, 则

    1. Type(x)Undefined, 返回 true
    2. Type(x)Null, 返回 true
    3. Type(x)Number, 则

      1. xNaN, 返回 false
      2. yNaN, 返回 false
      3. xy 为相等数值, 返回 true
      4. x+0y−0, 返回 true
      5. x−0y+0, 返回 true
      6. 返回 false
    4. Type(x)String, 则当 xy 为完全相同的字符序列(长度相等且相同字符在相同位置)时返回 true。 否则, 返回 false
    5. Type(x)Boolean, 当 xy 为同为 true 或者同为 false 时返回 true。 否则, 返回 false
    6. xy 为引用同一对象时返回 true。否则,返回 false
  2. xnullyundefined, 返回 true
  3. xundefinedynull, 返回 true
  4. Type(x)NumberType(y)String,返回比较 x == ToNumber(y) 的结果。
  5. Type(x)StringType(y)Number,返回比较 ToNumber(x) == y 的结果。
  6. Type(x)Boolean, 返回比较 ToNumber(x) == y 的结果。
  7. Type(y)Boolean, 返回比较 x == ToNumber(y) 的结果。
  8. Type(x)StringNumber,且 Type(y)Object,返回比较 x == ToPrimitive(y) 的结果。
  9. Type(x)ObjectType(y)StringNumber, 返回比较 ToPrimitive(x) == y 的结果。
  10. 返回 false

注:按以上相等之定义:

  • 字符串比较可以按这种方式强制执行: "" + a == "" + b
  • 数值比较可以按这种方式强制执行: +a == +b
  • 布尔值比较可以按这种方式强制执行: !a == !b

注:等值比较操作保证以下不变:

  • A != B 等价于 !(A==B)
  • A == B 等价于 B == A,除了 A 与 B 的执行顺序。

注:相等运算符不总是传递的。 例如,两个不同的 String 对象,都表示相同的字符串值;== 运算符认为每个 String 对象都与字符串值相等,但是两个字符串对象互不相等。例如:

  • new String("a") == "a""a" == new String("a") 皆为 true
  • new String("a") == new String("a")false

字符串比较使用的方式是简单地检测字符编码单元序列是否相同。不会做更复杂的、基于语义的字符或者字符串相等的定义以及 Unicode 规范中定义的 collating order。所以 Unicode 标准中认为相等的 String 值可能被检测为不等。实际上这一算法认为两个字符串已经是经过规范化的形式。

11.9.6 严格等于比较算法

比较 x===yxy 为值,需要产出 truefalse。比较过程如下:

  1. 如果 Type(x)Type(y) 的结果不一致,返回 false,否则
  2. 如果 Type(x) 结果为 Undefined,返回 true
  3. 如果 Type(x) 结果为 Null,返回 true
  4. 如果 Type(x) 结果为 Number,则

    1. 如果 xNaN,返回 false
    2. 如果 yNaN,返回 false
    3. 如果 xy 为同一个数字,返回 true
    4. 如果 x+0y-0,返回 true
    5. 如果 x-0y+0,返回 true
    6. 返回 false
  5. 如果 Type(x) 结果为 String,如果 xy 为完全相同的字符序列(相同的长度和相同的字符对应相同的位置),返回 true,否则,返回 false
  6. 如果 Type(x) 结果为 Boolean,如果 xy 都为 truefalse,则返回 true,否则,返回 false
  7. 如果 xy 引用到同一个 Object 对象,返回 true,否则,返回 false

注:此算法与 SameValue 算法在对待有符号的零和 NaN 上表现不同。

泻药。

个人认为,此类题目的用处只有一个:熟悉规范

所以,请各位参考 http://www.ecma-international.org/ecma-262/5.1/#sec-11.9.3, 并在console中自行测试运行结果与规范是否一致即可。

举个简单的例子:

console.log( [] == !{} ); //why true?

!{}是个布尔值,为false,因此我们比较的是 [] == 0;(11.9.3-7)

Number([])是0,因此我们比较的是 0 == 0; (11.9.3-1.c.iii)

@justjavac: 解释一下 n1 == n2 与 n3 == n4 的值。

其实我们需要解决三个问题:

  • (1)new Number(123) == Number(123); //true

Number函数要么返回一个数字,要么返回Not A Number(如果参数没有办法被转化为数字),在这三个例子中,我们不必担心NaN的问题,所以Number(123)等价于数字123。

于是(1)变成 new Number(123) == 123,进入规范 11.9.3-8

11.9.3-8提示我们去找ToPrimitive(Object(123))是什么,于是我们到了8.12.8--default value,一看,哎呦我去,正好有个valueof能用,于是(1)变成了 123 == 123;,结束。

  • (2)new Number(123) == new Number(123); //false

比较分支进入 11.9.3-1.f,就是看着两个Object的引用是不是相等的,这里明显是不等的。

  • (3)Number(123) == Number(123); //true

太简单了,omitted。

下面是思考题时间:

(1)'foo' == new function(){ return String('foo'); };

// false, why?

(2)'foo' == new function(){ return new String('foo'); };

// true, why?

对规范不理解,我解释一下代码1,

{} == {};

这个会产生一个解析错误:

SyntaxError: Unexpected token ==

其实,{} 在这里并不是一个空对象,解析器把它当作了一个“代码块”,因此出现了解析错误,如果把它解析为空对象,需要添加小括号。

({}) == ({});
  • 代码1:对象比较。
  • 代码2:隐式类型转换。

没人解释第一段代码吗?(明天我解释。)

严格相等比较运算符 === 只在变量类型相等时,才继续比较值是否相等。否则,直接返回false.

宽容相等比较运算符 == 在遇到不同变量类型比较时,先试图进行变量类型转换, 之后再进行严格相等比较.

由于宽容相等比较运算符,在自动进行变量类型转换时的步骤非常含混和难记, 所以运算结果经常出人意料。

对于{} === {}或者[] === [], 我觉得等同于下面这段代码:
var x = {}, y = {}; 或者 var x = Object(), y = Object();

> x === y
false
> x === x
true

就是说,x === y 仅当 xy 指向同一个object时为true. 想真正判断object或者array相等,只有遍历元素,然后判断是否每个都相等。

以上观点有不对的地方请大家指正~~

另,Douglas Crockford编写的jslint,在检测代码格式时,默认状态下禁止使用宽容相等比较运算符。 并把 == 归类为javascript中魔鬼运算符之一。

看了这个问题,又看了提问的人,忍不住想写两句:

a = function(){};
b = function(){};

a == b;
a === b;

希望这个图能更直观说明
clipboard.png

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
宣传栏