JavaScript 中的相等性判断研究

JavaScript 中的相等性判断研究

在 JavaScript 中,理解相等性判断的细微差别对于编写正确和高效的代码至关重要。本文将深入探讨 JavaScript 中的三种相等性判断方式,详细阐述其中的难点和易错点。

一、JavaScript 中的三种值比较运算

JavaScript 提供了三种不同的值比较运算:

  1. 严格相等(===:不进行类型转换,如果类型不同,直接返回 false
  2. 宽松相等(==:会进行类型转换,根据特定规则比较值。
  3. Object.is():不进行类型转换,也不对 NaN+0-0 进行特殊处理。

二、对应的算法

上述三个操作分别对应于 JavaScript 中的四个相等算法中的三个:

  • IsLooselyEqual:对应 ==
  • IsStrictlyEqual:对应 ===
  • SameValue:对应 Object.is()
  • SameValueZero:用于许多内置运算,例如Array.prototype.includes()

三、使用 === 进行严格相等比较

严格相等===)比较非引用类型时,如果两个值类型不同,直接返回 false;如果类型相同,值也相同,且都不是 number 类型,返回 true。对于 number 类型,如果两个值都是数值相同的非 NaN 数字,包括 +0 -0,则返回 trueNaN 与任何值(包括自身)都不全等,因此 NaN === NaN 返回 false+0 -0 被认为是相等的,因为在 JavaScript 中,+0 === -0 返回 true。对于引用类型, 如果两个对象的内容相同,内容不同也是返回false

易错点:

  • NaN 与任何值都不全等,包括自身。 因此,NaN === NaN 返回 false
  • +0-0 被认为是相等的。 在大多数情况下,这符合直觉,但在特殊情况下可能导致问题。
  • number类型需要特殊判断,其他都是判断类型和值是否相等。
  • 对象比较的是引用:和Object.is()比较对象的行为一致,如果两个对象内容相同,但是引用不同,那么会返回false。

示例:

console.log(NaN === NaN); // false
console.log(+0 === -0);   // true

注意事项:

  • 数组索引查找方法使用严格相等。 例如,Array.prototype.indexOf() 无法找到 NaN 的索引,因为 NaN !== NaN

四、使用 == 进行宽松相等比较

宽松相等==)在比较前会对操作数进行类型转换,其规则较为复杂,容易出错。

类型转换规则:

  1. nullundefinednull == undefinedundefined == undefinednull == null返回 truenull == other 都为false。
  2. 布尔值转换为数字true 转换为 1false 转换为 0
  3. 字符串与数字比较:字符串转换为数字。
  4. 对象与原始类型比较:对象转换为原始值(调用 valueOftoString 方法)。

易错点:

  • 可能导致非预期的类型转换。 例如,[] == false 返回 true,因为 [] 转换为 "",再转换为 0
  • NaN 与任何值都不相等,包括自身。 因此,NaN == NaN 返回 false
  • 引用类型的转换:不会比较引用对象的地址,而是将其转换为字符串、数字进行比较,所以再非严格条件下的相同内容的不同引用对象将会返回true

示例:

console.log([] == false);       // true
console.log('' == 0);           // true
console.log(null == undefined); // true

不推荐使用宽松相等的原因:

  • 容易导致难以察觉的错误。
  • 代码可读性差,维护困难。

五、使用 Object.is() 进行同值相等比较

Object.is()=== 类似,但有以下区别:

  • -0+0 不相等。 Object.is(+0, -0) 返回 false
  • NaN 被认为等于自身。 Object.is(NaN, NaN) 返回 true
  • 对象比较的是引用 如果两个对象内容相同,但是引用不同也会返回false
// Object.is()方法
Object.is(0, -0) // false
Object.is(NaN, NaN) // true
const obj_1 = {
    name: "Jack"
}
const obj_2 = {
    name: "Jack"
}
// 对于对象比较的是引用地址
console.log(Object.is(obj_1, obj_2)) // false\
// 对于非引用类型,就是先比较类型,再比较值
console.log(Object.is(obj_1.name, obj_2.name)) // true

使用场景:

  • 需要区分 -0+0 的情况。
  • 需要判断 NaN 是否存在。

示例:

console.log(Object.is(+0, -0)); // false
console.log(Object.is(NaN, NaN)); // true

六、SameValueZero 相等性

SameValueZero 类似于 Object.is(),但认为 +0-0 相等。这种相等性用于内部算法,如 Array.prototype.includes()MapSet 的比较。

实现方式:

function sameValueZero(x, y) {
  // x !== x && y !== y 其实就是isNaN(x) && isNaN(y)
  return x === y || (x !== x && y !== y);
}

注意事项:

  • NaN 被认为等于自身。
  • +0-0 被认为相等。
  • x !== x && y !== y 其实就是isNaN(x) && isNaN(y)
  • Array.peototype.includes()内部实现使用零值相等算法,所以可以查找NaN

七、相等性方法的比较

下表总结了不同相等性方法的比较结果:

xyx == yx === yObject.is(x, y)SameValueZero
undefinedundefined
nullnull
NaNNaN
+0-0
'0'0
false0
{}{}
[]false
nullundefined

易错点:

  • NaN 的比较在 Object.isSameValueZero 中返回 true
  • +0-0Object.is 中返回 false,在其他比较中返回 true

八、何时使用 Object.is() 而非 ===

建议:

  • 除非需要区分 +0-0,否则使用 === 更为合适。
  • 在处理不可变属性或需要精确比较的情况下,使用 Object.is()

九、特殊情况下的 -0NaN

可能引入 -0 的操作:

  • 一元负号(--0 可能由 -0-(-0) 得到。
  • 数学函数Math.atan2Math.ceilMath.powMath.round 等可能返回 -0
  • 位运算~<<>> 等可能导致 -0 消失。
  • 乘法和除法运算:0 * -1 和 0 / -1 都会得到 -0。

示例

// 一元符号演示
let negativeZero = -0
console.log(negativeZero)
let positiveZero = -(-0)
console.log(positiveZero)
console.log(Object.is(negativeZero, positiveZero)) // false

// 数学函数
let ceilRes = Math.ceil(-0.5)
console.log(ceilRes) // -0

// 乘法和除法运算 产生-0
let multiplyRes = 0 * -1
console.log(multiplyRes) // -0
let divideRes = 0 / -1
console.log(divideRes) // -0

注意事项:

  • 在涉及浮点数运算时,需要小心处理 -0
  • NaN 有多种表示形式,但在 JavaScript 中被认为是相同的。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值