ECMAScript 6 相等性


在〈弱类型的代价 – WAT!〉中谈过,JavaScript 中的相等性比较,主要有=====,为了避免自动类型转换来捣蛋,建议使用===进行严格的相等比较。

在〈与众不同的数据类型〉中谈过NaN,这是个麻烦的家伙,NaN === NaN会是false,只能使用isNaN函数来判定一个值是不是NaN,偏偏isNaN又不是那么可靠。在 ES6 中有了Number.isNaN,在〈增强的数值与字符串〉看过,现在只有NaN能让Number.isNaN结果为true了。

然而,现在必须得问的是,在谈到两个值的相等性时,到底要像===NaN视为false,还是要像Number.isNaN(或者isNaN),将NaN视为true呢?

例如,数组中如果有NaN,你知道indexOflastIndexOf找得到还是会返回 -1?在case比对时,如果case NaN,那么这会是个有效案例吗?如果一个Set包含了NaN,那么再add一个NaN,到底要接受还是不接受呢?

数组的indexOflastIndexOf采用的是===,因此试图找NaN的话,会返回falsecase也是使用===比较,因此case NaN是比对不到的:

> switch(NaN) {
...     case NaN:
...         console.log('NaN'); break;
...     default:
...         console.log('default');
... }
default
undefined
>

然而,SetMap在比较相等性时,若有两个NaN要比对,会视为相等,除此之外,行为跟===相同,在 ES6 中,称这种相等演算为 SameValueZero,以有别於单纯地使用===时的相等运算。

好吧!反正就是NaN的问题嘛!多记一种就是了,是这样的吗?来看看Object.defineProperty的行为:

> let o = {};
undefined
> Object.defineProperty(o, 'READONLY_N',
...     { value: -0, writable: false, configurable: false, enumerable: false });

{}
> Object.defineProperty(o, 'READONLY_N',
...     { value: 100, writable: false, configurable: false, enumerable: false })
;
TypeError: Cannot redefine property: READONLY_N
    at Function.defineProperty (<anonymous>)
    at repl:1:8
    at ContextifyScript.Script.runInThisContext (vm.js:50:33)
    at REPLServer.defaultEval (repl.js:240:29)
    at bound (domain.js:301:14)
    at REPLServer.runBound [as eval] (domain.js:314:12)
    at REPLServer.onLine (repl.js:441:10)
    at emitOne (events.js:121:20)
    at REPLServer.emit (events.js:211:7)
    at REPLServer.Interface._onLine (readline.js:282:10)
> Object.defineProperty(o, 'READONLY_N',
...     { value: -0, writable: false, configurable: false, enumerable: false });

{}
>

一个特性如果被Object.defineProperty定义为只读,那么就不能改变该特性的值,否则发生TypeError,然而,如果Object.defineProperty时实际上并没有变动值,像上例那样,还是指定为-0的话,并不会有TypeError,那么,将之定义为 0 呢?

> Object.defineProperty(o, 'READONLY_N',
...     { value: 0, writable: false, configurable: false, enumerable: false });
TypeError: Cannot redefine property: READONLY_N
    at Function.defineProperty (<anonymous>)
    at repl:1:8
    at ContextifyScript.Script.runInThisContext (vm.js:50:33)
    at REPLServer.defaultEval (repl.js:240:29)
    at bound (domain.js:301:14)
    at REPLServer.runBound [as eval] (domain.js:314:12)
    at REPLServer.onLine (repl.js:441:10)
    at emitOne (events.js:121:20)
    at REPLServer.emit (events.js:211:7)
    at REPLServer.Interface._onLine (readline.js:282:10)
>

惊喜!在这种情况下,0 与 -0 被视为不相等的喔!SameValueZero 演算将 0 与 -0 视为相等,而将 0 与 -0 视为不相等的演算,称为 SameValue,这其实在 ES5 时就规范了,Object.is采用的就是 SameValue 演算,因此Object.is(0, -0)的结果会是false

> Object.is(0, -0);
false
>

SameValue 演算会将NaN视为相等,然而 0 与 -0 视为不相等,而 ES6 的 SameValueZero 会将NaN视为相等,0 与 -0也视为相等。

现在,JavaScript 有四种相等性比较耶!好欢乐 !@#$%^…XD

基本上,大多数情况,还是使用===就好了,然而,如果要面对NaN0-0的时候,最好是搞清楚自己打算使用哪种相等性,而使用 API 时,也查清楚它会使用哪种相等性。


展开阅读全文