_.isEqual源码学习

news/2024/12/12 21:28:14

_.isEqual源码学习

作用

_.isEqual 比较两者的值是否相等。
_.isEqualWith(value, other, [customizer]),也是比较两者的值是否相等。它还接受一个customizer

我们需要比较的类型有许多。 对象,数组,字符串,布尔,Date,正则,Symbool等。

实现

入口isEqual

/*** Performs a deep comparison between two values to determine if they are* equivalent.* * **Note:** This method supports comparing arrays, array buffers, booleans,* date objects, error objects, maps, numbers, `Object` objects, regexes,* sets, strings, symbols, and typed arrays. `Object` objects are compared* by their own, not inherited, enumerable properties. Functions and DOM* nodes are **not** supported.* var object = { 'a': 1 };* var other = { 'a': 1 };** _.isEqual(object, other);* // => true** object === other;* // => false*/
function isEqual(value, other) {return baseIsEqual(value, other);
}

isEqual方法提供了一个很深的比较来决定两个值是否相等。该方法支持array,array buffers,booleans,data objects,error objects,maps,numbers,等等等等

对于稍复杂一点的对象,支持比较自身的属性,但是不包括继承和可枚举的属性。

不支持函数和dom节点的比较。

baseIsEqual

/*** @param {Function} [customizer] The function to customize comparisons.* @param {boolean} [bitmask] The bitmask of comparison flags.*  The bitmask may be composed of the following flags:*     1 - Unordered comparison 无序的比较*     2 - Partial comparison  部分比较。* @param {Object} [stack] Tracks traversed `value` and `other` objects. 跟踪 传入的value和other* @returns {boolean} 是否相等*/
function baseIsEqual(value, other, customizer, bitmask, stack) {if (value === other) {return true;}if (value == null || other == null || (!isObject(value) && !isObjectLike(other))) {return value !== value && other !== other; //NaN}return baseIsEqualDeep(value, other, baseIsEqual, customizer, bitmask, stack);
}

_.isEqual的基本实现,支持部分比较和跟踪遍历对象。

看了几个参数,有几点需要注意
  • stack是如和做到追踪传入的参数的
  • Unordered comparison 和 Partial comparison 是如何作用的
  • customizer 是怎么处理值比较的。
  • 不同类型判断equal的方式。

baseIsEqualDeep


/*** A specialized version of `baseIsEqual` for arrays and objects which performs* deep comparisons and tracks traversed objects enabling objects with circular* references to be compared.** @private* @param {Object} object The object to compare.* @param {Object} other The other object to compare.* @param {Function} equalFunc The function to determine equivalents of values.* @param {Function} [customizer] The function to customize comparisons.* @param {number} [bitmask] The bitmask of comparison flags. See `baseIsEqual`*  for more details.* @param {Object} [stack] Tracks traversed `object` and `other` objects.* @returns {boolean} Returns `true` if the objects are equivalent, else `false`.*/
function baseIsEqualDeep(object, other, equalFunc, customizer, bitmask, stack) {var objIsArr = isArray(object),//数组?othIsArr = isArray(other),//数组objTag = arrayTag,//[object Array]othTag = arrayTag;//[object Array]if (!objIsArr) {objTag = getTag(object);objTag = objTag == argsTag ? objectTag : objTag;//  '[object Arguments]' ?}if (!othIsArr) {othTag = getTag(other);othTag = othTag == argsTag ? objectTag : othTag;}var objIsObj = objTag == objectTag && !isHostObject(object),// obj是否是objothIsObj = othTag == objectTag && !isHostObject(other),// oth是否是objisSameTag = objTag == othTag; // obj 和 oth是否是统一类型if (isSameTag && !objIsObj) {stack || (stack = new Stack);return (objIsArr || isTypedArray(object))? equalArrays(object, other, equalFunc, customizer, bitmask, stack)// 数组比较: equalByTag(object, other, objTag, equalFunc, customizer, bitmask, stack);// 对象比较}if (!(bitmask & PARTIAL_COMPARE_FLAG)) {var objIsWrapped = objIsObj && hasOwnProperty.call(object, '__wrapped__'), //被包装对象othIsWrapped = othIsObj && hasOwnProperty.call(other, '__wrapped__'); // 被包装的。if (objIsWrapped || othIsWrapped) {var objUnwrapped = objIsWrapped ? object.value() : object,othUnwrapped = othIsWrapped ? other.value() : other;stack || (stack = new Stack);return equalFunc(objUnwrapped, othUnwrapped, customizer, bitmask, stack);}}if (!isSameTag) { // 不是相同的标签return false;}stack || (stack = new Stack);return equalObjects(object, other, equalFunc, customizer, bitmask, stack);
}
  • 针对数组。有equalArrays
  • 针对对象。有equalObjects
  • 针对otherTags有equa
  • 还有一个equalFunc,暂时不解释

相关调用逻辑

  • 前置如果二者不是相同的tag,直接return false
  • 如果相同的tag,而且是数组,调用equalArrays
  • 相同的tag,不是对象tag,不是数组,调用equalByTag
  • 相同的tag,是对象的话,调用equalObjects

equalByTag

baseIsEqualDeep的专门版本用来比较相同tag的对象。它仅支持Boolean`, `Date`, `Error`, `Number`, `RegExp`, or `String


function equalByTag(object, other, tag, equalFunc, customizer, bitmask, stack) {switch (tag) {case dataViewTag: //'[object DataView]'if ((object.byteLength != other.byteLength) ||(object.byteOffset != other.byteOffset)) {return false;}object = object.buffer;other = other.buffer;case arrayBufferTag: // '[object ArrayBuffer]'if ((object.byteLength != other.byteLength) ||!equalFunc(new Uint8Array(object), new Uint8Array(other))) {return false;}return true;case boolTag:case dateTag:case numberTag:// Coerce booleans to `1` or `0` and dates to milliseconds. 布尔值会转成"1"或者"0";日期会被转换成毫秒。// Invalid dates are coerced to `NaN`.无效的dates会被转成NaNreturn eq(+object, +other);// eq是浅比较判断相等case errorTag: // => new Errorreturn object.name == other.name && object.message == other.message;case regexpTag: // 正则 => new Rexgcase stringTag: // 字符串 => new String// Coerce regexes to strings and treat strings, primitives and objects, 将regexes强制转换为字符串,如果转换的字符串相等,则原对象也相等。// as equal. See http://www.ecma-international.org/ecma-262/7.0/#sec-regexp.prototype.tostring// for more details.return object == (other + '');case mapTag:var convert = mapToArray; // 将map转成数组case setTag:var isPartial = bitmask & PARTIAL_COMPARE_FLAG;convert || (convert = setToArray);if (object.size != other.size && !isPartial) {return false;}// Assume cyclic values are equal.// 循环引用var stacked = stack.get(object); if (stacked) { // 存在直接判断返回return stacked == other;}bitmask |= UNORDERED_COMPARE_FLAG;// Recursively compare objects (susceptible to call stack limits).stack.set(object, other);// 调用的是数组比较方法,object和other为set或者map,将之转换成数组。再去比较。var result = equalArrays(convert(object), convert(other), equalFunc, customizer, bitmask, stack);stack['delete'](object);// 删除掉对应的objectreturn result;case symbolTag:if (symbolValueOf) { //symbolProto=>symbolProto.valueOfreturn symbolValueOf.call(object) == symbolValueOf.call(other);  // 相等条件是比较两者的valueof。}}return false;
}
  • 布尔,date,NumberTag,通过+value转换成0或者1来比较。
  • error,比较条件是 error.name相等 && error.message相等
  • 正则和字符串, 对于正则,先转换成字符串,再比较相等否。
  • map或者set,将object和other转换成数组,再调用equalArrays判断

equalObjects

一个特殊版本的baseIsEqualDeep方法,用来比较对象。

/*** A specialized version of `baseIsEqualDeep` for objects with support for* partial deep comparisons*/
function equalObjects(object, other, equalFunc, customizer, bitmask, stack) {var isPartial = bitmask & PARTIAL_COMPARE_FLAG,objProps = keys(object),// object.keys(object) => [keyName0]objLength = objProps.length, // obj长度othProps = keys(other),//other keys数组othLength = othProps.length;// other的长度if (objLength != othLength && !isPartial) {return false;}var index = objLength;while (index--) { // 这里判断,如果 object的属性名在other中不存在,直接返回var key = objProps[index];// obj中的属性名if (!(isPartial ? key in other : /* other中有这个keyname**/hasOwnProperty.call(other, key))) { //object自由属性中没有key这个属性,就不想等return false;}}// Assume cyclic values are equal.var stacked = stack.get(object);if (stacked && stack.get(other)) {return stacked == other;}var result = true;stack.set(object, other);stack.set(other, object);var skipCtor = isPartial;while (++index < objLength) {key = objProps[index];var objValue = object[key],othValue = other[key];if (customizer) {var compared = isPartial? customizer(othValue, objValue, key, other, object, stack): customizer(objValue, othValue, key, object, other, stack);}// Recursively compare objects (susceptible to call stack limits).// 这里是判断对应key的value是否相等。直接返回false。当然即便相等,这里的逻辑也不足够严谨,还要往下走,比对constructor。这里使用`equalFunc`比较,是因为我们无法判断value的类型,此时的equalFunc 仍是`baseIsEqual`if (!(compared === undefined? (objValue === othValue || equalFunc(objValue, othValue, customizer, bitmask, stack)): compared)) {result = false;break;}skipCtor || (skipCtor = key == 'constructor');}if (result && !skipCtor) { // result为true,是通过上边的比较之后,仍然相等,那么就要去比对对象的constructor。即便key和value相等,但是constructor不相同,那么仍然不相等。var objCtor = object.constructor,othCtor = other.constructor;// Non `Object` object instances with different constructors are not equal.if (objCtor != othCtor &&('constructor' in object && 'constructor' in other) &&!(typeof objCtor == 'function' && objCtor instanceof objCtor &&typeof othCtor == 'function' && othCtor instanceof othCtor)) {result = false;}}stack['delete'](object);stack['delete'](other);return result;
}
  • equalObjects先去判断objectother的属性名是否相同,不相同直接返回。
  • 再去判断对应key的value的值是否相等
  • 再如果仍然符合条件再去判断constructor是否相同。

满足以上3个条件,对象才是相等的。

equalArrays(数组比较方法)

/*** A specialized version of `baseIsEqualDeep` for arrays with support for* partial deep comparisons.*/
function equalArrays(array, other, equalFunc, customizer, bitmask, stack) {var isPartial = bitmask & PARTIAL_COMPARE_FLAG,arrLength = array.length,//array的长度othLength = other.length;// other的长度if (arrLength != othLength && !(isPartial && othLength > arrLength)) { // 数组长度不相等,直接返回falsereturn false;}// Assume cyclic values are equal. 假设循环值相等。var stacked = stack.get(array); // 如果stacked不是undefined,我们当前看的 _.isEqual 是不存在这个判断的if (stacked && stack.get(other)) {return stacked == other;}var index = -1,result = true,seen = (bitmask & UNORDERED_COMPARE_FLAG) ? new SetCache : undefined;stack.set(array, other); // array 和 other做了关联 ,stack.set(other, array);// Ignore non-index properties. 忽略没有index的属性while (++index < arrLength) {var arrValue = array[index],othValue = other[index];if (customizer) {var compared = isPartial? customizer(othValue, arrValue, index, other, array, stack): customizer(arrValue, othValue, index, array, other, stack);}if (compared !== undefined) {if (compared) {continue;}result = false;break;}// Recursively compare arrays (susceptible to call stack limits).if (seen) {if (!arraySome(other, function(othValue, othIndex) {if (!seen.has(othIndex) &&(arrValue === othValue || equalFunc(arrValue, othValue, customizer, bitmask, stack))) {return seen.add(othIndex);}})) {result = false;break;}} else if (!(arrValue === othValue ||equalFunc(arrValue, othValue, customizer, bitmask, stack) // 如果不相等,直接跳走。)) {result = false;break;}}stack['delete'](array);stack['delete'](other);return result;
}

TODO

  • stack的原理,在整个工作流程中做了哪些事情
  • baseIsEqual的bitmask参数是做什么的
bitmask 按位操作符. 1&0=>0,undefined &1 =>0,1&1=>1 https://developer.mozilla.org...

https://dhexx.cn/news/show-3991602.html

相关文章

10hutool实战:TemporalAccessorUtil{TemporalAccessor} 工具类封装

技术活&#xff0c;该赏 点赞再看&#xff0c;养成习惯hutool实战&#xff08;带你掌握里面的各种工具&#xff09;目录 万字博文教你搞懂java源码的日期和时间相关用法 用途&#xff1a;TemporalAccessor 工具类封装 使用场景 TemporalAccessor 的实现类包含 InstantLocal…

springmvc 参数@RequestBody用例方法

前端代码 $.ajax({type: "post",url: baseUrl "handleSubmit",data: JSON.stringify(self.search),contentType: application/json,dataType: "json",success: function (data) {console.log(data)},error: function (error) {console.log(requ…

1hutool实战:IoUtil 流操作工具类(copy拷贝操作)

技术活&#xff0c;该赏 关注一键三连&#xff08;点赞&#xff0c;评论&#xff0c;收藏&#xff09;再看&#xff0c;养成好习惯hutool实战&#xff08;带你掌握里面的各种工具&#xff09;目录 用途&#xff1a;IO工具类&#xff08;copy拷贝操作&#xff09; 使用场景 IO…

JAVA面试核心教程|Java面试基础知识点总结

Java中的原始数据类型都有哪些&#xff0c;它们的大小及对应的封装类是什么&#xff1f; byte——1 byte——Byte short——2 bytes——Short int——4 bytes——Integer long——8 bytes——Long float——4 bytes——Float double——8 bytes——Double char——2 bytes——C…

2hutool实战:IoUtil 流操作工具类(获取getReader and getWriter)

技术活&#xff0c;该赏 关注一键三连&#xff08;点赞&#xff0c;评论&#xff0c;收藏&#xff09;再看&#xff0c;养成好习惯hutool实战&#xff08;带你掌握里面的各种工具&#xff09;目录 用途&#xff1a;IO工具类&#xff08;获取getReader and getWriter&#xff09…

3hutool实战:IoUtil 流操作工具类(从流中读取内容)

技术活&#xff0c;该赏 关注一键三连&#xff08;点赞&#xff0c;评论&#xff0c;收藏&#xff09;再看&#xff0c;养成好习惯hutool实战&#xff08;带你掌握里面的各种工具&#xff09;目录 用途&#xff1a;IO工具类&#xff08;从流中读取内容&#xff09; 使用场景 …

【tomcat】servlet原理及其生命周期

1.什么是servlet&#xff1f; Servlet&#xff08;Servlet Applet&#xff09;&#xff0c;全称Java Servlet,是用Java编写的服务器端程序。而这些Servlet都要实现Servlet这个接口。其主要功能在于交互式的浏览和修改数据&#xff0c;生成动态Web内容。Servlet运行于支持Java的…

4hutool实战:IoUtil 流操作工具类(toStream转为流)

技术活&#xff0c;该赏 关注一键三连&#xff08;点赞&#xff0c;评论&#xff0c;收藏&#xff09;再看&#xff0c;养成好习惯hutool实战&#xff08;带你掌握里面的各种工具&#xff09;目录 用途&#xff1a;IO工具类&#xff08;toStream转为流&#xff09; 使用场景 …

网管软件国产化支持趋势不可阻挡

2019独角兽企业重金招聘Python工程师标准>>> 虽然很多领先的网管技术都源于国外&#xff0c;但是现在新技术在国内的普及非常迅速&#xff0c;而且国内网管厂商大都具有很强的新技术信息捕捉和开发能力&#xff0c;在新技术和新产品上都能够迅速跟进国际水准&#x…