JS中的关联比较与相称比较运算

在JS中的关联比较(Relational Comparison)运算,指的是像x < y这类大小值的关联比较。

而相称比较,可区分为规范相称(standard equality)比较x == y与严厉相称(strict equality)比较x === y两大品种。严厉相称比较会比较左侧与右侧运算元的数据范例,值相称比较则只看值,简朴的来讲是如许诠释没错。

ToPrimitive运算的细致申明可参考: JS中的{} + {}与{} + []的效果是什么?

不过,这两种比较实际上依内部设想来讲,并不是那末简朴。固然,在平常的运用状况是不须要考量那末多,本文的申明会触及很多JS内部设想的部分,关于这两种比较来作比较完全的明白,重要的参考数据是ECMAScript的规范文件。

严厉相称比较(严厉相称比较演算)

严厉相称比较的演算划定规矩先明白,重假如由于在规范相称比较(只比较值不比较数据范例)时,它在演算时的某些状况下会跳到严厉相称比较的划定规矩来。

严厉相称比较的演算划定规矩很轻易明白,根据以下的步骤举行比较,出自ecma-262 11.9.6:

以下假设为比较 x === y的状况,Type(x)指的是x的数据范例,Type(y)指的是y的范例,终究返回值只需true或false,会根据下面的步骤举行比较,假如有返回时就住手以后的步骤:

注: Type(x)在ECMAScript的规范中指的并不是用typeof返回出来的效果,而是规范内部给定的种种数据范例,共有Undefined, Null, Boolean, String, Number 与 Object。比方typeof null的效果是”object”,但ECMAScript会以为Null是个自力的数据范例。

  1. Type(x)与Type(y)差别,返回false

  2. Type(x)是Undefined,返回true(固然此时Type(y)也是Undefined)

  3. Type(x)是Null,返回true(固然此时Type(y)也是Null)

  4. Type(x)是Number时

    • (a.) x是NaN,返回false

    • (b.) y是NaN,返回false

    • (c.) x与y是一样的数字,返回true

    • (d.) x是+0,y是-0,返回true

    • (e.) x是-0,y是+0,返回true

    • (f.) 其他状况,返回false

  5. Type(x)是String时,只需当x中的字符递次与y中完全雷同时(长度雷同,字符所在位置也雷同),返回true。其他状况就返回false。

  6. Type(x)是Boolean时,只需当x与y是同时为true或同时为false时,返回true。别的状况返回false。

  7. 只需当x与y同时参照到统一对象时,返回true。别的状况返回false。

备注: 这个演算与the SameValue Algorithm (9.12)差别之处在于,关于有号的0与NaN处理体式格局差别。

注: 同值演算(the SameValue Algorithm)是规范中的另一个内部演算法,只会用在很迥殊的处所,能够先略过不看。

从上述的严厉相称比较中,能够很清晰的看到数字、字符串、布尔与null、undefined或对象是怎样比较的。

规范相称比较(笼统相称比较演算)

规范相称比较的演算划定规矩根据以下的步骤举行比较,出自ecma-262 11.9.3:

以下假设为比较 x == y的状况,Type(x)指的是x的数据范例,Type(y)指的是y的范例,终究返回值只需true或false,会根据下面的步骤举行比较,假如有返回时就住手以后的步骤:

  1. Type(x)与Type(y)雷同时,举行严厉相称比较

  2. x是undefined,而y是null时,返回true

  3. x是null,而y是undefined时,返回true

  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)是Number或String个中一种,而Type(y)是个Object时,举行x == ToPrimitive(y)比较

  9. Type(x)是个Object,而Type(y)是Number或String个中一种时,举行ToPrimitive(x) == y比较

  10. 其他状况,返回false

备注1: 以下的是三种强迫转换的规范比较状况:

  • 字符串比较: “” + a == “” + b.

  • 数字比较: +a == +b.

  • 布尔比较: !a == !b

备注2: 规范相称比较有以下的稳定式(invariants):

  • A != B 相称于 !(A == B)

  • A == B 相称于 B == A

