<div id="left"></div>
<div id="right"></div>
<div id="result"></div>
const NUMBER_TAG = '[object Number]';
const STRING_TAG = '[object String]';
const BOOLEAN_TAG = '[object Boolean]';
const NULL_TAG = '[object Null]';
const UNDEFINED_TAG = '[object Undefined]';
const OBJECT_TAG = '[object Object]';
const ARRAY_TAG = '[object Array]';
const ERROR_TAG = '[object Error]';
const DATE_TAG = '[object Date]';
const REGEXP_TAG = '[object RegExp]';
const MAP_TAG = '[object Map]';
const SET_TAG = '[object Set]';
// 以下两种类型一般不比较相等性
const SYMBOL_TAG = '[object Symbol]';
const FUNCTION_TAG = '[object Function]';
const toString = Object.prototype.toString;
const getKeys = Object.keys;
// map转数组
function mapToArray(map) {
let idx = -1;
const result = new Array(map.size);
map.forEach(function (value, key) {
result[++idx] = [key, value];
});
return result;
}
// set转数组
function setToArray(set) {
let idx = -1;
const result = new Array(set.size);
set.forEach(function (value) {
result[++idx] = value;
});
return result;
}
/**
* 比较两个变量是否相等,包括引用类型
* @param {[type]} value [description]
* @param {[type]} other [description]
* @param {[type]} vStack [description]
* @param {[type]} oStack [description]
* @return {[type]} [description]
*/
function eq(value, other, vStack, oStack) {
const valueType = toString.call(value);
const otherType = toString.call(other);
// 类型不同直接返回false
if (valueType !== otherType) {
return false;
}
// 这里直接进行一个简单粗暴的判断, 这里默认+0与-0相等,不再进行处理
// 同样适用于基本类型和引用类型,如果是同一个引用也可返回true
if (value === other) {
return true;
}
// 处理NaN不相等情况
if (value !== value && other !== other) {
return true;
}
// 处理其他情况
switch (valueType) {
case NUMBER_TAG:
case DATE_TAG:
case BOOLEAN_TAG:
return +value === +other;
case STRING_TAG:
case REGEXP_TAG:
case NULL_TAG:
case UNDEFINED_TAG:
return '' + value === '' + other;
case ERROR_TAG:
return value.name === other.name && value.message === other.message;
case MAP_TAG:
const valueMapArr = mapToArray(value);
const otherMapArr = mapToArray(other);
if (!eq(valueMapArr, otherMapArr, vStack, oStack)) {
return false;
}
break;
case SET_TAG:
const valueSetArr = setToArray(value);
const otherSetArr = setToArray(other);
if (!eq(valueSetArr, otherSetArr, vStack, oStack)) {
return false;
}
break;
case ARRAY_TAG:
case OBJECT_TAG:
// 首先对比是否为循环引用
vStack = vStack || [];
oStack = oStack || [];
let vStackLength = vStack.length;
while (vStackLength--) {
if (
vStack[vStackLength] === value &&
oStack[vStackLength] === other
) {
return true;
}
}
vStack.push(value);
oStack.push(other);
// 数组对比
if (valueType === ARRAY_TAG) {
let vLength = value.length;
let oLength = other.length;
if (vLength !== oLength) {
return false;
}
while (vLength--) {
if (!eq(value[vLength], other[vLength], vStack, oStack)) {
return false;
}
}
}
// 对象对比
if (valueType === OBJECT_TAG) {
const vKeys = getKeys(value);
const oKeys = getKeys(other);
let vKeysLength = vKeys.length;
if (vKeys.length !== oKeys.length) {
return false;
}
while (vKeysLength--) {
// 对象的key顺序可能不一样,但是对象仍然可能相等
if (!oKeys.includes(vKeys[vKeysLength])) {
return false;
}
let currentKey = vKeys[vKeysLength];
if (
!eq(
value[currentKey],
other[currentKey],
vStack,
oStack
)
) {
return false;
}
}
}
break;
default:
return String(value) === String(other);
}
return true;
}
var p1 = {
name: "andy",
age: 12,
love: [1, 2, 3]
};
var p2 = {
name: "andy",
age: 12,
love: [1, 2, 3]
};
var result = eq(p1, p2);
document.getElementById('left').innerText = JSON.stringify(p1);
document.getElementById('right').innerText = JSON.stringify(p2);
document.getElementById('result').innerText = JSON.stringify(result);
console.log("equal:", result);
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.