1/* 2 * Copyright (c) 2022 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16import DeepTypeUtils from './DeepTypeUtils' 17function assertDeepEquals(actualValue, expected) { 18 console.log('actualValue:' + actualValue + ',expected:' + expected[0]); 19 let result = eq(actualValue, expected[0],[], []) 20 let msg = logMsg(actualValue, expected[0]); 21 return { 22 pass: result, 23 message: msg 24 }; 25} 26 27/** 28 * 获取失败显示日志 29 * @param actualValue 实际对象 30 * @param expected 期待比较对象 31 */ 32function logMsg(actualValue, expected) { 33 // 获取a的对象名称 34 const aClassName = Object.prototype.toString.call(actualValue); 35 const bClassName = Object.prototype.toString.call(expected); 36 let actualMsg; 37 let expectMsg; 38 if(aClassName == "[object Function]") { 39 actualMsg = "actualValue Function" 40 }else if(aClassName == "[object Promise]") { 41 actualMsg = "actualValue Promise" 42 }else if(aClassName == "[object Set]" || aClassName == "[object Map]") { 43 actualMsg = JSON.stringify(Array.from(actualValue));; 44 }else if(aClassName == "[object RegExp]") { 45 actualMsg = JSON.stringify(actualValue.source.replace("\\",""));; 46 }else if(aClassName == "[object BigInt]") { 47 actualMsg = actualValue; 48 } 49 else{ 50 actualMsg = JSON.stringify(actualValue); 51 } 52 if(bClassName == "[object Function]") { 53 expectMsg = "expected Function" 54 }else if(bClassName == "[object Promise]") { 55 expectMsg = "expected Promise" 56 }else if(bClassName == "[object Set]" || bClassName == "[object Map]") { 57 expectMsg = JSON.stringify(Array.from(expected)); 58 }else if(bClassName == "[object RegExp]") { 59 expectMsg = JSON.stringify(expected.source.replace("\\",""));; 60 }else if(bClassName == "[object BigInt]") { 61 expectMsg = expected; 62 } 63 else{ 64 expectMsg = JSON.stringify(expected); 65 } 66 return actualMsg + " is not deep equal " + expectMsg; 67} 68 69function eq(a, b, aStack, bStack) { 70 let result = true; 71 console.log('a is:' + a + ',b is:' + b); 72 const asymmetricResult = asymmetricMatch_(a,b); 73 if (!DeepTypeUtils.isUndefined(asymmetricResult)) { 74 return asymmetricResult; 75 } 76 77 if (a instanceof Error && b instanceof Error) { 78 result = a.message == b.message; 79 return result; 80 } 81 82 if (a === b) { 83 result = a !== 0 || 1 / a == 1 / b; 84 return result; 85 } 86 87 if (a === null || b === null) { 88 result = a === b; 89 return result; 90 } 91 // 获取a的对象名称 92 const aClassName = Object.prototype.toString.call(a); 93 const bClassName = Object.prototype.toString.call(b); 94 console.log('aClassName is:' + aClassName); 95 console.log('bClassName is:' + bClassName); 96 // 不同类型不同对象 97 if (aClassName != bClassName) { 98 return false; 99 } 100 // 俩个string对象 101 if(aClassName === '[object String]') { 102 result = a == String(b); 103 return result; 104 } 105 // 俩个Number对象 106 if(aClassName === '[object Number]') { 107 result = a != +a ? b != +b : a === 0 && b === 0 ? 1 / a == 1 / b : a == +b; 108 return result; 109 } 110 111 if(aClassName === '[object Date]' || aClassName === '[object Boolean]') { 112 result = +a == +b; 113 return result; 114 } 115 116 // 数组 117 if(aClassName === '[object ArrayBuffer]') { 118 return eq(new Uint8Array(a), new Uint8Array(b), aStack, bStack); 119 } 120 121 // 正则表达式 122 if(aClassName === '[object RegExp]') { 123 return ( 124 a.source == b.source && 125 a.global == b.global && 126 a.multiline == b.multiline && 127 a.ignoreCase == b.ignoreCase 128 ); 129 } 130 131 if (typeof a != 'object' || typeof b != 'object') { 132 return false; 133 } 134 135 const aIsDomNode = DeepTypeUtils.isDomNode(a); 136 const bIsDomNode = DeepTypeUtils.isDomNode(b); 137 if (aIsDomNode && bIsDomNode) { 138 // At first try to use DOM3 method isEqualNode 139 result = a.isEqualNode(b); 140 return result; 141 } 142 if (aIsDomNode || bIsDomNode) { 143 return false; 144 } 145 const aIsPromise = DeepTypeUtils.isPromise(a); 146 const bIsPromise = DeepTypeUtils.isPromise(b); 147 if (aIsPromise && bIsPromise) { 148 return a === b; 149 } 150 let length = aStack.length; 151 while (length--) { 152 if (aStack[length] == a) { 153 return bStack[length] == b; 154 } 155 } 156 aStack.push(a); 157 bStack.push(b); 158 let size = 0; 159 160 // 都是数组 161 if(aClassName == '[object Array]') { 162 const aLength = a.length; 163 const bLength = b.length; 164 if (aLength !== bLength) { 165 // 数组长度不同,不是同一个对象 166 return false; 167 } 168 for (let i = 0; i < aLength || i < bLength; i++) { 169 // 递归每一个元素是否相同 170 result = eq(i < aLength ? a[i] : void 0, i < bLength ? b[i] : void 0, aStack, bStack) && result; 171 } 172 if (!result) { 173 return false; 174 } 175 } else if(DeepTypeUtils.isMap(a) && DeepTypeUtils.isMap(b)) { 176 if (a.size != b.size) { 177 return false; 178 } 179 const keysA = []; 180 const keysB = []; 181 a.forEach(function(valueA, keyA) { 182 keysA.push(keyA); 183 }); 184 b.forEach(function(valueB, keyB) { 185 keysB.push(keyB); 186 }); 187 const mapKeys = [keysA, keysB]; 188 const cmpKeys = [keysB, keysA]; 189 for (let i = 0; result && i < mapKeys.length; i++) { 190 const mapIter = mapKeys[i]; 191 const cmpIter = cmpKeys[i]; 192 193 for (let j = 0; result && j < mapIter.length; j++) { 194 const mapKey = mapIter[j]; 195 const cmpKey = cmpIter[j]; 196 const mapValueA = a.get(mapKey); 197 let mapValueB; 198 if ( 199 DeepTypeUtils.isAsymmetricEqualityTester_(mapKey) || 200 (DeepTypeUtils.isAsymmetricEqualityTester_(cmpKey) && 201 eq(mapKey, cmpKey)) 202 ) { 203 mapValueB = b.get(cmpKey); 204 } else { 205 mapValueB = b.get(mapKey); 206 } 207 result = eq(mapValueA, mapValueB, aStack, bStack); 208 } 209 } 210 if (!result) { 211 return false; 212 } 213 } else if(DeepTypeUtils.isSet(a) && DeepTypeUtils.isSet(b)) { 214 if (a.size != b.size) { 215 return false; 216 } 217 const valuesA = []; 218 a.forEach(function(valueA) { 219 valuesA.push(valueA); 220 }); 221 const valuesB = []; 222 b.forEach(function(valueB) { 223 valuesB.push(valueB); 224 }); 225 const setPairs = [[valuesA, valuesB], [valuesB, valuesA]]; 226 const stackPairs = [[aStack, bStack], [bStack, aStack]]; 227 for (let i = 0; result && i < setPairs.length; i++) { 228 const baseValues = setPairs[i][0]; 229 const otherValues = setPairs[i][1]; 230 const baseStack = stackPairs[i][0]; 231 const otherStack = stackPairs[i][1]; 232 for (const baseValue of baseValues) { 233 let found = false; 234 for (let j = 0; !found && j < otherValues.length; j++) { 235 const otherValue = otherValues[j]; 236 const prevStackSize = baseStack.length; 237 // 深度比较对象 238 found = eq(baseValue, otherValue, baseStack, otherStack); 239 if (!found && prevStackSize !== baseStack.length) { 240 baseStack.splice(prevStackSize); 241 otherStack.splice(prevStackSize); 242 } 243 } 244 result = result && found; 245 } 246 } 247 if (!result) { 248 return false; 249 } 250 } else { 251 const aCtor = a.constructor, 252 bCtor = b.constructor; 253 if ( 254 aCtor !== bCtor && 255 DeepTypeUtils.isFunction_(aCtor) && 256 DeepTypeUtils.isFunction_(bCtor) && 257 a instanceof aCtor && 258 b instanceof bCtor && 259 !(aCtor instanceof aCtor && bCtor instanceof bCtor) 260 ) { 261 return false; 262 } 263 } 264 265 // 获取对象所有的属性集合 266 const aKeys = DeepTypeUtils.keys(a, aClassName == '[object Array]'); 267 size = aKeys.length; 268 269 // 俩个对象属性长度不一致, 俩对象不相同 270 if (DeepTypeUtils.keys(b, bClassName == '[object Array]').length !== size) { 271 return false; 272 } 273 274 // 俩对象属性数量相同, 递归比较每个属性值得值 275 for (const key of aKeys) { 276 console.log('key is:' + key); 277 // b 没有 key 属性 278 if(!DeepTypeUtils.has(b, key)) { 279 result = false; 280 continue; 281 } 282 if (!eq(a[key], b[key], aStack, bStack)) { 283 result = false; 284 } 285 } 286 if (!result) { 287 return false; 288 } 289 aStack.pop(); 290 bStack.pop(); 291 return result; 292} 293 294function asymmetricMatch_(a, b) { 295 const asymmetricA = DeepTypeUtils.isAsymmetricEqualityTester_(a); 296 const asymmetricB = DeepTypeUtils.isAsymmetricEqualityTester_(b); 297 298 if (asymmetricA === asymmetricB) { 299 return undefined; 300 } 301 302} 303 304/** 305 * 获取对象的自有属性 306 * 307 * @param obj 对象 308 * @param isArray 是否是一个数组 309 */ 310function keys(obj, isArray) { 311 const keys = []; 312 313} 314 315export default assertDeepEquals; 316