/* * Copyright (c) 2022 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import DeepTypeUtils from './DeepTypeUtils' function assertDeepEquals(actualValue, expected) { console.log('actualValue:' + actualValue + ',expected:' + expected[0]); let result = eq(actualValue, expected[0],[], []) let msg = logMsg(actualValue, expected[0]); return { pass: result, message: msg }; } /** * 获取失败显示日志 * @param actualValue 实际对象 * @param expected 期待比较对象 */ function logMsg(actualValue, expected) { // 获取a的对象名称 const aClassName = Object.prototype.toString.call(actualValue); const bClassName = Object.prototype.toString.call(expected); let actualMsg; let expectMsg; if(aClassName == "[object Function]") { actualMsg = "actualValue Function" }else if(aClassName == "[object Promise]") { actualMsg = "actualValue Promise" }else if(aClassName == "[object Set]" || aClassName == "[object Map]") { actualMsg = JSON.stringify(Array.from(actualValue));; }else if(aClassName == "[object RegExp]") { actualMsg = JSON.stringify(actualValue.source.replace("\\",""));; }else if(aClassName == "[object BigInt]") { actualMsg = actualValue; } else{ actualMsg = JSON.stringify(actualValue); } if(bClassName == "[object Function]") { expectMsg = "expected Function" }else if(bClassName == "[object Promise]") { expectMsg = "expected Promise" }else if(bClassName == "[object Set]" || bClassName == "[object Map]") { expectMsg = JSON.stringify(Array.from(expected)); }else if(bClassName == "[object RegExp]") { expectMsg = JSON.stringify(expected.source.replace("\\",""));; }else if(bClassName == "[object BigInt]") { expectMsg = expected; } else{ expectMsg = JSON.stringify(expected); } return actualMsg + " is not deep equal " + expectMsg; } function eq(a, b, aStack, bStack) { let result = true; console.log('a is:' + a + ',b is:' + b); const asymmetricResult = asymmetricMatch_(a,b); if (!DeepTypeUtils.isUndefined(asymmetricResult)) { return asymmetricResult; } if (a instanceof Error && b instanceof Error) { result = a.message == b.message; return result; } if (a === b) { result = a !== 0 || 1 / a == 1 / b; return result; } if (a === null || b === null) { result = a === b; return result; } // 获取a的对象名称 const aClassName = Object.prototype.toString.call(a); const bClassName = Object.prototype.toString.call(b); console.log('aClassName is:' + aClassName); console.log('bClassName is:' + bClassName); // 不同类型不同对象 if (aClassName != bClassName) { return false; } // 俩个string对象 if(aClassName === '[object String]') { result = a == String(b); return result; } // 俩个Number对象 if(aClassName === '[object Number]') { result = a != +a ? b != +b : a === 0 && b === 0 ? 1 / a == 1 / b : a == +b; return result; } if(aClassName === '[object Date]' || aClassName === '[object Boolean]') { result = +a == +b; return result; } // 数组 if(aClassName === '[object ArrayBuffer]') { return eq(new Uint8Array(a), new Uint8Array(b), aStack, bStack); } // 正则表达式 if(aClassName === '[object RegExp]') { return ( a.source == b.source && a.global == b.global && a.multiline == b.multiline && a.ignoreCase == b.ignoreCase ); } if (typeof a != 'object' || typeof b != 'object') { return false; } const aIsDomNode = DeepTypeUtils.isDomNode(a); const bIsDomNode = DeepTypeUtils.isDomNode(b); if (aIsDomNode && bIsDomNode) { // At first try to use DOM3 method isEqualNode result = a.isEqualNode(b); return result; } if (aIsDomNode || bIsDomNode) { return false; } const aIsPromise = DeepTypeUtils.isPromise(a); const bIsPromise = DeepTypeUtils.isPromise(b); if (aIsPromise && bIsPromise) { return a === b; } let length = aStack.length; while (length--) { if (aStack[length] == a) { return bStack[length] == b; } } aStack.push(a); bStack.push(b); let size = 0; // 都是数组 if(aClassName == '[object Array]') { const aLength = a.length; const bLength = b.length; if (aLength !== bLength) { // 数组长度不同,不是同一个对象 return false; } for (let i = 0; i < aLength || i < bLength; i++) { // 递归每一个元素是否相同 result = eq(i < aLength ? a[i] : void 0, i < bLength ? b[i] : void 0, aStack, bStack) && result; } if (!result) { return false; } } else if(DeepTypeUtils.isMap(a) && DeepTypeUtils.isMap(b)) { if (a.size != b.size) { return false; } const keysA = []; const keysB = []; a.forEach(function(valueA, keyA) { keysA.push(keyA); }); b.forEach(function(valueB, keyB) { keysB.push(keyB); }); const mapKeys = [keysA, keysB]; const cmpKeys = [keysB, keysA]; for (let i = 0; result && i < mapKeys.length; i++) { const mapIter = mapKeys[i]; const cmpIter = cmpKeys[i]; for (let j = 0; result && j < mapIter.length; j++) { const mapKey = mapIter[j]; const cmpKey = cmpIter[j]; const mapValueA = a.get(mapKey); let mapValueB; if ( DeepTypeUtils.isAsymmetricEqualityTester_(mapKey) || (DeepTypeUtils.isAsymmetricEqualityTester_(cmpKey) && eq(mapKey, cmpKey)) ) { mapValueB = b.get(cmpKey); } else { mapValueB = b.get(mapKey); } result = eq(mapValueA, mapValueB, aStack, bStack); } } if (!result) { return false; } } else if(DeepTypeUtils.isSet(a) && DeepTypeUtils.isSet(b)) { if (a.size != b.size) { return false; } const valuesA = []; a.forEach(function(valueA) { valuesA.push(valueA); }); const valuesB = []; b.forEach(function(valueB) { valuesB.push(valueB); }); const setPairs = [[valuesA, valuesB], [valuesB, valuesA]]; const stackPairs = [[aStack, bStack], [bStack, aStack]]; for (let i = 0; result && i < setPairs.length; i++) { const baseValues = setPairs[i][0]; const otherValues = setPairs[i][1]; const baseStack = stackPairs[i][0]; const otherStack = stackPairs[i][1]; for (const baseValue of baseValues) { let found = false; for (let j = 0; !found && j < otherValues.length; j++) { const otherValue = otherValues[j]; const prevStackSize = baseStack.length; // 深度比较对象 found = eq(baseValue, otherValue, baseStack, otherStack); if (!found && prevStackSize !== baseStack.length) { baseStack.splice(prevStackSize); otherStack.splice(prevStackSize); } } result = result && found; } } if (!result) { return false; } } else { const aCtor = a.constructor, bCtor = b.constructor; if ( aCtor !== bCtor && DeepTypeUtils.isFunction_(aCtor) && DeepTypeUtils.isFunction_(bCtor) && a instanceof aCtor && b instanceof bCtor && !(aCtor instanceof aCtor && bCtor instanceof bCtor) ) { return false; } } // 获取对象所有的属性集合 const aKeys = DeepTypeUtils.keys(a, aClassName == '[object Array]'); size = aKeys.length; // 俩个对象属性长度不一致, 俩对象不相同 if (DeepTypeUtils.keys(b, bClassName == '[object Array]').length !== size) { return false; } // 俩对象属性数量相同, 递归比较每个属性值得值 for (const key of aKeys) { console.log('key is:' + key); // b 没有 key 属性 if(!DeepTypeUtils.has(b, key)) { result = false; continue; } if (!eq(a[key], b[key], aStack, bStack)) { result = false; } } if (!result) { return false; } aStack.pop(); bStack.pop(); return result; } function asymmetricMatch_(a, b) { const asymmetricA = DeepTypeUtils.isAsymmetricEqualityTester_(a); const asymmetricB = DeepTypeUtils.isAsymmetricEqualityTester_(b); if (asymmetricA === asymmetricB) { return undefined; } } /** * 获取对象的自有属性 * * @param obj 对象 * @param isArray 是否是一个数组 */ function keys(obj, isArray) { const keys = []; } export default assertDeepEquals;