备注3: 相称比较运算不一定老是能够改变(transitive),比方:

  • new String(“a”) == “a” 与 “a” == new String(“a”) 的效果都是true

  • new String(“a”) == new String(“a”) 效果是false.

备注4: 字符串比较运用的是简朴的字符测试。并不是运用庞杂的、语义导向的字符定义或是Unicode所定义的字符串相称或校正递次。

注: 上述的ToNumber与ToPrimitive都是规范内部运算时运用的要领,并不是让开发者运用的。

由规范相称比较的演算得知,它的运算是以”数字为最优先”,任何别的的范比方果与数字作相称比较,一定要先强迫转为数字再比较。但这是一个相称具有隐蔽作用的运算,在平常实作时,会很轻易形成误会,比方以下的例子:

> 0 == []
true

> '' == []
true

上面这是由于空数组[],举行ToPrimitive运算后,获得的是空字符串,所以作值相称比较,相称于空字符串在举行比较。

> '[object Object]' == {}
true

> NaN == {}
false

上面的空对象字面量,举行ToPrimitive运算后,获得的是'[object Object]'字符串,这个值会假如与数字范例的NaN比较,会跳到同范例相称的严厉相称比较中,NaN不论与任何数字作相称比较,一定是返回false。

> 1 == new Number(1)
true

> 1 === new Number(1)
false

> 1 === Number(1)
true

上面申清楚明了,包装对象在JS中的内部设想中,规范的值相称比较是雷同的,但严厉相称比较是差别的值,包装对象仍然是个对象,只是内里的valueOf要领是返回这个对象内里带的原始数据范例值,经由ToPrimitive要领运算后,会返回原始数据的值。Number()函数挪用只是转数字范例用的函数,这个用法常常会与包装对象的用法混在一同。

这个小节的结论是,在JS中没有必要的状况下,运用严厉的相称比较为最好的值相称比较体式格局,规范的相称轻易发生不经意的副作用,有的时刻你可能会获得不预期的效果。

关联比较(笼统关联比较演算)

关联比较的演算划定规矩重假如根据以下的步骤举行比较,出自ecma-262 11.8.5:

以下假设为比较 x < y的状况,由于在规范中的笼统关联比较演算的申明比较庞杂,有触及布尔标记的以左方优先或右方优先,而且终究返回值有true、false与undefined,实际上终究不会有undefined值涌现,即是获得false罢了,以下为只斟酌左方优先(LeftFirst)的简化过的步骤。会根据下面的步骤举行比较,假如有返回时就住手以后的步骤:

  • (1. & 2.) x经由ToPrimitive(x, hint Number)运算为px值,y经由ToPrimitive(y, hint Number)运算为py值

  • (3.) 假如Type(px)与Type(py)差别时为String时

    • (a.b.) px作ToNumber(px)运算,获得nx值,与py作ToNumber(py)值,获得ny值

    • (c.d.) nx或ny中有其一为NaN时,返回undefined

    • (e.) nx与ny是一样的Number值,返回false

    • (f.) nx是+0,而且ny是−0,返回false

    • (g.) nx是−0,而且ny是+0,返回false.

    • (h.) nx是+∞,返回false

    • (i.) ny是+∞,返回true

    • (j.) ny是−∞,返回false

    • (k.) nx是−∞,返回true

    • (l.) 假如在数学上的值,nx小于ny,而且nx与ny是有限值(finite),而且差别时为0时,返回true。不然返回false。

  • (4.) 假如Type(px)与Type(py)同时为String时

    • (a.) 假如py是px的前缀(prefix)时,返回false (前缀代表px字符串中是由py字符串构成的,py只是px的子字符串的状况)

    • (b.) 假如px是py的前缀(prefix)时,返回true

    • (c.d.e.f) 以字符串中的按递次的字符,用字符的编码整数的大小来比较。k是可获得的一个最小非负整数,在px与py中的k位置有差别的字符(从左侧算过来)。在px中某个位置k的字符编码整数为m,在py某个位置k的字符编辑为n,假如m < n,则返回true,不然返回false

备注2: 字符串比较运用的是简朴的辞书递次测试。并不是运用庞杂的、语义导向的字符定义或是Unicode所定义的字符串相称或校正递次。

