Title: 从underscore源码看如何判断两个对象相等 · Issue #10 · webproblem/Blog · GitHub
Open Graph Title: 从underscore源码看如何判断两个对象相等 · Issue #10 · webproblem/Blog
X Title: 从underscore源码看如何判断两个对象相等 · Issue #10 · webproblem/Blog
Description: 首先要清楚 JavaScript 中的相等分为宽松相等(==)和严格相等(===)。宽松相等在比较值的时候会先进行类型的隐式转换,严格相等下如果比较值的类型不一致,那么就判定比较值不全等。如果比较值是引用类型,宽松相等和严格相等就不能直接判断出值是否相等了(引用类型浅拷贝比较值除外,也就是比较值指向的是同一引用地址),原因是对于任意两个不同的非原始对象,即便他们有相同的结构,都会计算得到 false 。 var num = 1; var str = '1'; conso...
Open Graph Description: 首先要清楚 JavaScript 中的相等分为宽松相等(==)和严格相等(===)。宽松相等在比较值的时候会先进行类型的隐式转换,严格相等下如果比较值的类型不一致,那么就判定比较值不全等。如果比较值是引用类型,宽松相等和严格相等就不能直接判断出值是否相等了(引用类型浅拷贝比较值除外,也就是比较值指向的是同一引用地址),原因是对于任意两个不同的非原始对象,即便他们有相同的结构,都会计算得到 f...
X Description: 首先要清楚 JavaScript 中的相等分为宽松相等(==)和严格相等(===)。宽松相等在比较值的时候会先进行类型的隐式转换,严格相等下如果比较值的类型不一致,那么就判定比较值不全等。如果比较值是引用类型,宽松相等和严格相等就不能直接判断出值是否相等了(引用类型浅拷贝比较值除外,也就是比较值指向的是同一引用地址),原因是对于任意两个不同的非原始对象,即便他们有相同的结构,都会计算得到 f...
Opengraph URL: https://github.com/webproblem/Blog/issues/10
X: @github
Domain: patch-diff.githubusercontent.com
{"@context":"https://schema.org","@type":"DiscussionForumPosting","headline":"从underscore源码看如何判断两个对象相等","articleBody":"首先要清楚 JavaScript 中的相等分为宽松相等(==)和严格相等(===)。宽松相等在比较值的时候会先进行类型的隐式转换,严格相等下如果比较值的类型不一致,那么就判定比较值不全等。如果比较值是引用类型,宽松相等和严格相等就不能直接判断出值是否相等了(引用类型浅拷贝比较值除外,也就是比较值指向的是同一引用地址),原因是对于任意两个不同的非原始对象,即便他们有相同的结构,都会计算得到 false 。\r\n\r\n```javascript\r\nvar num = 1;\r\nvar str = '1';\r\nconsole.log(num == str); // true\r\nconsole.log(num === str); // false\r\n\r\nvar obj1 = {name: '白展堂'};\r\nvar obj2 = {name: '白展堂'};\r\nvar obj3 = obj1;\r\nconsole.log(obj1 == obj2); // false\r\nconsole.log(obj1 === obj2); // false\r\nconsole.log(obj1 == obj3); // true\r\nconsole.log(obj1 === obj3); // true\r\n\r\nvar arr1 = [1];\r\nvar arr2 = [1];\r\nconsole.log(arr1 == arr2); // false\r\nconsole.log(arr1 === arr2); // false\r\n```\r\n\r\n### JSON.stringify\r\n\r\n如何判断对象是否相等?\r\n\r\n一种解决方案就是使用 JSON.stringify 序列化成字符串再做比较。\r\n\r\n```javascript\r\nvar obj1 = {name: '白展堂', age: 25};\r\nvar obj2 = {name: '白展堂', age: 25};\r\nJSON.stringify(obj1) === JSON.stringify(obj2); // true\r\n\r\nvar arr1 = ['a', 'b', 'c', 'd'];\r\nvar arr2 = ['a', 'b', 'c', 'd'];\r\nJSON.stringify(arr1) === JSON.stringify(arr2); // true\r\n```\r\n\r\n这种方案看似可以判断出对象是否相等,但是会不会存在问题呢?看过 underscore 源码的都知道,isEqual 函数的实现有多复杂,很多种情况显然不是通过 JSON.stringify 序列化就能解决的。\r\n\r\n先来分析下 JSON.stringify 方案存在的问题,假设比较对象中的属性值存在 RegExp 对象,判定结果是怎样的呢?\r\n\r\n```javascript\r\nfunction eq(a, b) {\r\n return JSON.stringify(a) === JSON.stringify(b);\r\n}\r\nvar obj1 = {name: '白展堂', reg: /test1/i};\r\nvar obj2 = {name: '白展堂', reg: /test2/i};\r\neq(obj1, obj2); // true\r\n```\r\n\r\n结果为 true,也就是说 obj1 和 obj2 序列化的字符串是一致的。\r\n\r\n```javascript\r\nvar obj1 = {name: '白展堂', reg: /test1/i};\r\nvar obj2 = {name: '白展堂', reg: /test2/i};\r\nJSON.stringify(obj1); // \"{\"name\":\"白展堂\",\"reg\":{}}\"\r\nJSON.stringify(obj2); // \"{\"name\":\"白展堂\",\"reg\":{}}\"\r\n```\r\n\r\n可以看到,JSON.stringify 将 RegExp 对象序列化成了 '{}',也就是说 JSON.stringify 序列化对于某些情况会存在问题,比如 undefined 和 Function 函数在序列化过程中会被忽略。\r\n\r\n```javascript\r\nfunction test() {}\r\nJSON.stringify(undefined) === JSON.stringify(test); // true\r\n```\r\n\r\n### _.isEqual\r\n\r\n那么如何完美的判断对象或值相等?现在来看看 underscore 中的 isEqual 函数是如何针对不同的比较值进行处理的。\r\n\r\n#### 区分 +0 与 -0 之间的差异\r\n\r\nECMAScript 中,认为 0 与 -0 是相等的,其实不然。\r\n\r\n```javascript\r\n1 / 0 // Infinity\r\n1 / -0 // -Infinity\r\n1 / 0 === 1 / -0 // false\r\n```\r\n\r\n原因是因为 JavaScript 中的 Number 是64位双精度浮点数,采用了IEEE_754 浮点数表示法,这是一种二进制表示法,按照这个标准,最高位是符号位(0 代表正,1 代表负),剩下的用于表示大小。而对于零这个边界值 ,1000(-0) 和 0000(0)都是表示 0 ,这才有了正负零的区别。\r\n\r\n那么如何区分 0 与 -0?\r\n\r\n```javascript\r\nfunction eq(a, b) {\r\n // 比较值a,b相等且值不是0和-0的情况\r\n if(a === b) {\r\n return a !== 0 || 1 / a === 1 / b; \r\n }\r\n return false;\r\n}\r\neq(0, 0); // true\r\neq(0, -0); // false\r\n```\r\n\r\n#### 判断值是否为 NaN\r\n\r\n判断某个值是否为 NaN 时不能直接比较这个值是否等于 NaN,因为 ECMAScript 中 NaN 不等于自身,可以使用原生函数 Number.isNaN() 或 isNaN()。\r\n\r\n```javascript\r\nvar a = NaN;\r\na === NaN; // false\r\nisNaN(a); // true\r\n```\r\n\r\n那么自己如何实现判断 NaN 值的方法?利用 NaN 不等于自身的原理。\r\n\r\n```javascript\r\nfunction eq(a, b) {\r\n if(a !== a) return b !== b; \r\n}\r\neq(NaN, NaN); //true\r\neq(NaN, 'test'); // false\r\n```\r\n\r\n#### 隐式类型转换\r\n\r\n对于 RegExp,String,Number,Boolean 等类型的值,假设一个比较值是字面量赋值,另一个比较值的通过构造函数生成的,ECMAScript 会认为两个值并不相等。\r\n\r\n```javascript\r\nvar s1 = 'test';\r\nvar s2 = new String('test');\r\nconsole.log(s1 === s2); // false\r\ntypeof s1; // 'string'\r\ntypeof s2; // 'object'\r\n\r\nvar n1 = 100;\r\nvar n2 = new Number(100);\r\nconsole.log(n1 === n2); // false\r\ntypeof n1; // 'number'\r\ntypeof n2; // 'object'\r\n```\r\n\r\n原因是因为字面量赋值的变量和构造函数生成的变量之间的类型不同,前面说过,严格相等下不同类型的值是不全等的,那么如何处理这种情况?答案是对比较值进行隐式转换。\r\n\r\n\r\n\r\n#### 递归遍历\r\n\r\n对于 toString() 是 Array 和 Object 类型的比较值,则循环遍历里面的元素或属性进行比较,只有length 属性值相等且里面的元素或属性都相等的情况下,就说明两个比较值是相等的了。存在一种情况就是比较值里的元素或者属性值是一个嵌套的对象,这就需要使用递归遍历。\r\n\r\n\r\n\r\nPS: underscore 源码中的 _.isEqual 源码注释地址: [源码注释](https://github.com/webproblem/Blog/blob/master/%E6%BA%90%E7%A0%81%E8%A7%A3%E8%AF%BB/underscore/Underscore.1.8.3.analysis.js#L1340-L1465)。\r\n\r\n### 参考\r\n\r\n* https://github.com/mqyqingfeng/Blog/issues/41\r\n* https://github.com/hanzichi/underscore-analysis/issues/5\r\n* https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Equality_comparisons_and_sameness\r\n* https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify\r\n\r\n","author":{"url":"https://github.com/webproblem","@type":"Person","name":"webproblem"},"datePublished":"2018-11-09T06:33:09.000Z","interactionStatistic":{"@type":"InteractionCounter","interactionType":"https://schema.org/CommentAction","userInteractionCount":3},"url":"https://github.com/10/Blog/issues/10"}
| route-pattern | /_view_fragments/issues/show/:user_id/:repository/:id/issue_layout(.:format) |
| route-controller | voltron_issues_fragments |
| route-action | issue_layout |
| fetch-nonce | v2:a89a2648-e5b2-1aa9-bcbe-a49a33cb9713 |
| current-catalog-service-hash | 81bb79d38c15960b92d99bca9288a9108c7a47b18f2423d0f6438c5b7bcd2114 |
| request-id | E2B0:83DE7:80211AA:A529851:6975DBBE |
| html-safe-nonce | b4eab9547f55e29a58f6e3529883d8128a6669078ba2d3f24a022cf99d304b99 |
| visitor-payload | eyJyZWZlcnJlciI6IiIsInJlcXVlc3RfaWQiOiJFMkIwOjgzREU3OjgwMjExQUE6QTUyOTg1MTo2OTc1REJCRSIsInZpc2l0b3JfaWQiOiIxODc2ODA4MTY2NDYxMDA0NzM0IiwicmVnaW9uX2VkZ2UiOiJpYWQiLCJyZWdpb25fcmVuZGVyIjoiaWFkIn0= |
| visitor-hmac | b403bfb2b1681a753698bec45ddc15f997464af8558141aec53c82d728a6308f |
| hovercard-subject-tag | issue:379040288 |
| github-keyboard-shortcuts | repository,issues,copilot |
| google-site-verification | Apib7-x98H0j5cPqHWwSMm6dNU4GmODRoqxLiDzdx9I |
| octolytics-url | https://collector.github.com/github/collect |
| analytics-location | / |
| fb:app_id | 1401488693436528 |
| apple-itunes-app | app-id=1477376905, app-argument=https://github.com/_view_fragments/issues/show/webproblem/Blog/10/issue_layout |
| twitter:image | https://opengraph.githubassets.com/0f57fe1ddf05c41514fa1b1be647df615396f630788e1076ee0450bc4f3adfa2/webproblem/Blog/issues/10 |
| twitter:card | summary_large_image |
| og:image | https://opengraph.githubassets.com/0f57fe1ddf05c41514fa1b1be647df615396f630788e1076ee0450bc4f3adfa2/webproblem/Blog/issues/10 |
| og:image:alt | 首先要清楚 JavaScript 中的相等分为宽松相等(==)和严格相等(===)。宽松相等在比较值的时候会先进行类型的隐式转换,严格相等下如果比较值的类型不一致,那么就判定比较值不全等。如果比较值是引用类型,宽松相等和严格相等就不能直接判断出值是否相等了(引用类型浅拷贝比较值除外,也就是比较值指向的是同一引用地址),原因是对于任意两个不同的非原始对象,即便他们有相同的结构,都会计算得到 f... |
| og:image:width | 1200 |
| og:image:height | 600 |
| og:site_name | GitHub |
| og:type | object |
| og:author:username | webproblem |
| hostname | github.com |
| expected-hostname | github.com |
| None | 2bce766e7450b03e00b2fc5badd417927ce33a860e78cda3e4ecb9bbd1374cc6 |
| turbo-cache-control | no-preview |
| go-import | github.com/webproblem/Blog git https://github.com/webproblem/Blog.git |
| octolytics-dimension-user_id | 20440496 |
| octolytics-dimension-user_login | webproblem |
| octolytics-dimension-repository_id | 115346710 |
| octolytics-dimension-repository_nwo | webproblem/Blog |
| octolytics-dimension-repository_public | true |
| octolytics-dimension-repository_is_fork | false |
| octolytics-dimension-repository_network_root_id | 115346710 |
| octolytics-dimension-repository_network_root_nwo | webproblem/Blog |
| turbo-body-classes | logged-out env-production page-responsive |
| disable-turbo | false |
| browser-stats-url | https://api.github.com/_private/browser/stats |
| browser-errors-url | https://api.github.com/_private/browser/errors |
| release | fcca2b8ef702b5f7f91427a6e920fa44446fe312 |
| ui-target | canary-1 |
| theme-color | #1e2327 |
| color-scheme | light dark |
Links:
Viewport: width=device-width