_.differenceBy(array, [values], [iteratee=_.identity])
Array VALUES identity _.
2023-09-11 14:15:02 时间
5
_.differenceBy(array, [values], [iteratee=_.identity])
_.differenceBy方法类似于_.difference方法,它会接受一个迭代器函数参数,为数组和排除的数组的每个元素都用这个函数处理后再比较。
参数
array (Array): 用来检查的数组
[values] (...Array): 用来排除的数组
[iteratee=_.identity] (Function): 处理每一个元素的迭代器函数
返回值
(Array):返回一个包含过滤值的新数组
例子
_.differenceBy([2.1, 1.2], [2.3, 3.4], Math.floor); // => [1.2] // The `_.property` iteratee shorthand. _.differenceBy([{ 'x': 2 }, { 'x': 1 }], [{ 'x': 1 }], 'x'); // => [{ 'x': 2 }]
源代码:
/** * lodash (Custom Build) <https://lodash.com/> * Build: `lodash modularize exports="npm" -o ./` * Copyright jQuery Foundation and other contributors <https://jquery.org/> * Released under MIT license <https://lodash.com/license> * Based on Underscore.js 1.8.3 <http://underscorejs.org/LICENSE> * Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors */ /** Used as the size to enable large array optimizations. */ var LARGE_ARRAY_SIZE = 200; /** Used as the `TypeError` message for "Functions" methods. */ var FUNC_ERROR_TEXT = 'Expected a function'; /** Used to stand-in for `undefined` hash values. */ var HASH_UNDEFINED = '__lodash_hash_undefined__'; /** Used to compose bitmasks for comparison styles. */ var UNORDERED_COMPARE_FLAG = 1, PARTIAL_COMPARE_FLAG = 2; /** Used as references for various `Number` constants. */ var INFINITY = 1 / 0, MAX_SAFE_INTEGER = 9007199254740991; /** `Object#toString` result references. */ var argsTag = '[object Arguments]', arrayTag = '[object Array]', boolTag = '[object Boolean]', dateTag = '[object Date]', errorTag = '[object Error]', funcTag = '[object Function]', genTag = '[object GeneratorFunction]', mapTag = '[object Map]', numberTag = '[object Number]', objectTag = '[object Object]', promiseTag = '[object Promise]', regexpTag = '[object RegExp]', setTag = '[object Set]', stringTag = '[object String]', symbolTag = '[object Symbol]', weakMapTag = '[object WeakMap]'; var arrayBufferTag = '[object ArrayBuffer]', dataViewTag = '[object DataView]', float32Tag = '[object Float32Array]', float64Tag = '[object Float64Array]', int8Tag = '[object Int8Array]', int16Tag = '[object Int16Array]', int32Tag = '[object Int32Array]', uint8Tag = '[object Uint8Array]', uint8ClampedTag = '[object Uint8ClampedArray]', uint16Tag = '[object Uint16Array]', uint32Tag = '[object Uint32Array]'; /** Used to match property names within property paths. */ var reIsDeepProp = /\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/, reIsPlainProp = /^\w*$/, reLeadingDot = /^\./, rePropName = /[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|$))/g; /** * Used to match `RegExp` * [syntax characters](http://ecma-international.org/ecma-262/7.0/#sec-patterns). */ var reRegExpChar = /[\\^$.*+?()[\]{}|]/g; /** Used to match backslashes in property paths. */ var reEscapeChar = /\\(\\)?/g; /** Used to detect host constructors (Safari). */ var reIsHostCtor = /^\[object .+?Constructor\]$/; /** Used to detect unsigned integer values. */ var reIsUint = /^(?:0|[1-9]\d*)$/; /** Used to identify `toStringTag` values of typed arrays. */ var typedArrayTags = {}; typedArrayTags[float32Tag] = typedArrayTags[float64Tag] = typedArrayTags[int8Tag] = typedArrayTags[int16Tag] = typedArrayTags[int32Tag] = typedArrayTags[uint8Tag] = typedArrayTags[uint8ClampedTag] = typedArrayTags[uint16Tag] = typedArrayTags[uint32Tag] = true; typedArrayTags[argsTag] = typedArrayTags[arrayTag] = typedArrayTags[arrayBufferTag] = typedArrayTags[boolTag] = typedArrayTags[dataViewTag] = typedArrayTags[dateTag] = typedArrayTags[errorTag] = typedArrayTags[funcTag] = typedArrayTags[mapTag] = typedArrayTags[numberTag] = typedArrayTags[objectTag] = typedArrayTags[regexpTag] = typedArrayTags[setTag] = typedArrayTags[stringTag] = typedArrayTags[weakMapTag] = false; /** Detect free variable `global` from Node.js. */ var freeGlobal = typeof global == 'object' && global && global.Object === Object && global; /** Detect free variable `self`. */ var freeSelf = typeof self == 'object' && self && self.Object === Object && self; /** Used as a reference to the global object. */ var root = freeGlobal || freeSelf || Function('return this')(); /** Detect free variable `exports`. */ var freeExports = typeof exports == 'object' && exports && !exports.nodeType && exports; /** Detect free variable `module`. */ var freeModule = freeExports && typeof module == 'object' && module && !module.nodeType && module; /** Detect the popular CommonJS extension `module.exports`. */ var moduleExports = freeModule && freeModule.exports === freeExports; /** Detect free variable `process` from Node.js. */ var freeProcess = moduleExports && freeGlobal.process; /** Used to access faster Node.js helpers. */ var nodeUtil = (function() { try { return freeProcess && freeProcess.binding('util'); } catch (e) {} }()); /* Node.js helper references. */ var nodeIsTypedArray = nodeUtil && nodeUtil.isTypedArray; /** * A faster alternative to `Function#apply`, this function invokes `func` * with the `this` binding of `thisArg` and the arguments of `args`. * * @private * @param {Function} func The function to invoke. * @param {*} thisArg The `this` binding of `func`. * @param {Array} args The arguments to invoke `func` with. * @returns {*} Returns the result of `func`. */ //一个速度更快的apply方法 function apply(func, thisArg, args) { switch (args.length) { case 0: return func.call(thisArg); case 1: return func.call(thisArg, args[0]); case 2: return func.call(thisArg, args[0], args[1]); case 3: return func.call(thisArg, args[0], args[1], args[2]); } return func.apply(thisArg, args); } /** * A specialized version of `_.includes` for arrays without support for * specifying an index to search from. * * @private * @param {Array} [array] The array to inspect. * @param {*} target The value to search for. * @returns {boolean} Returns `true` if `target` is found, else `false`. */ //判断数组是否包含给定值 function arrayIncludes(array, value) { var length = array ? array.length : 0;//数组长度 return !!length && baseIndexOf(array, value, 0) > -1;//调用baseIndexOf判断array是否包含value } /** * This function is like `arrayIncludes` except that it accepts a comparator. * * @private * @param {Array} [array] The array to inspect. * @param {*} target The value to search for. * @param {Function} comparator The comparator invoked per element. * @returns {boolean} Returns `true` if `target` is found, else `false`. */ //类似于数组的includes方法,区别是它的comparator需要作为参数传入 function arrayIncludesWith(array, value, comparator) { var index = -1,//循环索引 length = array ? array.length : 0;//数组长度 while (++index < length) {//循环数组,用comparator比较当前值和target,找到返回true,否则false if (comparator(value, array[index])) { return true; } } return false; } /** * A specialized version of `_.map` for arrays without support for iteratee * shorthands. * * @private * @param {Array} [array] The array to iterate over. * @param {Function} iteratee The function invoked per iteration. * @returns {Array} Returns the new mapped array. */ //_.map基础实现 function arrayMap(array, iteratee) { var index = -1,//循环索引 length = array ? array.length : 0,//数组长度 result = Array(length);//结果数组 while (++index < length) {//循环调用回调函数迭代器 result[index] = iteratee(array[index], index, array); } return result; } /** * Appends the elements of `values` to `array`. * * @private * @param {Array} array The array to modify. * @param {Array} values The values to append. * @returns {Array} Returns `array`. */ //将values的元素添加到array的后面 function arrayPush(array, values) { var index = -1,//循环索引 length = values.length,//添加的元素数组 offset = array.length;//开始添加的偏移量,就是array的长度s while (++index < length) {//循环values数组,将其元素添加入array的后面 array[offset + index] = values[index]; } return array; } /** * A specialized version of `_.some` for arrays without support for iteratee * shorthands. * * @private * @param {Array} [array] The array to iterate over. * @param {Function} predicate The function invoked per iteration. * @returns {boolean} Returns `true` if any element passes the predicate check, * else `false`. */ //类似于数组的some方法 function arraySome(array, predicate) { var index = -1,//循环索引 length = array ? array.length : 0;//数组长度 while (++index < length) {//循环调用传入的回调函数,参数是值,值的索引,数组本身 if (predicate(array[index], index, array)) { return true; } }//如果有一个返回true,就返回true,否则,返回false return false; } /** * The base implementation of `_.findIndex` and `_.findLastIndex` without * support for iteratee shorthands. * * @private * @param {Array} array The array to inspect. * @param {Function} predicate The function invoked per iteration. * @param {number} fromIndex The index to search from. * @param {boolean} [fromRight] Specify iterating from right to left. * @returns {number} Returns the index of the matched value, else `-1`. */ //找到数组中指定元素的位置 //array查找的数组,predicate每次循环调用的函数,fromIndex遍历的起始位置,fromRight是否反向查找 function baseFindIndex(array, predicate, fromIndex, fromRight) { var length = array.length,//数组长度 index = fromIndex + (fromRight ? 1 : -1);//循环索引,如果反向循环就加1,如果正向就减1 while ((fromRight ? index-- : ++index < length)) { if (predicate(array[index], index, array)) {//调用predicate,根据其返回的真假值,如果返回true说明找到了 return index; } } return -1;//找不到返回-1 } /** * The base implementation of `_.indexOf` without `fromIndex` bounds checks. * * @private * @param {Array} array The array to inspect. * @param {*} value The value to search for. * @param {number} fromIndex The index to search from. * @returns {number} Returns the index of the matched value, else `-1`. */ //寻找数组中指定值的位置,找不到返回-1,fromIndex是查找起始位置 function baseIndexOf(array, value, fromIndex) { if (value !== value) {//如果value是NaN就调用baseFindIndex查找,传入的回调函数是自己实现的isNaN return baseFindIndex(array, baseIsNaN, fromIndex); } var index = fromIndex - 1,//循环索引 length = array.length;//数组长度 while (++index < length) {//如果当前值严格等于value,就返回index索引 if (array[index] === value) { return index; } } return -1;//找不到返回-1 } /** * The base implementation of `_.isNaN` without support for number objects. * * @private * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is `NaN`, else `false`. */ //判断一个之是不是NaN function baseIsNaN(value) { return value !== value; } /** * The base implementation of `_.property` without support for deep paths. * * @private * @param {string} key The key of the property to get. * @returns {Function} Returns the new accessor function. */ //返回一个方法,这个方法返回object的对应key的对应value function baseProperty(key) { return function(object) { return object == null ? undefined : object[key]; }; } /** * The base implementation of `_.times` without support for iteratee shorthands * or max array length checks. * * @private * @param {number} n The number of times to invoke `iteratee`. * @param {Function} iteratee The function invoked per iteration. * @returns {Array} Returns the array of results. */ //_times的基础实现,返回一个长度为n的数组,元素值是遍历器处理过的index //n是循环调用遍历器的次数,iteratee是遍历器 function baseTimes(n, iteratee) { var index = -1,//循环索引 result = Array(n);//结果数组 while (++index < n) { result[index] = iteratee(index); } return result; } /** * The base implementation of `_.unary` without support for storing metadata. * * @private * @param {Function} func The function to cap arguments for. * @returns {Function} Returns the new capped function. */ //_.unary的基础实现,创建一个函数只接受一个参数,忽略之后所有参数 function baseUnary(func) { return function(value) { return func(value); }; } /** * Checks if a cache value for `key` exists. * * @private * @param {Object} cache The cache to query. * @param {string} key The key of the entry to check. * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`. */ //判断cache中是否有给定key function cacheHas(cache, key) { return cache.has(key); } /** * Gets the value at `key` of `object`. * * @private * @param {Object} [object] The object to query. * @param {string} key The key of the property to get. * @returns {*} Returns the property value. */ function getValue(object, key) { return object == null ? undefined : object[key]; } /** * Checks if `value` is a host object in IE < 9. * * @private * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is a host object, else `false`. */ //检查一个值是否是宿主对象,在小于IE9的浏览器情况下,例如BOM和DOM对象 function isHostObject(value) { // Many host objects are `Object` objects that can coerce to strings // despite having improperly defined `toString` methods. //很多宿主对象可以强制转换成字符串经管它们的toString方法定义并不正确 var result = false; if (value != null && typeof value.toString != 'function') { try { result = !!(value + ''); } catch (e) {} } return result; } /** * Converts `map` to its key-value pairs. * * @private * @param {Object} map The map to convert. * @returns {Array} Returns the key-value pairs. */ //将自己实现的兼容式map数据类型转变成key-value形式 function mapToArray(map) { var index = -1,//循环索引 result = Array(map.size);//结果数组 map.forEach(function(value, key) {//循环赋值 /* {key1: value1, key2: value2, ...} 变成: [[key1, value1], [key2, value2], ...] */ result[++index] = [key, value]; }); return result; } /** * Creates a unary function that invokes `func` with its argument transformed. * * @private * @param {Function} func The function to wrap. * @param {Function} transform The argument transform. * @returns {Function} Returns the new function. */ //创建一个一元方法,来处理函数的参数 function overArg(func, transform) { return function(arg) { return func(transform(arg)); }; } /** * Converts `set` to an array of its values. * * @private * @param {Object} set The set to convert. * @returns {Array} Returns the values. */ //将set类型转变成数组 function setToArray(set) { var index = -1,//循环索引 result = Array(set.size);//结果数组 set.forEach(function(value) {//循环赋值 result[++index] = value; }); return result; } /** Used for built-in method references. */ var arrayProto = Array.prototype, funcProto = Function.prototype, objectProto = Object.prototype; /** Used to detect overreaching core-js shims. */ var coreJsData = root['__core-js_shared__']; /** Used to detect methods masquerading as native. */ var maskSrcKey = (function() { var uid = /[^.]+$/.exec(coreJsData && coreJsData.keys && coreJsData.keys.IE_PROTO || ''); return uid ? ('Symbol(src)_1.' + uid) : ''; }()); /** Used to resolve the decompiled source of functions. */ var funcToString = funcProto.toString; /** Used to check objects for own properties. */ var hasOwnProperty = objectProto.hasOwnProperty; /** * Used to resolve the * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring) * of values. */ var objectToString = objectProto.toString; /** Used to detect if a method is native. */ var reIsNative = RegExp('^' + funcToString.call(hasOwnProperty).replace(reRegExpChar, '\\$&') .replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g, '$1.*?') + '$' ); /** Built-in value references. */ var Symbol = root.Symbol, Uint8Array = root.Uint8Array, propertyIsEnumerable = objectProto.propertyIsEnumerable, splice = arrayProto.splice, spreadableSymbol = Symbol ? Symbol.isConcatSpreadable : undefined; /* Built-in method references for those with the same name as other `lodash` methods. */ var nativeKeys = overArg(Object.keys, Object), nativeMax = Math.max; /* Built-in method references that are verified to be native. */ var DataView = getNative(root, 'DataView'), Map = getNative(root, 'Map'), Promise = getNative(root, 'Promise'), Set = getNative(root, 'Set'), WeakMap = getNative(root, 'WeakMap'), nativeCreate = getNative(Object, 'create'); /** Used to detect maps, sets, and weakmaps. */ var dataViewCtorString = toSource(DataView), mapCtorString = toSource(Map), promiseCtorString = toSource(Promise), setCtorString = toSource(Set), weakMapCtorString = toSource(WeakMap); /** Used to convert symbols to primitives and strings. */ var symbolProto = Symbol ? Symbol.prototype : undefined, symbolValueOf = symbolProto ? symbolProto.valueOf : undefined, symbolToString = symbolProto ? symbolProto.toString : undefined; /** * Creates a hash object. * * @private * @constructor * @param {Array} [entries] The key-value pairs to cache. */ //Hash类构造函数,接收参数是一个包含键值对数组的数组: //[['key1', 'values1'], ['key2', 'values2']......] function Hash(entries) { var index = -1,//循环索引 length = entries ? entries.length : 0;//retries长度 this.clear();//初始化 while (++index < length) {//循环并设置数据到__data__属性里 var entry = entries[index]; this.set(entry[0], entry[1]); } } /** * Removes all key-value entries from the hash. * * @private * @name clear * @memberOf Hash */ //初始化Hash实例__data__属性 function hashClear() { this.__data__ = nativeCreate ? nativeCreate(null) : {}; //Object.create({}) } /** * Removes `key` and its value from the hash. * * @private * @name delete * @memberOf Hash * @param {Object} hash The hash to modify. * @param {string} key The key of the value to remove. * @returns {boolean} Returns `true` if the entry was removed, else `false`. */ //从Hash实例上删除指定key function hashDelete(key) { return this.has(key) && delete this.__data__[key]; } /** * Gets the hash value for `key`. * * @private * @name get * @memberOf Hash * @param {string} key The key of the value to get. * @returns {*} Returns the entry value. */ //获取Hash实例上指定key对应值 function hashGet(key) { var data = this.__data__; if (nativeCreate) { var result = data[key]; return result === HASH_UNDEFINED ? undefined : result; } return hasOwnProperty.call(data, key) ? data[key] : undefined; } /** * Checks if a hash value for `key` exists. * * @private * @name has * @memberOf Hash * @param {string} key The key of the entry to check. * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`. */ //判断Hash实例上是否有指定key function hashHas(key) { var data = this.__data__; return nativeCreate ? data[key] !== undefined : hasOwnProperty.call(data, key); } /** * Sets the hash `key` to `value`. * * @private * @name set * @memberOf Hash * @param {string} key The key of the value to set. * @param {*} value The value to set. * @returns {Object} Returns the hash instance. */ //Hash实例设置新值到__data__ function hashSet(key, value) { var data = this.__data__; data[key] = (nativeCreate && value === undefined) ? HASH_UNDEFINED : value; return this; } // Add methods to `Hash`. //以下都是Hash实例的方法 Hash.prototype.clear = hashClear; Hash.prototype['delete'] = hashDelete; Hash.prototype.get = hashGet; Hash.prototype.has = hashHas; Hash.prototype.set = hashSet; /** * Creates an list cache object. * * @private * @constructor * @param {Array} [entries] The key-value pairs to cache. */ //ListCache构造函数,ListCache其实就是一个自己实现的Map数据类型 //参数entries是键值对数组,结构如下 /* [ [key1, value1], [key2, value2], ... ] */ function ListCache(entries) { var index = -1,//循环索引 length = entries ? entries.length : 0;//entries长度 this.clear();//初始化实例上的__data__属性 while (++index < length) {//循环参数,给__data__上存储键值对 var entry = entries[index]; this.set(entry[0], entry[1]); } } /** * Removes all key-value entries from the list cache. * * @private * @name clear * @memberOf ListCache */ //初始化ListCache实例__data__ function listCacheClear() { this.__data__ = []; } /** * Removes `key` and its value from the list cache. * * @private * @name delete * @memberOf ListCache * @param {string} key The key of the value to remove. * @returns {boolean} Returns `true` if the entry was removed, else `false`. */ //从ListCache实例上删除指定key function listCacheDelete(key) { var data = this.__data__, index = assocIndexOf(data, key); if (index < 0) { return false; } var lastIndex = data.length - 1; if (index == lastIndex) { data.pop(); } else { splice.call(data, index, 1); } return true; } /** * Gets the list cache value for `key`. * * @private * @name get * @memberOf ListCache * @param {string} key The key of the value to get. * @returns {*} Returns the entry value. */ //从ListCache实例上获取指定key对应值 function listCacheGet(key) { var data = this.__data__, index = assocIndexOf(data, key); return index < 0 ? undefined : data[index][1]; } /** * Checks if a list cache value for `key` exists. * * @private * @name has * @memberOf ListCache * @param {string} key The key of the entry to check. * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`. */ //判断ListCache实例上是否存在指定key function listCacheHas(key) { return assocIndexOf(this.__data__, key) > -1; } /** * Sets the list cache `key` to `value`. * * @private * @name set * @memberOf ListCache * @param {string} key The key of the value to set. * @param {*} value The value to set. * @returns {Object} Returns the list cache instance. */ //在ListCache实例上添加指定key function listCacheSet(key, value) { var data = this.__data__, index = assocIndexOf(data, key);//在__data__上寻找有没有重复的key,如果没有就返回-1,如果有就返回索引 if (index < 0) {//如果没有重复的key,__data__存入键值对 data.push([key, value]); } else {//如果有重复的key,就改变对应的value data[index][1] = value; } return this; } //以下都是ListCache实例方法 // Add methods to `ListCache`. ListCache.prototype.clear = listCacheClear; ListCache.prototype['delete'] = listCacheDelete; ListCache.prototype.get = listCacheGet; ListCache.prototype.has = listCacheHas; ListCache.prototype.set = listCacheSet; /** * Creates a map cache object to store key-value pairs. * * @private * @constructor * @param {Array} [entries] The key-value pairs to cache. */ //MapCache类型构造函数,创建一个map缓存对象来储存键值对 //entries参数是键值对数组 function MapCache(entries) { var index = -1,//循环参数 length = entries ? entries.length : 0;//entries长度 this.clear();//初始化实例上的size属性和__data__属性 while (++index < length) {//循环键值对数组,并调用this.prototype.set方法 var entry = entries[index]; this.set(entry[0], entry[1]); } } /* SetCacheObject { __data__: { size: xxx, __data__: { hash: new Hash, map: new Map, string: new Hash } } } => { __data__: { size: xxx, __data__: { hash: { size: xxx, __data__: { values4: '__lodash_hash_undefined__', values6: '__lodash_hash_undefined__', ... } }, map: new Map, string: { size: xxx, __data__: { values3: '__lodash_hash_undefined__', values8: '__lodash_hash_undefined__', ... } } } } } */ /** * Removes all key-value entries from the map. * * @private * @name clear * @memberOf MapCache */ //清除当前MapCache实例上所有键值对 function mapCacheClear() { this.__data__ = { 'hash': new Hash, 'map': new (Map || ListCache), 'string': new Hash }; } /** * Removes `key` and its value from the map. * * @private * @name delete * @memberOf MapCache * @param {string} key The key of the value to remove. * @returns {boolean} Returns `true` if the entry was removed, else `false`. */ //从MapCache实例上移除指定key function mapCacheDelete(key) { return getMapData(this, key)['delete'](key); } /** * Gets the map value for `key`. * * @private * @name get * @memberOf MapCache * @param {string} key The key of the value to get. * @returns {*} Returns the entry value. */ //从MapCache实例上获取指定key的值 function mapCacheGet(key) { return getMapData(this, key).get(key); } /** * Checks if a map value for `key` exists. * * @private * @name has * @memberOf MapCache * @param {string} key The key of the entry to check. * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`. */ //判断MapCache实例上是否存在指定key function mapCacheHas(key) { return getMapData(this, key).has(key); } /** * Sets the map `key` to `value`. * * @private * @name set * @memberOf MapCache * @param {string} key The key of the value to set. * @param {*} value The value to set. * @returns {Object} Returns the map cache instance. */ function mapCacheSet(key, value) { //根据key的数据类型获取map里对应的数据集合,要么是Map对象,要么是ListCache对象,要么是Hash对象 //调用数据集合对象上的set方法,设置键值对到上面 getMapData(this, key).set(key, value); return this; } // Add methods to `MapCache`. MapCache.prototype.clear = mapCacheClear;//MapCache原型上的clear方法 MapCache.prototype['delete'] = mapCacheDelete;//MapCache原型上的delete方法 MapCache.prototype.get = mapCacheGet;//MapCache原型上的get方法 MapCache.prototype.has = mapCacheHas;//MapCache原型上的has方法 MapCache.prototype.set = mapCacheSet;//MapCache原型上的set方法 /** * * Creates an array cache object to store unique values. * * @private * @constructor * @param {Array} [values] The values to cache. */ //SetCachel构造函数,为一个数组创建缓存加快查找速度 function SetCache(values) {//values参数为一个数组,将为它设置缓存 var index = -1,//循环变量 length = values ? values.length : 0;//values数组长度 this.__data__ = new MapCache;//为SetCache新实例添加__data__属性,__data__里存放一个MapCache实例 while (++index < length) { this.add(values[index]);//根据每个值调用this.prototype.add方法 } } /* SetCache类型 values [value1, value2......] new SetCache(values) SetCacheObject { __data__: { value1: '__lodash_hash_undefined__', value2: '__lodash_hash_undefined__', ...... } } */ /** * Adds `value` to the array cache. * * @private * @name add * @memberOf SetCache * @alias push * @param {*} value The value to cache. * @returns {Object} Returns the cache instance. */ //this.prototype.add,调用当前SetCache实例上的MapCache实例的set方法,将值添加入缓存里 //MapCache上设置的值类型是键值对,这里调用,value是key,HASH_UNDEFINED是value function setCacheAdd(value) { this.__data__.set(value, HASH_UNDEFINED); return this; } /** * Checks if `value` is in the array cache. * * @private * @name has * @memberOf SetCache * @param {*} value The value to search for. * @returns {number} Returns `true` if `value` is found, else `false`. */ //this.prototype.has,调用当前SetCache实例上的MapCache实例的has方法,判断当前缓存里有没有这个值 function setCacheHas(value) { return this.__data__.has(value); } // Add methods to `SetCache`. SetCache.prototype.add = SetCache.prototype.push = setCacheAdd;//为SetCache原型添加add方法 SetCache.prototype.has = setCacheHas;//为SetCache原型添加has方法 /** * Creates a stack cache object to store key-value pairs. * * @private * @constructor * @param {Array} [entries] The key-value pairs to cache. */ function Stack(entries) { this.__data__ = new ListCache(entries); } /** * Removes all key-value entries from the stack. * * @private * @name clear * @memberOf Stack */ function stackClear() { this.__data__ = new ListCache; } /** * Removes `key` and its value from the stack. * * @private * @name delete * @memberOf Stack * @param {string} key The key of the value to remove. * @returns {boolean} Returns `true` if the entry was removed, else `false`. */ function stackDelete(key) { return this.__data__['delete'](key); } /** * Gets the stack value for `key`. * * @private * @name get * @memberOf Stack * @param {string} key The key of the value to get. * @returns {*} Returns the entry value. */ function stackGet(key) { return this.__data__.get(key); } /** * Checks if a stack value for `key` exists. * * @private * @name has * @memberOf Stack * @param {string} key The key of the entry to check. * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`. */ function stackHas(key) { return this.__data__.has(key); } /** * Sets the stack `key` to `value`. * * @private * @name set * @memberOf Stack * @param {string} key The key of the value to set. * @param {*} value The value to set. * @returns {Object} Returns the stack cache instance. */ function stackSet(key, value) { var cache = this.__data__; if (cache instanceof ListCache) { var pairs = cache.__data__; if (!Map || (pairs.length < LARGE_ARRAY_SIZE - 1)) { pairs.push([key, value]); return this; } cache = this.__data__ = new MapCache(pairs); } cache.set(key, value); return this; } // Add methods to `Stack`. Stack.prototype.clear = stackClear; Stack.prototype['delete'] = stackDelete; Stack.prototype.get = stackGet; Stack.prototype.has = stackHas; Stack.prototype.set = stackSet; /** * Creates an array of the enumerable property names of the array-like `value`. * * @private * @param {*} value The value to query. * @param {boolean} inherited Specify returning inherited property names. * @returns {Array} Returns the array of property names. */ //创建一个包含一个array-like对象的所有自身可枚举属性的key的数组 function arrayLikeKeys(value, inherited) { // Safari 8.1 makes `arguments.callee` enumerable in strict mode. // Safari 9 makes `arguments.length` enumerable in strict mode. var result = (isArray(value) || isArguments(value)) ? baseTimes(value.length, String) : []; //结果数组,如果value是array或者arguments对象,调用baseTimes返回一个结果数组,元素是String化的索引值 var length = result.length,//结果数组长度 skipIndexes = !!length; for (var key in value) {//循环value对象 if ((inherited || hasOwnProperty.call(value, key)) && !(skipIndexes && (key == 'length' || isIndex(key, length)))) { //如果除了索引属性和length属性之外还有其他属性,加入结果数组 result.push(key); } } return result; } /** * Gets the index at which the `key` is found in `array` of key-value pairs. * * @private * @param {Array} array The array to inspect. * @param {*} key The key to search for. * @returns {number} Returns the index of the matched value, else `-1`. */ //寻找键值对数组中key对应的数据索引 function assocIndexOf(array, key) { var length = array.length;//数组长度 while (length--) {//循环寻找键值对对应的索引,找到就返回,没找到就返回-1 if (eq(array[length][0], key)) { return length; } } return -1; } /** * The base implementation of methods like `_.difference` without support * for excluding multiple arrays or iteratee shorthands. * * @private * @param {Array} array The array to inspect. * @param {Array} values The values to exclude. * @param {Function} [iteratee] The iteratee invoked per element. * @param {Function} [comparator] The comparator invoked per element. * @returns {Array} Returns the new array of filtered values. */ //_.difference基础实现 //array,用于检查的数组 //values,用于排除元素的数组 //iteratee,迭代器,类型是function,循环会调用 //comparator,比较器,类型function,循环会调用 function baseDifference(array, values, iteratee, comparator) { var index = -1,//循环索引 includes = arrayIncludes,//arrayIncludes方法,判断数组是否包含给定值,类似原生的includes isCommon = true,//isCommon用来标记是否要使用普通的比较方式 length = array.length,//数组长度 result = [],//结果数组 valuesLength = values.length;//用于排除的数组的长度 if (!length) {//如果array无长度,返回空数组 return result; } if (iteratee) {//如果传递了迭代器参数,就循环values,对每个值运行迭代器,生成新的values values = arrayMap(values, baseUnary(iteratee)); } if (comparator) {//如果传递了比较器参数,就是用arrayIncludesWith方法 includes = arrayIncludesWith; isCommon = false; } else if (values.length >= LARGE_ARRAY_SIZE) { //如果没有传递自定义includes方法,且values数组的长度超过200,indludes方法就换成cacheHas,启用cache以达到性能优化的效果 includes = cacheHas; isCommon = false; values = new SetCache(values); } outer: //label语句,将下面的for循环标记,continue会跳过本次循环,继续走下一次外层循环 while (++index < length) {//循环array var value = array[index],//array当前值 computed = iteratee ? iteratee(value) : value; //如果有iteratee参数,就把array数组的循环当前值value传入iteratee,返回值存为computed value = (comparator || value !== 0) ? value : 0; if (isCommon && computed === computed) { //排除array当前循环值是否是NaN的情况,isCommon用来标记是否要使用普通的比较方式 var valuesIndex = valuesLength;//values数组循环索引 while (valuesIndex--) {//倒序循环values数组 if (values[valuesIndex] === computed) { //循环values,如果发现values里有值和当前array循环元素相等,直接跳出values循环,循环下一次array continue outer; } } result.push(value);//values循环结束没有发现有相等的值,就push入结果数组 } else if (!includes(values, computed, comparator)) { //如果当前array循环值是NaN或者需要使用特殊比较方法 //调用includes判断values中有没有和当前array循环值相等的 result.push(value); } } return result; } /** * The base implementation of `_.flatten` with support for restricting flattening. * * @private * @param {Array} array The array to flatten. * @param {number} depth The maximum recursion depth. * @param {boolean} [predicate=isFlattenable] The function invoked per iteration. * @param {boolean} [isStrict] Restrict to values that pass `predicate` checks. * @param {Array} [result=[]] The initial result value. * @returns {Array} Returns the new flattened array. */ //这个方法用于数组展开 //array需要展开操作的数组,depth需要展开的层数 //predicate用于判断值是否可展开 //isStrict标识用于判断是否约束值必须通过predicate方法的检查 function baseFlatten(array, depth, predicate, isStrict, result) { var index = -1,//循环索引 length = array.length;//数组长度 predicate || (predicate = isFlattenable);//predicate每次循环都会调用,用来判断当前值是否是一个可展开的 result || (result = []); while (++index < length) { var value = array[index];//当前循环的元素值 if (depth > 0 && predicate(value)) {//如果展开层数大于0且当前循环值可展开 if (depth > 1) {//如果展开层数大于一层就继续递归调用,层数减一 // Recursively flatten arrays (susceptible to call stack limits). baseFlatten(value, depth - 1, predicate, isStrict, result); } else {//如果只展开一层,就展开后push到result里 arrayPush(result, value); } } else if (!isStrict) {//如果没有传递isStrict标识,就直接将当前循环值push入结果数组 result[result.length] = value; } } return result; } /** * The base implementation of `_.get` without support for default values. * * @private * @param {Object} object The object to query. * @param {Array|string} path The path of the property to get. * @returns {*} Returns the resolved value. */ //根据path获取object上的属性值 //object需要获取属性值的对象 //path属性所在的路径 function baseGet(object, path) { path = isKey(path, object) ? [path] : castPath(path); //判断path是不是合法属性名,如果是就存成[path],如果不是就调用castPath方法生成path的数组 var index = 0,//循环索引 length = path.length;//path数字的长度 while (object != null && index < length) {//获取深层的值 object = object[toKey(path[index++])]; } return (index && index == length) ? object : undefined;//找到就返回,没找到返回undefined } /** * The base implementation of `getTag`. * * @private * @param {*} value The value to query. * @returns {string} Returns the `toStringTag`. */ //调用Object.prototype.toString获得数据的类型tag function baseGetTag(value) { return objectToString.call(value); } /** * The base implementation of `_.hasIn` without support for deep paths. * * @private * @param {Object} [object] The object to query. * @param {Array|string} key The key to check. * @returns {boolean} Returns `true` if `key` exists, else `false`. */ //object是否存在path路径的浅层实现,不考虑深层path路径 function baseHasIn(object, key) {//直接用in操作符判断 return object != null && key in Object(object); } /** * The base implementation of `_.isEqual` which supports partial comparisons * and tracks traversed objects. * * @private * @param {*} value The value to compare. * @param {*} other The other value to compare. * @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. * @returns {boolean} Returns `true` if the values are equivalent, else `false`. */ //_.isEqual的基础实现,深度比较两个对象是否是相等的 //value要比较的对象,other要比较的另一个对象,customizer自定义比较方法 //bitmask为1说明是无序的比较,为2说明是部分比较 //stack追踪传入的参数 function baseIsEqual(value, other, customizer, bitmask, stack) { if (value === other) {//如果value和other严格相等,直接返回true return true; } if (value == null || other == null || (!isObject(value) && !isObjectLike(other))) { //判断有值为空或者NaN,反正不是数组或对象的情况的情况 return value !== value && other !== other; } return baseIsEqualDeep(value, other, baseIsEqual, customizer, bitmask, stack); //调用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),//判断object是不是数组 othIsArr = isArray(other),//判断other是不是数组 objTag = arrayTag,//'[object Array]' othTag = arrayTag;//'[object Array]' if (!objIsArr) { objTag = getTag(object);//如果object不是数组,就获取object的数据类型tag objTag = objTag == argsTag ? objectTag : objTag; } if (!othIsArr) { othTag = getTag(other);//如果other不是数组,就获取other的数据类型tag othTag = othTag == argsTag ? objectTag : othTag; } var objIsObj = objTag == objectTag && !isHostObject(object),//object是对象类型且不是宿主对象 othIsObj = othTag == objectTag && !isHostObject(other),//other是对象类型且不是宿主对象 isSameTag = objTag == othTag;//object和other的类型tag一样 if (isSameTag && !objIsObj) {//object和other的tag相等且它们不是本地对象类型 stack || (stack = new Stack);//新建一个ListCache类型数据,ListCache其实就是一个自己实现的Map数据类型 return (objIsArr || isTypedArray(object)) ? equalArrays(object, other, equalFunc, customizer, bitmask, stack) : equalByTag(object, other, objTag, equalFunc, customizer, bitmask, stack); //如果都是数组类型或者typedArray类型就调用equalArrays方法判断 //否则调用equalByTag判断 } if (!(bitmask & PARTIAL_COMPARE_FLAG)) {//如果不是部分比较,且是object类型 //此处__warpped__属性看不明白??? 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);//递归调用baseIsEqual } } if (!isSameTag) {//如果toStringTag不一样,直接返回false return false; } stack || (stack = new Stack); return equalObjects(object, other, equalFunc, customizer, bitmask, stack); //其他情况使用equalObjects来判断 } /** * The base implementation of `_.isMatch` without support for iteratee shorthands. * * @private * @param {Object} object The object to inspect. * @param {Object} source The object of property values to match. * @param {Array} matchData The property names, values, and compare flags to match. * @param {Function} [customizer] The function to customize comparisons. * @returns {boolean} Returns `true` if `object` is a match, else `false`. */ //object,给定对象 //source,用来比较的源对象 //matchData,source参数的[key, value, boolean]的数组的形式,第三个布尔值表明当前值是否适合用===比较 //自定义比较函数 function baseIsMatch(object, source, matchData, customizer) { var index = matchData.length,//循环索引,从结尾开始循环 length = index,//source长度 noCustomizer = !customizer;//有没有传递自定义比较方法的标识 if (object == null) {//如果object为空,若source有属性,返回false,若source无属性,返回true return !length; } object = Object(object);//强制转换object while (index--) {//按source属性长度循环 var data = matchData[index];//当前matchData if ((noCustomizer && data[2]) ? data[1] !== object[data[0]] : !(data[0] in object) ) {//如果没有自定义比较方法且当前source属性值适合===判断,也就是说是简单类型数据,就直接用!==判断是否不相等 //否则用in来判断object是否不含有source key属性 return false; } } //上面循环先循环一遍判断适合使用===直接判断的属性,下面循环再循环一遍判断其他需要深度循环判断的属性 //上面循环结束后index循环索引变成0,现在再次正向循环 while (++index < length) { data = matchData[index];//当前matchData var key = data[0],//当前source key objValue = object[key],//当前object 对应source key 的值 srcValue = data[1];//当前source value if (noCustomizer && data[2]) {//如果没有自定义比较方法,且是简单类型,就判断objValue上是否不存在此属性 //如果不存在直接返回false if (objValue === undefined && !(key in object)) { return false; } } else { var stack = new Stack;//新实例化一个Stack对象 if (customizer) {//如果有自定义比较方法,就用自定义的比较 var result = customizer(objValue, srcValue, key, object, source, stack); } if (!(result === undefined ? baseIsEqual(srcValue, objValue, customizer, UNORDERED_COMPARE_FLAG | PARTIAL_COMPARE_FLAG, stack) : result )) {//如果没有自定义比较方法,就用baseIsEqual来比较 return false; } } } return true; } /** * The base implementation of `_.isNative` without bad shim checks. * * @private * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is a native function, * else `false`. */ function baseIsNative(value) { if (!isObject(value) || isMasked(value)) { return false; } var pattern = (isFunction(value) || isHostObject(value)) ? reIsNative : reIsHostCtor; return pattern.test(toSource(value)); } /** * The base implementation of `_.isTypedArray` without Node.js optimizations. * * @private * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is a typed array, else `false`. */ //_.isTypedArray基础实现,不使用nodejs优化的方法 function baseIsTypedArray(value) { return isObjectLike(value) && isLength(value.length) && !!typedArrayTags[objectToString.call(value)]; } /** * The base implementation of `_.iteratee`. * * @private * @param {*} [value=_.identity] The value to convert to an iteratee. * @returns {Function} Returns the iteratee. */ //_.iteratee的基础实现,把一个值转换成一个迭代器函数 function baseIteratee(value) { // Don't store the `typeof` result in a variable to avoid a JIT bug in Safari 9. // See https://bugs.webkit.org/show_bug.cgi?id=156034 for more details. if (typeof value == 'function') {//如果value本来就是function,直接返回 return value; } if (value == null) {//如果value是空,迭代器就使用事先定义好的identity方法 //identity方法返回原值,不做操作 return identity; } if (typeof value == 'object') {//如果value是对象 return isArray(value) ? baseMatchesProperty(value[0], value[1]) : baseMatches(value); //如果value是一个数组,调用baseMatchesProperty,则value[0]是path,获取对象上的属性的路径;value[1]是srcValue,和之后传入参数根据路径获取到的属性值比较用的值,baseMatchesProperty会返回一个方法,这个方法判断value[1]是否和传入的对象根据value[0]路径获取到的属性 是相等的,内部比较使用类似baseIsEqual的深层比较方法。相等返回true,否则false //如果value的类型不是一个数组,是一个对象,那么调用baseMatches,baseMatches创建一个方法,这个方法会使用部分的(partial)深度比较来比较给定的对象和source对象,如果给定对象拥有相等的属性值,就返回true,否则false。这里给baseMatches传入的value参数就是source,之后differenctBy循环的时候所有值都会和source比较 } return property(value); //如果value都不是以上情况,那就调用property,创建一个方法,这个方法可以根据给定path属性路径返回给定对象的属性值 } /** * The base implementation of `_.keys` which doesn't treat sparse arrays as dense. * * @private * @param {Object} object The object to query. * @returns {Array} Returns the array of property names. */ //_.keys的基础实现,将对象的可枚举属性的key组成一个数组返回 function baseKeys(object) { if (!isPrototype(object)) {//如果不是prototype对象,使用原生Object.keys方法 return nativeKeys(object); } var result = [];//结果数组 for (var key in Object(object)) { if (hasOwnProperty.call(object, key) && key != 'constructor') { //不等于constructor的自身属性push入结果数组 result.push(key); } } return result; } /** * The base implementation of `_.matches` which doesn't clone `source`. * * @private * @param {Object} source The object of property values to match. * @returns {Function} Returns the new spec function. */ //_.matches方法的基础实现 //创建一个方法,这个方法会使用部分的(partial)深度比较来比较给定的对象和source对象,如果给定对象拥有相等的属性值,就返回true,否则false function baseMatches(source) { var matchData = getMatchData(source); //getMatchData,把对象变成[key, value, boolean]的数组的形式,第三个布尔值表明当前值是否适合用===比较 if (matchData.length == 1 && matchData[0][2]) {//如果source对象只有一个属性 return matchesStrictComparable(matchData[0][0], matchData[0][1]); //matchesStrictComparable返回一个方法,这个方法用于比较传入对象object key值对应的值是否和给定值srcValue相等 //matchData[0][0]就是key,matchData[0][1]就是srcValue } return function(object) { //如果source有多个属性,那么就调用baseIsMatch return object === source || baseIsMatch(object, source, matchData); }; } /** * The base implementation of `_.matchesProperty` which doesn't clone `srcValue`. * * @private * @param {string} path The path of the property to get. * @param {*} srcValue The value to match. * @returns {Function} Returns the new spec function. */ //生成一个函数,这个函数可以深度比较对象的某个属性是否与给定值相等 //path参数是想要获取的属性的路径 //srcValue是用来匹配的值 function baseMatchesProperty(path, srcValue) { if (isKey(path) && isStrictComparable(srcValue)) { //如果path是属性名 并且 srcValue可以适用严格等于===来比较 return matchesStrictComparable(toKey(path), srcValue); //返回一个返回object[key] === srcValue判断结果的函数 } return function(object) { var objValue = get(object, path);//根据path获取object上对应健的值 return (objValue === undefined && objValue === srcValue) ? hasIn(object, path) : baseIsEqual(srcValue, objValue, undefined, UNORDERED_COMPARE_FLAG | PARTIAL_COMPARE_FLAG); //返回布尔值,判断根据path获取到的值和给定值是否相等 //如果获取到的值和用来比较的值都是undefined,那就object中是否存在这个path路径 //hasIn判断path路径在给定object中是否存在 //否则用baseIsEqual判断 }; } /** * A specialized version of `baseProperty` which supports deep paths. * * @private * @param {Array|string} path The path of the property to get. * @returns {Function} Returns the new accessor function. */ //返回一个方法,这个方法根据path返回object的对应属性值 function basePropertyDeep(path) { return function(object) { return baseGet(object, path); }; } /** * The base implementation of `_.rest` which doesn't validate or coerce arguments. * * @private * @param {Function} func The function to apply a rest parameter to. * @param {number} [start=func.length-1] The start position of the rest parameter. * @returns {Function} Returns the new function. */ //_.rest方法的基础实现,为函数实现ES6的rest参数 //rest参数的起始位置,默认是形参个数减去1,func.length - 1 function baseRest(func, start) { start = nativeMax(start === undefined ? (func.length - 1) : start, 0); //处理rest参数的起始位置,如果没传递start,就用函数形参个数减一,如果传递了,就在start和0之间选一个最大值 return function() { var args = arguments,//函数接收到的参数数组 index = -1,//循环索引 length = nativeMax(args.length - start, 0),//rest参数的长度 array = Array(length);//创建一个长度为rest参数长度的数组 while (++index < length) {//循环rest参数长度,将rest参数全部存入array变量中 array[index] = args[start + index]; } index = -1;//循环参数 var otherArgs = Array(start + 1);//创建一个数组存放除了rest参数以外的其他参数 while (++index < start) {//循环获取其他参数存入otherArgs中 otherArgs[index] = args[index]; } otherArgs[start] = array;//otherArgs的最后一个参数就是rest参数数组 return apply(func, this, otherArgs);//调用func,参数已处理成带有rest参数的形式 }; } /** * The base implementation of `_.toString` which doesn't convert nullish * values to empty strings. * * @private * @param {*} value The value to process. * @returns {string} Returns the string. */ //转换一个值为字符串,不会处理空值 function baseToString(value) { // Exit early for strings to avoid a performance hit in some environments. if (typeof value == 'string') {//如果已经是字符串,直接返回 return value; } if (isSymbol(value)) {//如果是Symbol对象,调用Symbol.prototype.toString处理 return symbolToString ? symbolToString.call(value) : ''; } var result = (value + '');//隐式转换 return (result == '0' && (1 / value) == -INFINITY) ? '-0' : result;//处理-0的情况 } /** * Casts `value` to a path array if it's not one. * * @private * @param {*} value The value to inspect. * @returns {Array} Returns the cast property path array. */ //把一个.分隔的path值转换成一个path数组 function castPath(value) { return isArray(value) ? value : stringToPath(value); //如果path值已经是数组了,直接返回;如果不是调用stringToPath方法转换 } /** * A specialized version of `baseIsEqualDeep` for arrays with support for * partial deep comparisons. * * @private * @param {Array} array The array to compare. * @param {Array} other The other array 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 `array` and `other` objects. * @returns {boolean} Returns `true` if the arrays are equivalent, else `false`. */ //深度比较数组是否相等 //partical部分比较的意思就是,other的长度可以比array长,other在包含array所有元素的情况下,还可以有自己的独特于array的元素 function equalArrays(array, other, equalFunc, customizer, bitmask, stack) { //bitmask内部使用的时候值只能是1或2,1代表无序比较,2代表部分比较 //bitmask按位与后,只有按位与操作符两边数字相等的情况下才会返回整数,否则返回0 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. //一开始先假设循环值都是相等的 //stack默认是listCache对象,是自己实现的Map数据类型 var stacked = stack.get(array);//获取stack上的array key对应的value if (stacked && stack.get(other)) {//如果stack key存在,就和other key比较 return stacked == other; } var index = -1,//循环索引 result = true,//一开始先假设循环值都是相等的 //如果是无序比较,就实例化一个SetCache对象 seen = (bitmask & UNORDERED_COMPARE_FLAG) ? new SetCache : undefined; stack.set(array, other);//给stack里存上array和other,交叉存 stack.set(other, array); // Ignore non-index properties. while (++index < arrLength) {//循环array的长度 var arrValue = array[index],//当前循环的array值 othValue = other[index];//当前循环的other值 if (customizer) {//如果提供了customizer比较方法,就用它来比较,partial部分比较要反过来传递array和other var compared = isPartial ? customizer(othValue, arrValue, index, other, array, stack) : customizer(arrValue, othValue, index, array, other, stack); } if (compared !== undefined) {//如果自定义比较有了结果,且为真,就continue继续下一次循环,否则result=false,跳出循环 if (compared) { continue; } result = false; break; } // Recursively compare arrays (susceptible to call stack limits). //递归地比较数组元素值是否相等 if (seen) {//如果创建了seen变量,说明是无序比较 //seen是SetCache对象,也是利用key-value形式存储值 //遍历other,如果有值和当前array的值相等,就存入seen中,并且arraySome返回true,否则返回false跳出循环 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上删除array和other属性 stack['delete'](other); return result; } /** * A specialized version of `baseIsEqualDeep` for comparing objects of * the same `toStringTag`. * * **Note:** This function only supports comparing values with tags of * `Boolean`, `Date`, `Error`, `Number`, `RegExp`, or `String`. * * @private * @param {Object} object The object to compare. * @param {Object} other The other object to compare. * @param {string} tag The `toStringTag` of the objects 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`. */ //比较toStringTag相等的两个对象是否相等 //object用来比较的对象 //other用来比较的另一个对象 //tag比较对象的toStringTag //equalFunc基础比较相等方法,用来递归调用 //customizer自定义比较方法 //bitmask是否无序比较或者部分比较的标志 //stack,key-value结构跟踪比较的对象 function equalByTag(object, other, tag, equalFunc, customizer, bitmask, stack) { switch (tag) {//根据toStringTag来比较object和other对象 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: //'[object Boolean]' case dateTag: //'[object Date]' case numberTag: //'[object Number]' // Coerce booleans to `1` or `0` and dates to milliseconds. // Invalid dates are coerced to `NaN`. //强制转换布尔值到1或0,时间对象转换为毫秒,无效的时间转换为NaN return eq(+object, +other); case errorTag: //'[object Error]' return object.name == other.name && object.message == other.message; case regexpTag: //'[object RegExp]' case stringTag: //'[object String]' // Coerce regexes to strings and treat strings, primitives and objects, // as equal. See http://www.ecma-international.org/ecma-262/7.0/#sec-regexp.prototype.tostring // for more details. return object == (other + ''); case mapTag: //'[object Map]' var convert = mapToArray; //将map转换成数组的方法 /* {key1: value1, key2: value2, ...} 变成: [[key1, value1], [key2, value2], ...] */ case setTag: //'[object Set]' var isPartial = bitmask & PARTIAL_COMPARE_FLAG;//是否部分比较 convert || (convert = setToArray);//将map或者set转换成数组的方法,mapToArray或者setToArray if (object.size != other.size && !isPartial) {//如果不是部分比较,且长度不一样,直接返回false 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);//在stack里把object和other都存上 var result = equalArrays(convert(object), convert(other), equalFunc, customizer, bitmask, stack); //把object和other都转换成数组,然后用equalArrays方法来比较是否相等 stack['delete'](object);//清除stack return result; case symbolTag: //'[object Symbol]' if (symbolValueOf) {//Symbol.prototype.valueOf return symbolValueOf.call(object) == symbolValueOf.call(other); } } return false; } /** * A specialized version of `baseIsEqualDeep` for objects with support for * partial deep comparisons. * * @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`. */ //判断object类型深度比较是否相等,支持部分比较 function equalObjects(object, other, equalFunc, customizer, bitmask, stack) { var isPartial = bitmask & PARTIAL_COMPARE_FLAG,//是否部分比较 objProps = keys(object),//Object.keys(),返回key组成的数组 objLength = objProps.length, othProps = keys(other),//Object.keys(),返回key组成的数组 othLength = othProps.length; if (objLength != othLength && !isPartial) {//如果不是部分比较,且object和other的key长度不一样,返回false return false; } var index = objLength;//循环索引是object key数组的长度 while (index--) { var key = objProps[index];//object 的key if (!(isPartial ? key in other : hasOwnProperty.call(other, key))) { //如果是部分比较,就用in来判断,否则用Object.prototype.hasOwnProperty //判断object的key是否other里也有 //如果没有,返回false 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;//部分比较,跳过constructor while (++index < objLength) {//循环 key = objProps[index];//object的key var objValue = object[key],//object key对应的value othValue = other[key];//other key对应的value 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). if (!(compared === undefined ? (objValue === othValue || equalFunc(objValue, othValue, customizer, bitmask, stack)) : compared )) { //如果自定义比较失败就跳出循环,result=false //如果不是自定义比较,就用===比较,或者继续递归调用baseIsEqual来深度比较 result = false; break; } skipCtor || (skipCtor = key == 'constructor'); } if (result && !skipCtor) {//不是部分比较且object中有constructor属性,不跳过constructor属性,判断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; } /** * Gets the data for `map`. * * @private * @param {Object} map The map to query. * @param {string} key The reference key. * @returns {*} Returns the map data. */ //从map实例里找到key对应的值 function getMapData(map, key) { var data = map.__data__; return isKeyable(key) ? data[typeof key == 'string' ? 'string' : 'hash'] : data.map; //根据key的类型返回对应的数据,要么是Map对象,要么是ListCache对象,要么是Hash对象 } /** * Gets the property names, values, and compare flags of `object`. * * @private * @param {Object} object The object to query. * @returns {Array} Returns the match data of `object`. */ //获取对象中的属性名,属性值,和用来匹配的标记 function getMatchData(object) { var result = keys(object),//object的key组成的数组 length = result.length;//key数组的长度 while (length--) {//循环 var key = result[length],//当前key value = object[key];//当前值 result[length] = [key, value, isStrictComparable(value)]; //重写result的当前值,变成[key, value, boolean]的形式,第三个布尔值表明当前值是否适合用===比较 } return result; } /** * Gets the native function at `key` of `object`. * * @private * @param {Object} object The object to query. * @param {string} key The key of the method to get. * @returns {*} Returns the function if it's native, else `undefined`. */ function getNative(object, key) { var value = getValue(object, key); return baseIsNative(value) ? value : undefined; } /** * Gets the `toStringTag` of `value`. * * @private * @param {*} value The value to query. * @returns {string} Returns the `toStringTag`. */ var getTag = baseGetTag; //处理toStringTag的兼容问题 // Fallback for data views, maps, sets, and weak maps in IE 11, // for data views in Edge < 14, and promises in Node.js. if ((DataView && getTag(new DataView(new ArrayBuffer(1))) != dataViewTag) || (Map && getTag(new Map) != mapTag) || (Promise && getTag(Promise.resolve()) != promiseTag) || (Set && getTag(new Set) != setTag) || (WeakMap && getTag(new WeakMap) != weakMapTag)) {//如果在某些环境下无法判断这5种类型,就做一下兼容处理 getTag = function(value) { var result = objectToString.call(value), Ctor = result == objectTag ? value.constructor : undefined, ctorString = Ctor ? toSource(Ctor) : undefined; //获取值的constructor构造函数属性,如果获取到了就返回对应的tag,如果获取不到就返回原始toString值 if (ctorString) { switch (ctorString) { case dataViewCtorString: return dataViewTag; case mapCtorString: return mapTag; case promiseCtorString: return promiseTag; case setCtorString: return setTag; case weakMapCtorString: return weakMapTag; } } return result; }; } /** * Checks if `path` exists on `object`. * * @private * @param {Object} object The object to query. * @param {Array|string} path The path to check. * @param {Function} hasFunc The function to check properties. * @returns {boolean} Returns `true` if `path` exists, else `false`. */ //判断object中是否存在path路径,考虑深层path路径 //object给定对象,path给定路径,hasFunc只判断一层路径的基础方法 function hasPath(object, path, hasFunc) { path = isKey(path, object) ? [path] : castPath(path);//将路径转换成路径数组 var result,//结果布尔值 index = -1,//循环索引 length = path.length;//路径数组的长度 while (++index < length) {//循环路径数组 var key = toKey(path[index]);//转换key值为合法key值 if (!(result = object != null && hasFunc(object, key))) { //如果有哪一层不存在,就跳出循环且result赋值false break; } //否则继续获取下一层 object = object[key]; } if (result) {//如果循环结束后result为true,说明存在此路径,返回true return result; } var length = object ? object.length : 0;//object的length return !!length && isLength(length) && isIndex(key, length) && (isArray(object) || isArguments(object)); //判断object是数组的情况 //有length属性,length属性是有效数字,判断最后一个key值是否是一个数组有效索引,object是数组或者是一个arguments对象 } /** * Checks if `value` is a flattenable `arguments` object or array. * * @private * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is flattenable, else `false`. */ //检查一个变量是否是一个可展开的对象或者数组 function isFlattenable(value) { return isArray(value) || isArguments(value) || !!(spreadableSymbol && value && value[spreadableSymbol]); //如果是数组,则可展开 //如果是arguments对象,则可展开 //如果当前环境含有Symbol对象,且此变量含有Symbol.isConcatSpreadable属性,Symbol.isConcatSpreadable用于改变array或者array-like对象使用concat时的默认行为 } /** * Checks if `value` is a valid array-like index. * * @private * @param {*} value The value to check. * @param {number} [length=MAX_SAFE_INTEGER] The upper bounds of a valid index. * @returns {boolean} Returns `true` if `value` is a valid index, else `false`. */ //检查一个值是否是一个有效的array-like的索引 function isIndex(value, length) { length = length == null ? MAX_SAFE_INTEGER : length;//如果length是空就换成最大安全数字 return !!length && (typeof value == 'number' || reIsUint.test(value)) && (value > -1 && value % 1 == 0 && value < length); //length存在 //value是number值或者value符合reIsUint = /^(?:0|[1-9]\d*)$/正则 //value是正数,value是整数,value小于length } /** * Checks if `value` is a property name and not a property path. * * @private * @param {*} value The value to check. * @param {Object} [object] The object to query keys on. * @returns {boolean} Returns `true` if `value` is a property name, else `false`. */ //用来判断一个值是一个属性名,而不是一个属性的路径,这个主要和_.get等方法匹配,看懂了get方法就明白这里的路径是什么意思 /* var object = { 'a': [{ 'b': { 'c': 3 } }] }; _.get(object, 'a[0].b.c'); // => 3 _.get(object, ['a', '0', 'b', 'c']); // => 3 _.get(object, 'a.b.c', 'default'); // => 'default' */ function isKey(value, object) {//value用于检查的值,object用于在其中查询是否存在value属性的对象 if (isArray(value)) {//如果value是数组,返回false,说明value是一个属性路径 return false; } var type = typeof value; if (type == 'number' || type == 'symbol' || type == 'boolean' || value == null || isSymbol(value)) {//如果value是以上类型,则是属性名,返回true return true; } return reIsPlainProp.test(value) || !reIsDeepProp.test(value) || (object != null && value in Object(object)); //判断字符串的情况,第一个正则reIsPlainProp判断如果都是单词说明是属性名 //第二个正则reIsDeepProp判断如果含有点.或者含有数组说明不是属性名 //第三种情况如果value在object里能找到说明是属性名 } /** * Checks if `value` is suitable for use as unique object key. * * @private * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is suitable, else `false`. */ //判断一个值是否合适作为一个唯一的对象key //如果这个值是String或者Number或者Symbol或者Boolean值,且不是__proto__,就可以作为一个唯一的键 //如果这个值不是以上四种类型,但是它等于null,那也可以作为一个唯一的键 function isKeyable(value) { var type = typeof value; return (type == 'string' || type == 'number' || type == 'symbol' || type == 'boolean') ? (value !== '__proto__') : (value === null); } /** * Checks if `func` has its source masked. * * @private * @param {Function} func The function to check. * @returns {boolean} Returns `true` if `func` is masked, else `false`. */ function isMasked(func) { return !!maskSrcKey && (maskSrcKey in func); } /** * Checks if `value` is likely a prototype object. * * @private * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is a prototype, else `false`. */ //检查一个值是否类似一个prototype对象 function isPrototype(value) { var Ctor = value && value.constructor,//含有constructor属性 proto = (typeof Ctor == 'function' && Ctor.prototype) || objectProto; return value === proto;//如果构造函数的prototype和value相等,说明value对象是一个prototype对象 } /** * Checks if `value` is suitable for strict equality comparisons, i.e. `===`. * * @private * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` if suitable for strict * equality comparisons, else `false`. */ //检查一个值用严格等于===来比较是否合适 //这个值不是NaN也不是对象就符合要求 function isStrictComparable(value) { return value === value && !isObject(value); } /** * A specialized version of `matchesProperty` for source values suitable * for strict equality comparisons, i.e. `===`. * * @private * @param {string} key The key of the property to get. * @param {*} srcValue The value to match. * @returns {Function} Returns the new spec function. */ //返回一个方法,这个方法用于比较传入对象object key值对应的值是否和给定值srcValue相等 function matchesStrictComparable(key, srcValue) { return function(object) { if (object == null) {//如果传入对象为空,返回false return false; } return object[key] === srcValue && (srcValue !== undefined || (key in Object(object))); //给定值不为undefined且object中有此key值,且key对应值和给定值严格严格相等,就返回true }; } /** * Converts `string` to a property path array. * * @private * @param {string} string The string to convert. * @returns {Array} Returns the property path array. */ //转换字符串值变为属性path数组 var stringToPath = memoize(function(string) { string = toString(string);//将值转换成字符串 var result = [];//结果数组 if (reLeadingDot.test(string)) { //reLeadingDot = /^\./,判断字符串的开头如果是一个点,结果数组第一个就push一个空字符串 result.push(''); } string.replace(rePropName, function(match, number, quote, string) { //rePropName = /[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|$))/g; //rePropName这个正则匹配三种形式 //?:是非捕获分组 //第一种形式:不是点.也不是[或者]的任意字符组成的字符串,例如:'aaassssdfdffdf' //第二种形式:判断取数组下标的情况,例如:a[0].b.c.a[0][1] //第三种形式:??? result.push(quote ? string.replace(reEscapeChar, '$1') : (number || match)); //reEscapeChar = /\\(\\)?/g //match是匹配到的内容,number是第二种形式的第一个捕获分组,quote是第二种形式的第二个捕获分组 //有quote说明是带引号的内容,再执行一次replace操作,??? }); return result; }); /** * Converts `value` to a string key if it's not a string or symbol. * * @private * @param {*} value The value to inspect. * @returns {string|symbol} Returns the key. */ //如果一个值不是string或者symbol,就把它转换成string function toKey(value) { if (typeof value == 'string' || isSymbol(value)) {//如果这个值是字符串或者symbol,直接返回 return value; } var result = (value + '');//转换成字符串 return (result == '0' && (1 / value) == -INFINITY) ? '-0' : result;//判断value是负0的情况 } /** * Converts `func` to its source code. * * @private * @param {Function} func The function to process. * @returns {string} Returns the source code. */ function toSource(func) { if (func != null) { try { return funcToString.call(func); } catch (e) {} try { return (func + ''); } catch (e) {} } return ''; } /** * This method is like `_.difference` except that it accepts `iteratee` which * is invoked for each element of `array` and `values` to generate the criterion * by which they're compared. Result values are chosen from the first array. * The iteratee is invoked with one argument: (value). * * **Note:** Unlike `_.pullAllBy`, this method returns a new array. * * @static * @memberOf _ * @since 4.0.0 * @category Array * @param {Array} array The array to inspect. * @param {...Array} [values] The values to exclude. * @param {Function} [iteratee=_.identity] The iteratee invoked per element. * @returns {Array} Returns the new array of filtered values. * @example * * _.differenceBy([2.1, 1.2], [2.3, 3.4], Math.floor); * // => [1.2] * * // The `_.property` iteratee shorthand. * _.differenceBy([{ 'x': 2 }, { 'x': 1 }], [{ 'x': 1 }], 'x'); * // => [{ 'x': 2 }] */ //和_.difference方法类似,区别是传入一个迭代器在比较的时候给values数组的每一个元素都执行迭代器,用处理后的值来比较 var differenceBy = baseRest(function(array, values) { var iteratee = last(values);//最后一个参数是迭代器 if (isArrayLikeObject(iteratee)) {//如果迭代器是一个array-like对象,就赋值为undefined iteratee = undefined; } return isArrayLikeObject(array) ? baseDifference(array, baseFlatten(values, 1, isArrayLikeObject, true), baseIteratee(iteratee, 2)) : []; //如果起始数组array不是array-like对象,就返回空数组 //如果是array-like对象,就调用baseDifference方法 //第一个参数是array起始数组,第二个参数是用来排除的数组values展开一层后的数组,第三个参数是迭代器 //baseIteratee会处理iteratee参数,有下面几种情况: //如果iteratee是function,直接返回 //如果iteratee是数组,那么数组第一个值是一个path属性路径,第二个值是srcValue,最后返回的方法会判断value[1]是否和传入的对象根据value[0]路径获取到的属性 是相等的 //如果iteratee是一个对象,则iteratee就是source参数,这个方法会使用部分的(partial)深度比较来比较给定的对象和source对象,如果给定对象拥有相等的属性值,就返回true,否则false //如果iteratee是其他值,就返回一个方法,这个方法可以根据给定path属性路径返回给定对象的属性值 }); /** * Gets the last element of `array`. * * @static * @memberOf _ * @since 0.1.0 * @category Array * @param {Array} array The array to query. * @returns {*} Returns the last element of `array`. * @example * * _.last([1, 2, 3]); * // => 3 */ //获取一个数组的结尾元素 function last(array) { var length = array ? array.length : 0; return length ? array[length - 1] : undefined; } /** * Creates a function that memoizes the result of `func`. If `resolver` is * provided, it determines the cache key for storing the result based on the * arguments provided to the memoized function. By default, the first argument * provided to the memoized function is used as the map cache key. The `func` * is invoked with the `this` binding of the memoized function. * * **Note:** The cache is exposed as the `cache` property on the memoized * function. Its creation may be customized by replacing the `_.memoize.Cache` * constructor with one whose instances implement the * [`Map`](http://ecma-international.org/ecma-262/7.0/#sec-properties-of-the-map-prototype-object) * method interface of `delete`, `get`, `has`, and `set`. * * @static * @memberOf _ * @since 0.1.0 * @category Function * @param {Function} func The function to have its output memoized. * @param {Function} [resolver] The function to resolve the cache key. * @returns {Function} Returns the new memoized function. * @example * * var object = { 'a': 1, 'b': 2 }; * var other = { 'c': 3, 'd': 4 }; * * var values = _.memoize(_.values); * values(object); * // => [1, 2] * * values(other); * // => [3, 4] * * object.a = 2; * values(object); * // => [1, 2] * * // Modify the result cache. * values.cache.set(object, ['a', 'b']); * values(object); * // => ['a', 'b'] * * // Replace `_.memoize.Cache`. * _.memoize.Cache = WeakMap; */ //创建一个可以缓存func的结果的函数,resolver用来解析缓存result的键 function memoize(func, resolver) { if (typeof func != 'function' || (resolver && typeof resolver != 'function')) { //func和resolver(如果提供的话)如果不是函数,抛错误 throw new TypeError(FUNC_ERROR_TEXT); } var memoized = function() { var args = arguments, key = resolver ? resolver.apply(this, args) : args[0], //如果提供了resolver,就用resolver来确定key,否则key就是memoized的第一个参数 cache = memoized.cache;//cashe对象 if (cache.has(key)) {//如果cache中已经有此key,就直接获取后返回 return cache.get(key); } var result = func.apply(this, args);//否则调用func计算出key对应的value memoized.cache = cache.set(key, result);//将新value存入cache后返回 return result; }; memoized.cache = new (memoize.Cache || MapCache);//cache使用自定义的MapCache类型 return memoized; } // Assign cache to `_.memoize`. memoize.Cache = MapCache; /** * Performs a * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) * comparison between two values to determine if they are equivalent. * * @static * @memberOf _ * @since 4.0.0 * @category Lang * @param {*} value The value to compare. * @param {*} other The other value to compare. * @returns {boolean} Returns `true` if the values are equivalent, else `false`. * @example * * var object = { 'a': 1 }; * var other = { 'a': 1 }; * * _.eq(object, object); * // => true * * _.eq(object, other); * // => false * * _.eq('a', 'a'); * // => true * * _.eq('a', Object('a')); * // => false * * _.eq(NaN, NaN); * // => true */ //判断两个值是否相等 function eq(value, other) { return value === other || (value !== value && other !== other); } /** * Checks if `value` is likely an `arguments` object. * * @static * @memberOf _ * @since 0.1.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is an `arguments` object, * else `false`. * @example * * _.isArguments(function() { return arguments; }()); * // => true * * _.isArguments([1, 2, 3]); * // => false */ //判断一个值是一个类似arguments对象 function isArguments(value) { // Safari 8.1 makes `arguments.callee` enumerable in strict mode. return isArrayLikeObject(value) && hasOwnProperty.call(value, 'callee') && (!propertyIsEnumerable.call(value, 'callee') || objectToString.call(value) == argsTag); //value是一个array-like对象,value有名为callee的自身属性,callee属性不可枚举,对象的toString标签是'[object Arguments]' } /** * Checks if `value` is classified as an `Array` object. * * @static * @memberOf _ * @since 0.1.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is an array, else `false`. * @example * * _.isArray([1, 2, 3]); * // => true * * _.isArray(document.body.children); * // => false * * _.isArray('abc'); * // => false * * _.isArray(_.noop); * // => false */ //判断一个值是不是数组对象 var isArray = Array.isArray; /** * Checks if `value` is array-like. A value is considered array-like if it's * not a function and has a `value.length` that's an integer greater than or * equal to `0` and less than or equal to `Number.MAX_SAFE_INTEGER`. * * @static * @memberOf _ * @since 4.0.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is array-like, else `false`. * @example * * _.isArrayLike([1, 2, 3]); * // => true * * _.isArrayLike(document.body.children); * // => true * * _.isArrayLike('abc'); * // => true * * _.isArrayLike(_.noop); * // => false */ //判断一个值是否是一个array-like //规则:不等于null,不是function类型,并且有length属性,length是大于0小于Number.MAX_SAFE_INTEGER的整数 function isArrayLike(value) { return value != null && isLength(value.length) && !isFunction(value); } /** * This method is like `_.isArrayLike` except that it also checks if `value` * is an object. * * @static * @memberOf _ * @since 4.0.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is an array-like object, * else `false`. * @example * * _.isArrayLikeObject([1, 2, 3]); * // => true * * _.isArrayLikeObject(document.body.children); * // => true * * _.isArrayLikeObject('abc'); * // => false * * _.isArrayLikeObject(_.noop); * // => false */ //判断一个值是不是一个array-like对象 //isObjectLike判断一个值是否是一个object-like,规则是:typeof返回object,并且不是null //isArrayLike判断一个值是否是一个array-like,规则:不等于null,不是function类型,并且有length属性,length是大于0小于Number.MAX_SAFE_INTEGER的整数 function isArrayLikeObject(value) { return isObjectLike(value) && isArrayLike(value); } /** * Checks if `value` is classified as a `Function` object. * * @static * @memberOf _ * @since 0.1.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is a function, else `false`. * @example * * _.isFunction(_); * // => true * * _.isFunction(/abc/); * // => false */ function isFunction(value) { // The use of `Object#toString` avoids issues with the `typeof` operator // in Safari 8-9 which returns 'object' for typed array and other constructors. var tag = isObject(value) ? objectToString.call(value) : ''; return tag == funcTag || tag == genTag; } /** * Checks if `value` is a valid array-like length. * * **Note:** This method is loosely based on * [`ToLength`](http://ecma-international.org/ecma-262/7.0/#sec-tolength). * * @static * @memberOf _ * @since 4.0.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is a valid length, else `false`. * @example * * _.isLength(3); * // => true * * _.isLength(Number.MIN_VALUE); * // => false * * _.isLength(Infinity); * // => false * * _.isLength('3'); * // => false */ //判断一个值是否是一个有效的array-like对象的length属性 function isLength(value) { return typeof value == 'number' && value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER; //是number类型,是正数,是整数不是小数,在最大数字范围内 } /** * Checks if `value` is the * [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types) * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`) * * @static * @memberOf _ * @since 0.1.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is an object, else `false`. * @example * * _.isObject({}); * // => true * * _.isObject([1, 2, 3]); * // => true * * _.isObject(_.noop); * // => true * * _.isObject(null); * // => false */ //判断一个值是否是一个对象 function isObject(value) { var type = typeof value; return !!value && (type == 'object' || type == 'function'); } /** * Checks if `value` is object-like. A value is object-like if it's not `null` * and has a `typeof` result of "object". * * @static * @memberOf _ * @since 4.0.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is object-like, else `false`. * @example * * _.isObjectLike({}); * // => true * * _.isObjectLike([1, 2, 3]); * // => true * * _.isObjectLike(_.noop); * // => false * * _.isObjectLike(null); * // => false */ //判断一个值是否是一个object-like,规则是:typeof返回object,并且不是null function isObjectLike(value) { return !!value && typeof value == 'object'; } /** * Checks if `value` is classified as a `Symbol` primitive or object. * * @static * @memberOf _ * @since 4.0.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is a symbol, else `false`. * @example * * _.isSymbol(Symbol.iterator); * // => true * * _.isSymbol('abc'); * // => false */ //判断一个值是否是原生的Symbol对象 function isSymbol(value) { return typeof value == 'symbol' || (isObjectLike(value) && objectToString.call(value) == symbolTag); } /** * Checks if `value` is classified as a typed array. * * @static * @memberOf _ * @since 3.0.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is a typed array, else `false`. * @example * * _.isTypedArray(new Uint8Array); * // => true * * _.isTypedArray([]); * // => false */ //判断一个值是typedArray类型,如果是nodejs环境就使用nodejs自带的优化方法 var isTypedArray = nodeIsTypedArray ? baseUnary(nodeIsTypedArray) : baseIsTypedArray; /** * Converts `value` to a string. An empty string is returned for `null` * and `undefined` values. The sign of `-0` is preserved. * * @static * @memberOf _ * @since 4.0.0 * @category Lang * @param {*} value The value to process. * @returns {string} Returns the string. * @example * * _.toString(null); * // => '' * * _.toString(-0); * // => '-0' * * _.toString([1, 2, 3]); * // => '1,2,3' */ //将一个值转换成字符串值,空值会返回空字符串 function toString(value) { return value == null ? '' : baseToString(value); } /** * Gets the value at `path` of `object`. If the resolved value is * `undefined`, the `defaultValue` is returned in its place. * * @static * @memberOf _ * @since 3.7.0 * @category Object * @param {Object} object The object to query. * @param {Array|string} path The path of the property to get. * @param {*} [defaultValue] The value returned for `undefined` resolved values. * @returns {*} Returns the resolved value. * @example * * var object = { 'a': [{ 'b': { 'c': 3 } }] }; * * _.get(object, 'a[0].b.c'); * // => 3 * * _.get(object, ['a', '0', 'b', 'c']); * // => 3 * * _.get(object, 'a.b.c', 'default'); * // => 'default' */ //根据path来获取object上对应健的值 function get(object, path, defaultValue) { var result = object == null ? undefined : baseGet(object, path);//根据路径获取值 return result === undefined ? defaultValue : result;//如果结果是undefined,返回默认值 } /** * Checks if `path` is a direct or inherited property of `object`. * * @static * @memberOf _ * @since 4.0.0 * @category Object * @param {Object} object The object to query. * @param {Array|string} path The path to check. * @returns {boolean} Returns `true` if `path` exists, else `false`. * @example * * var object = _.create({ 'a': _.create({ 'b': 2 }) }); * * _.hasIn(object, 'a'); * // => true * * _.hasIn(object, 'a.b'); * // => true * * _.hasIn(object, ['a', 'b']); * // => true * * _.hasIn(object, 'b'); * // => false */ //判断path路径在给定object中是否存在 function hasIn(object, path) { return object != null && hasPath(object, path, baseHasIn); } /** * Creates an array of the own enumerable property names of `object`. * * **Note:** Non-object values are coerced to objects. See the * [ES spec](http://ecma-international.org/ecma-262/7.0/#sec-object.keys) * for more details. * * @static * @since 0.1.0 * @memberOf _ * @category Object * @param {Object} object The object to query. * @returns {Array} Returns the array of property names. * @example * * function Foo() { * this.a = 1; * this.b = 2; * } * * Foo.prototype.c = 3; * * _.keys(new Foo); * // => ['a', 'b'] (iteration order is not guaranteed) * * _.keys('hi'); * // => ['0', '1'] */ //将对象可枚举的属性的key提取出来创建一个新数组,与Object.keys一样的效果 function keys(object) { return isArrayLike(object) ? arrayLikeKeys(object) : baseKeys(object); //先判断object是否是array-like对象,如果是调用arrayLikeKeys,如果不是调用baseKeys } /** * This method returns the first argument it receives. * * @static * @since 0.1.0 * @memberOf _ * @category Util * @param {*} value Any value. * @returns {*} Returns `value`. * @example * * var object = { 'a': 1 }; * * console.log(_.identity(object) === object); * // => true */ //用作默认迭代器,直接返回接收到的第一个参数 function identity(value) { return value; } /** * Creates a function that returns the value at `path` of a given object. * * @static * @memberOf _ * @since 2.4.0 * @category Util * @param {Array|string} path The path of the property to get. * @returns {Function} Returns the new accessor function. * @example * * var objects = [ * { 'a': { 'b': 2 } }, * { 'a': { 'b': 1 } } * ]; * * _.map(objects, _.property('a.b')); * // => [2, 1] * * _.map(_.sortBy(objects, _.property(['a', 'b'])), 'a.b'); * // => [1, 2] */ //创建一个方法,这个方法可以根据给定path属性路径返回给定对象的属性值 function property(path) { return isKey(path) ? baseProperty(toKey(path)) : basePropertyDeep(path); //先判断path是key还是path //如果是key,调用baseProperty,返回一个方法,这个方法返回object的对应key的对应value //如果是路径,调用basePropertyDeep,返回一个方法,这个方法根据path返回object的对应属性值 } module.exports = differenceBy;
相关文章
- C pointer to array/array of pointers disambiguation
- es5 - array - join
- _.sortedUniqBy(array, [iteratee])
- Array.prototype.keys()、Array.prototype.values()和Array.prototype.entries()
- Array.prototype.indexOf()和Array.prototype.lastIndexOf()
- Google Earth Engine(GEE)——建立一个图表(ui.Chart.array.values)chart散点图
- 一个字符串是否在另外一个字符串数组里 Array.Exists 的用法 Array.IndexOf 用法
- java中 set,list,array(集合与数组)相互转换
- [Leetcode]-Remove Duplicates from Sorted Array
- [LeetCode] 1005. Maximize Sum Of Array After K Negations K次取反后最大化的数组和
- [LeetCode] 932. Beautiful Array 漂亮数组
- 【系列课】Springmvc4+Mybatis3+Spring4+Bootstrap3之列表-Array-专题视频课程