注: +∞相称于全局属性InfinityNumber.POSITIVE_INFINITY−∞相称于全局属性-InfinityNumber.NEGATIVE_INFINITY

关联比较基本上要区分为数字范例与字符串范例,但依旧是以”数字”为最优先的比较,只需有其他范例与数字比拟较,一定会先被强迫转换为数字。但在这之前,须要先用ToPrimitive而且是hint为数字来转换为原始数据范例。

以下为一些与对象、数组、Date对象的关联比较例子:

> 1 < (new Date())
true

> 1 > (new Date())
false

> [] < 1
true

> [] > 1
false

> ({}) < 1
false

> ({}) > 1
false

虽然在规范中的笼统关联比较演算中,有存在一种返回值undefined,但在实在的状况并没有这类返回值,相称不论怎么比较都是获得false的值。上面的例子中,空对象({})的ToPrimitive运算得出的是'[object Object]'字符串值,经由ToNumber运算会获得NaN数字范例的值,这个值不论与数字1作大于小于的关联运算,都是false。

Date()对象由于ToPrimitive运算的hint为数字,所以也是会作转换为数字范例的值为优先(也就是挪用valueOf为优先),所以并不是一般状况的以输出字符串为优先(也就是挪用toString要领为优先)的预设状况。

以下为一些字符串关联比较的例子:

> 'a' > ''
true

> 'a' < ''
false

> 'a' > []
true

> 'a' < []
false

> 'a' > ({})
true

> 'a' < ({})
false

字符串与空字符串比拟,都是套用前缀(prefix)的划定规矩步骤,由于空字符串算是一切字符串的前缀(构成的子字符串之一),所以一定地一切有值的字符串值一定是大于空字符串。

空数组经由ToPrimitive运算出来的是空字符串,所以与空字符串比拟较的效果雷同。

空对象经由ToPrimitive运算出来的是'[object Object]'字符串值,以'a'.charCodeAt(0)计算出的值是字符编码是97数字,而'['.charCodeAt(0)则是91数字,所以'a' > ({})会是获得true。

假如最先混用数字与字符串比较,多是有圈套的比较例子:

> '11' > '3'
false

> '11' > 3
true

> 'one' < 3
false

> 'one' > 3
false

’11’与’3’比拟较,实在都是字符串比较,要遵照可比较的字符位置来比较,也就是’1’与’3’字符的比较,它们的字符编码数字分别是49与51,所以'1' < '3',这里的运算的效果一定是返回false。

’11’与3数字比较,是会强迫都转为数字来比较,’11’会转为11数字值,所以大于3。

‘one’这个字符串转为数字后,是NaN这个数字值,NaN与任何数字比较,既不大于也不小于,不论作大于或小于,都是返回false。(实际上在规范中它这类返回值叫undefined)

字符串与数字以外其他的原始数据范例的比较,只需记得准绳就是强迫转为数字来比较就是了,以下为例子:

> true > null
true

> false > undefined
false

简朴地申明在ToNumber运算时,这些其他的原始数据范例值的转换效果以下:

  • Undefined -> NaN

  • Null -> +0

  • Boolean -> (true -> 1, false -> 0)

注: JS以为+0与-0是完全雷同的值,在严厉相称比较中是相称的。

注: 字符串比较实际上是拆为字符在辞书表中的编辑整数值来比较,关于非英语系的言语,JS别的有供应String.prototype.localeCompare的要领来举行部分言语的比较事情。

总结

本章延长了之前的加法运算文章中的ToPrimitive运算说明注解的部分,较为细致的来研讨JS中的相称比较(包括规范的与严厉的)与关联比较的部分。至于没提到的,不相称(==)与严厉不相称(!==),或是大于即是(>=)或小于即是(<=)只是这些演算计划的再组合效果罢了。

规范的值相称比较(==),是一种有不经意的副作用的运算,不论怎样,开发者一定要只管防止,比较前能够自行转换范例的体式格局,再作严厉的相称比较,本章也有申明为什么要防止运用它的来由。

    原文作者:eyesofkids
    原文地址: https://segmentfault.com/a/1190000008038751
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