object/isEqual.jsjavascript
/**
 * 比较两个值并处理循环引用。
 * @private
 */
function equal(a, b, seen) {
  if (Object.is(a, b)) return true
  if (typeof a !== 'object' || typeof b !== 'object' || a === null || b === null) return false
  if (a.constructor !== b.constructor) return false
  if (seen.get(a) === b) return true
  seen.set(a, b)
  if (a instanceof Date) return a.getTime() === b.getTime()
  if (a instanceof RegExp) return a.source === b.source && a.flags === b.flags
  if (a instanceof Map) return a.size === b.size && [...a].every(([key, value]) => b.has(key) && equal(value, b.get(key), seen))
  if (a instanceof Set) return a.size === b.size && [...a].every(value => b.has(value))
  if (ArrayBuffer.isView(a)) return a.length === b.length && a.every((value, index) => Object.is(value, b[index]))
  if (Array.isArray(a)) return a.length === b.length && a.every((value, index) => equal(value, b[index], seen))
  const keysA = Reflect.ownKeys(a)
  const keysB = Reflect.ownKeys(b)
  const enumerableKeysA = keysA.filter(key => Object.prototype.propertyIsEnumerable.call(a, key))
  const enumerableKeysB = keysB.filter(key => Object.prototype.propertyIsEnumerable.call(b, key))
  return enumerableKeysA.length === enumerableKeysB.length && enumerableKeysA.every(key => Object.prototype.hasOwnProperty.call(b, key) && equal(a[key], b[key], seen))
}

/**
 * 深度比较两个值,支持循环引用、Map、Set、Date 和 RegExp。
 * @function isEqual
 * @memberof module:object
 * @param {*} first 第一个值
 * @param {*} second 第二个值
 * @returns {boolean} 两个值深度相等时为 true

 */
function isEqual(first, second) {
  return equal(first, second, new Map())
}

export default isEqual