We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
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
首先要清楚 JavaScript 中的相等分为宽松相等(==)和严格相等(===)。宽松相等在比较值的时候会先进行类型的隐式转换,严格相等下如果比较值的类型不一致,那么就判定比较值不全等。如果比较值是引用类型,宽松相等和严格相等就不能直接判断出值是否相等了(引用类型浅拷贝比较值除外,也就是比较值指向的是同一引用地址),原因是对于任意两个不同的非原始对象,即便他们有相同的结构,都会计算得到 false 。
var num = 1; var str = '1'; console.log(num == str); // true console.log(num === str); // false var obj1 = {name: '白展堂'}; var obj2 = {name: '白展堂'}; var obj3 = obj1; console.log(obj1 == obj2); // false console.log(obj1 === obj2); // false console.log(obj1 == obj3); // true console.log(obj1 === obj3); // true var arr1 = [1]; var arr2 = [1]; console.log(arr1 == arr2); // false console.log(arr1 === arr2); // false
如何判断对象是否相等?
一种解决方案就是使用 JSON.stringify 序列化成字符串再做比较。
var obj1 = {name: '白展堂', age: 25}; var obj2 = {name: '白展堂', age: 25}; JSON.stringify(obj1) === JSON.stringify(obj2); // true var arr1 = ['a', 'b', 'c', 'd']; var arr2 = ['a', 'b', 'c', 'd']; JSON.stringify(arr1) === JSON.stringify(arr2); // true
这种方案看似可以判断出对象是否相等,但是会不会存在问题呢?看过 underscore 源码的都知道,isEqual 函数的实现有多复杂,很多种情况显然不是通过 JSON.stringify 序列化就能解决的。
先来分析下 JSON.stringify 方案存在的问题,假设比较对象中的属性值存在 RegExp 对象,判定结果是怎样的呢?
function eq(a, b) { return JSON.stringify(a) === JSON.stringify(b); } var obj1 = {name: '白展堂', reg: /test1/i}; var obj2 = {name: '白展堂', reg: /test2/i}; eq(obj1, obj2); // true
结果为 true,也就是说 obj1 和 obj2 序列化的字符串是一致的。
var obj1 = {name: '白展堂', reg: /test1/i}; var obj2 = {name: '白展堂', reg: /test2/i}; JSON.stringify(obj1); // "{"name":"白展堂","reg":{}}" JSON.stringify(obj2); // "{"name":"白展堂","reg":{}}"
可以看到,JSON.stringify 将 RegExp 对象序列化成了 '{}',也就是说 JSON.stringify 序列化对于某些情况会存在问题,比如 undefined 和 Function 函数在序列化过程中会被忽略。
function test() {} JSON.stringify(undefined) === JSON.stringify(test); // true
那么如何完美的判断对象或值相等?现在来看看 underscore 中的 isEqual 函数是如何针对不同的比较值进行处理的。
ECMAScript 中,认为 0 与 -0 是相等的,其实不然。
1 / 0 // Infinity 1 / -0 // -Infinity 1 / 0 === 1 / -0 // false
原因是因为 JavaScript 中的 Number 是64位双精度浮点数,采用了IEEE_754 浮点数表示法,这是一种二进制表示法,按照这个标准,最高位是符号位(0 代表正,1 代表负),剩下的用于表示大小。而对于零这个边界值 ,1000(-0) 和 0000(0)都是表示 0 ,这才有了正负零的区别。
那么如何区分 0 与 -0?
function eq(a, b) { // 比较值a,b相等且值不是0和-0的情况 if(a === b) { return a !== 0 || 1 / a === 1 / b; } return false; } eq(0, 0); // true eq(0, -0); // false
判断某个值是否为 NaN 时不能直接比较这个值是否等于 NaN,因为 ECMAScript 中 NaN 不等于自身,可以使用原生函数 Number.isNaN() 或 isNaN()。
var a = NaN; a === NaN; // false isNaN(a); // true
那么自己如何实现判断 NaN 值的方法?利用 NaN 不等于自身的原理。
function eq(a, b) { if(a !== a) return b !== b; } eq(NaN, NaN); //true eq(NaN, 'test'); // false
对于 RegExp,String,Number,Boolean 等类型的值,假设一个比较值是字面量赋值,另一个比较值的通过构造函数生成的,ECMAScript 会认为两个值并不相等。
var s1 = 'test'; var s2 = new String('test'); console.log(s1 === s2); // false typeof s1; // 'string' typeof s2; // 'object' var n1 = 100; var n2 = new Number(100); console.log(n1 === n2); // false typeof n1; // 'number' typeof n2; // 'object'
原因是因为字面量赋值的变量和构造函数生成的变量之间的类型不同,前面说过,严格相等下不同类型的值是不全等的,那么如何处理这种情况?答案是对比较值进行隐式转换。
对于 toString() 是 Array 和 Object 类型的比较值,则循环遍历里面的元素或属性进行比较,只有length 属性值相等且里面的元素或属性都相等的情况下,就说明两个比较值是相等的了。存在一种情况就是比较值里的元素或者属性值是一个嵌套的对象,这就需要使用递归遍历。
PS: underscore 源码中的 _.isEqual 源码注释地址: 源码注释。
The text was updated successfully, but these errors were encountered:
用心了,讲的很细致!
Sorry, something went wrong.
隐式类型转换第二段,有几个错别字😁
@YoungLightMing 多谢指出,已更正
No branches or pull requests
首先要清楚 JavaScript 中的相等分为宽松相等(==)和严格相等(===)。宽松相等在比较值的时候会先进行类型的隐式转换,严格相等下如果比较值的类型不一致,那么就判定比较值不全等。如果比较值是引用类型,宽松相等和严格相等就不能直接判断出值是否相等了(引用类型浅拷贝比较值除外,也就是比较值指向的是同一引用地址),原因是对于任意两个不同的非原始对象,即便他们有相同的结构,都会计算得到 false 。
JSON.stringify
如何判断对象是否相等?
一种解决方案就是使用 JSON.stringify 序列化成字符串再做比较。
这种方案看似可以判断出对象是否相等,但是会不会存在问题呢?看过 underscore 源码的都知道,isEqual 函数的实现有多复杂,很多种情况显然不是通过 JSON.stringify 序列化就能解决的。
先来分析下 JSON.stringify 方案存在的问题,假设比较对象中的属性值存在 RegExp 对象,判定结果是怎样的呢?
结果为 true,也就是说 obj1 和 obj2 序列化的字符串是一致的。
可以看到,JSON.stringify 将 RegExp 对象序列化成了 '{}',也就是说 JSON.stringify 序列化对于某些情况会存在问题,比如 undefined 和 Function 函数在序列化过程中会被忽略。
_.isEqual
那么如何完美的判断对象或值相等?现在来看看 underscore 中的 isEqual 函数是如何针对不同的比较值进行处理的。
区分 +0 与 -0 之间的差异
ECMAScript 中,认为 0 与 -0 是相等的,其实不然。
原因是因为 JavaScript 中的 Number 是64位双精度浮点数,采用了IEEE_754 浮点数表示法,这是一种二进制表示法,按照这个标准,最高位是符号位(0 代表正,1 代表负),剩下的用于表示大小。而对于零这个边界值 ,1000(-0) 和 0000(0)都是表示 0 ,这才有了正负零的区别。
那么如何区分 0 与 -0?
判断值是否为 NaN
判断某个值是否为 NaN 时不能直接比较这个值是否等于 NaN,因为 ECMAScript 中 NaN 不等于自身,可以使用原生函数 Number.isNaN() 或 isNaN()。
那么自己如何实现判断 NaN 值的方法?利用 NaN 不等于自身的原理。
隐式类型转换
对于 RegExp,String,Number,Boolean 等类型的值,假设一个比较值是字面量赋值,另一个比较值的通过构造函数生成的,ECMAScript 会认为两个值并不相等。
原因是因为字面量赋值的变量和构造函数生成的变量之间的类型不同,前面说过,严格相等下不同类型的值是不全等的,那么如何处理这种情况?答案是对比较值进行隐式转换。
递归遍历
对于 toString() 是 Array 和 Object 类型的比较值,则循环遍历里面的元素或属性进行比较,只有length 属性值相等且里面的元素或属性都相等的情况下,就说明两个比较值是相等的了。存在一种情况就是比较值里的元素或者属性值是一个嵌套的对象,这就需要使用递归遍历。
PS: underscore 源码中的 _.isEqual 源码注释地址: 源码注释。
参考
The text was updated successfully, but these errors were encountered: