Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

重学js —— 比较 #56

Open
lizhongzhen11 opened this issue Nov 16, 2019 · 0 comments
Open

重学js —— 比较 #56

lizhongzhen11 opened this issue Nov 16, 2019 · 0 comments
Labels
js基础 Good for newcomers 重学js 重学js系列 规范+MDN

Comments

@lizhongzhen11
Copy link
Owner

lizhongzhen11 commented Nov 16, 2019

比较

先看代码:

1 > 0 // true
NaN > 0 // false
NaN === NaN // false 
Infinity === Infinity // true
0n === 0 // false
0n == 0 // true
true > false // true
'A' > 'B' // false
'A' > 'a' // false
'a' > 'A' // true
null == undefined // true
null === undefined // false
{} < {} // false
{} > {} // false
{} == {} //false
{} === {} //fasle
Symbol() > 0 // 报错
Symbol() == Symbol() // false
Symbol() === Symbol() // false
Object.is(+0, -0) // false
Object.is(NaN, NaN) // true
new String('a') == new String('a') // false

上面的代码,我能说出所有的正确打印结果吗?如果能的话,我能说出所有的比较原理吗?

对不起,我都做不到。比如对象之间比较以及 SymbolNumber 的比较,我不敢说出结果,因为我没手动测试过,更对其原理一无所知,所以我遇到这些完全没底气!

其他的方法可以不看,主要是以下的与比较有直接关联的方法:

IsStringPrefix ( p, q )

判断字符串 p 是不是字符串 q 的前缀。

  • pq 都必须是字符串
  • 如果 q 可以是 p 和其他字符串 r 的串联,返回 true,否则返回 false
  • 注意:任何字符串都是其自身的前缀,r 可能是空字符串

SameValue ( x, y )

  • 判断类型是不是一样,不是返回 fasle
  • 判断 x 是不是 NumberBigInt,然后对 xy 执行 sameValue (BigInt::sameValue ( x, y )Number::sameValue ( x, y ))操作比较
  • 不符合以上条件,直接返回调用 SameValueNonNumeric(x, y) 后的值

Object.is 采用该算法,与严格相等不同的是对NaN以及+0, -0的判断。它认为NaN是相等的,但同时认为+0和-0不等。严格相等使用equal算法!!!

SameValueZero ( x, y )

其实本质和 SameValue 区别不大,只是它只比较 +0-0

SameValueNonNumeric ( x, y )

最终会返回 truefalse

  • 要求 xy 不能是NumberBigInt
  • 要求 xy 类型一致
  • 如果 xundefined,返回 true
  • 如果 xnull,返回 true
  • 如果 xString
    • 如果 xy 是完全相同的代码单元序列(在相应的索引处具有相同的长度和相同的代码单位),返回 true,否则返回 false
  • 如果 xBoolean
    • 如果 xy 都是 true 或者都是 false,则返回 true,否则返回 false
  • 如果 xSymbol
    • 如果 xy 是相同的 Symbol ,返回 true,否则返回 false
  • 如果 xy 是相同的对象 ,返回 true,否则返回 false

抽象关系比较

比较 x < y(其中 xy 是值)会产生 truefalseundefined(这表明至少一个操作数是 NaN)。除x和y外,该算法还使用名为 LeftFirst 的布尔标志作为参数。该标志用于控制对 xy 执行具有潜在可见副作用的操作的顺序。LeftFirst 默认为 true

  • 如果 LeftFirsttrue
    • 定义过程变量 px,执行ToPrimitive(x, hint Number)(如果是 Object,默认先执行 valueOf(),不满足再执行 toString()
    • 定义过程变量 py,执行ToPrimitive(y, hint Number)(如果是 Object,默认先执行 valueOf(),不满足再执行 toString()
  • 否则,
    • 注意:需要颠倒顺序以保持从左到右运算
    • 定义过程变量 py,执行ToPrimitive(y, hint Number)(如果是 Object,默认先执行 valueOf(),不满足再执行 toString()
    • 定义过程变量 px,执行ToPrimitive(x, hint Number)(如果是 Object,默认先执行 valueOf(),不满足再执行 toString()
  • 如果 pxpy 都是 String
    • 执行IsStringPrefix(py, px),如果结果为 true,则返回 faslepx包含py
    • 执行IsStringPrefix(px, py),如果结果为 true,则返回 truepy包含px
    • 如果 pxpy 不具有 前缀(必须是前缀) 包含关系,则定义过程变量 k 为最小非负整数,以便 px 内索引 k 处的码点与 py 内索引 k 处的码点不同。(必须有一个 k,因为两个字符串都不是另一个的前缀)
    • 定义过程变量 m 为整数,该整数是 px 以内的索引 k 处的码点的数值
    • 定义过程变量 n 为整数,该整数是 py 以内的索引 k 处的码点的数值
    • 如果 m < n,返回 true,否则返回 false这里主要比较的是字符串在Unicode码表中的码点数值
  • 否则,
    • 如果 pxBigIntpyString
    • 如果 pyBigIntpxString
    • 定义过程变量 nx,执行ToNumeric(px) 将结果赋值给 nxpxpy 此时已经都是primitive(原始,这里应该都是 数值)值了,顺序不重要)
    • 定义过程变量 ny,执行ToNumeric(py) 将结果赋值给 ny
    • 如果 nxny 类型一致,执行对应类型的 lessThan(nx, ny)Number::lessThan (nx, py)BigInt::lessThan(nx, py))方法
    • 如果 nxBigInt 类型并且 nyNumber 类型,或者两者类型对调
    • 如果 nxnyNaN,返回 undefined
    • 如果 nx-Infinityny+Infinity,返回 true
    • 如果 nx+Infinityny-Infinity,返回 false
    • 如果 nx 的数学值比 ny 的数学值小,返回 true,否则返回 false

字符串比较,先比较是不是前缀包含。何为前缀包含?例如abca 可以认为是前缀包含,但是 abcb 则不是,因为它需要 a + b + cb 不是 abc 的前缀。

字符串的比较,如果两字符串不具有包含关系,则 从两个字符串的第一个下标开始,比较同一下标对应的字符在Unicode码表中的码点数值大小。一旦某个下标所对应的字符分出了大小,则比较结束

注意:NullUndefiend 以及 Boolean 类型比较会在执行ToNumeric(px)时转换成数值。

抽象相等 ==

日常使用的 ==

  • 如果 xy 的类型一致,则返回 x === y 的结果。(即类型一致,默认采用全等比较
  • 如果 xnull 同时 yundefined,返回 true
  • 如果 xundefined 同时 ynull,返回 true
  • 如果 xNumber 类型,yString 类型,将 y 转为 Number 然后继续比较
  • 如果 xString 类型,yNumber 类型,将 x 转为 Number 然后继续比较
  • 如果 xBigInt 类型,yString 类型,
    • 定义过程变量 n,执行StringToBigInt(y)将结果赋值给 n
    • 如果 nNaN,返回 false
    • 否则,返回 x == n 的结果
  • 如果 xString 类型,yBigInt 类型,返回 y == x 的结果(其实就是位置对调然后重复上一步)
  • 如果 xBoolean 类型,将 x 转为 Number 类型,然后比较转为 Number 类型后的值与 y 比较结果
  • 如果 yBoolean 类型,将 y 转为 Number 类型,然后比较转为 Number 类型后的值与 x 比较结果
  • 如果 xStringNumberBigIntSymbol 类型,yObject 类型,先将 y 转为原始类型,然后再比较(执行 ToPrimitive(y)
  • 如果 xObject 类型,yStringNumberBigIntSymbol 类型,先将 x 转为原始类型,然后再比较(执行 ToPrimitive(x)
  • 如果 xBigInt 类型,yNumber 类型,或者 xNumber 类型,yBigInt 类型
    • 如果 xy 有一个是 NaN+Infinity-Infinity,返回 false(事实上,BigInt 不支持这三个值)
    • 如果 x 的数学值和 y 的数学值相等,返回 true,否则返回 false
  • 以上都不满足,返回 false

注意:StringNumberBooleanNumber 比较,最终都会转为 Number 类型进行比较!

严格相等 ===

就是日常使用的 === 全等。

注意,这里使用的是equal算法,而Object.is使用的是sameValue算法

回归开头代码

首先是字符串的比较,我知道了它会先判断是不是前缀,否则直接开始比较相同下标对应的字符在Unicode中的码点大小:

'A' > 'B' // false
'A' > 'a' // false
'a' > 'A' // true

接着是全等,这个其实不难,既要判断类型又要判断值是不是都一样:

0n === 0 // false
null === undefined // false
NaN === NaN // false 
Infinity === Infinity // true
{} === {} //fasle
Symbol() === Symbol() // false

容易困惑的在于NaNSymbol

  1. 首先 NaN 属于 Number 类型,适用于Number::equal ( x, y ),不论谁是 NaN,都返回 false!!!
  2. 其次是 Object,它最终比较的是地址内存值
  3. 还有 Symbol,有点 Object 的意思。

接下来是普通 ==,它只要求值相等不强调类型:

0n == 0 // true
null == undefined // true
{} == {} //false
Symbol() == Symbol() // false

最后,则是其它的大小比较了:

true > false // true
NaN > 0 // false
{} < {} // false
{} > {} // false
Symbol() > 0 // 报错
  1. Boolean 会被转为 Number 来比较
  2. NaN > 0 会执行Number::lessThan (nx, py),有NaN,根据规范应该返回undefined,不过浏览器把它实现成了 false!!!
  3. Symbol() > 0 会尝试把 Symbol 转为 Number,可是根据类型转换规则,会报类型错误
  4. {} < {}{} > {} 都会尝试转为原始值在进行比较,可惜,它们最终都会转为 "[object Object]",两个最终字符串相同,所以只能是 false!!!

额外补充Object.is

如果仔细看内容的话就知道为什么了。根本在于 Object.is 内部使用的是 ::sameValue ( x, y ) 算法,而严格相等使用的是 ::equal ( x, y ) 算法!

所以两者在 NaN 以及 +0-0 的比较上不同

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
js基础 Good for newcomers 重学js 重学js系列 规范+MDN
Projects
None yet
Development

No branches or pull requests

1 participant