_.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是否是obj
othIsObj = othTag == objectTag && !isHostObject(other),// oth是否是obj
isSameTag = 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会被转成NaN
return eq(+object, +other);// eq是浅比较判断相等
case errorTag: // => new Error
return object.name == other.name && object.message == other.message;
case regexpTag: // 正则 => new Rexg
case 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);// 删除掉对应的object
return result;
case symbolTag:
if (symbolValueOf) { //symbolProto=>symbolProto.valueOf
return 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
先去判断object
和other
的属性名是否相同,不相同直接返回。 - 再去判断对应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)) { // 数组长度不相等,直接返回false
return 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...
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。