9node
_.dropRightWhile(array, [predicate=_.identity])
建立一个数组的切片,从结尾丢弃掉一些元素。被丢弃的元素是从结尾开始循环,直到predicate返回了false。predicate函数有三个参数,value,index,array。jquery
array (Array): 须要建立切片的数组
[predicate=_.identity] (Function): 遍历调用的方法web
返回值npm
(Array): 返回一个新数组切片数组
例子promise
var users = [ { 'user': 'barney', 'active': true }, { 'user': 'fred', 'active': false }, { 'user': 'pebbles', 'active': false } ]; _.dropRightWhile(users, function(o) { return !o.active; }); // => objects for ['barney'] // The `_.matches` iteratee shorthand. _.dropRightWhile(users, { 'user': 'pebbles', 'active': false }); // => objects for ['barney', 'fred'] // The `_.matchesProperty` iteratee shorthand. _.dropRightWhile(users, ['active', false]); // => objects for ['barney'] // The `_.property` iteratee shorthand. _.dropRightWhile(users, 'active'); // => objects for ['barney', 'fred', 'pebbles']
源代码:浏览器
/** * 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 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`. */ function arraySome(array, predicate) { var index = -1, length = array ? array.length : 0; while (++index < length) { if (predicate(array[index], index, array)) { return true; } } return false; } /** * 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); }; } /** * 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. */ 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; /* Built-in method references for those with the same name as other `lodash` methods. */ var nativeKeys = overArg(Object.keys, Object); /* 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. */ function Hash(entries) { var index = -1, length = entries ? entries.length : 0; this.clear(); while (++index < length) { var entry = entries[index]; this.set(entry[0], entry[1]); } } /** * Removes all key-value entries from the hash. * * @private * @name clear * @memberOf Hash */ function hashClear() { this.__data__ = nativeCreate ? nativeCreate(null) : {}; } /** * 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`. */ 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. */ 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`. */ 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. */ function hashSet(key, value) { var data = this.__data__; data[key] = (nativeCreate && value === undefined) ? HASH_UNDEFINED : value; return this; } // Add methods to `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. */ function ListCache(entries) { var index = -1, length = entries ? entries.length : 0; this.clear(); while (++index < length) { var entry = entries[index]; this.set(entry[0], entry[1]); } } /** * Removes all key-value entries from the list cache. * * @private * @name clear * @memberOf ListCache */ 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`. */ 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. */ 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`. */ 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. */ function listCacheSet(key, value) { var data = this.__data__, index = assocIndexOf(data, key); if (index < 0) { data.push([key, value]); } else { data[index][1] = value; } return this; } // 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. */ function MapCache(entries) { var index = -1, length = entries ? entries.length : 0; this.clear(); while (++index < length) { var entry = entries[index]; this.set(entry[0], entry[1]); } } /** * Removes all key-value entries from the map. * * @private * @name clear * @memberOf 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`. */ 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. */ 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`. */ 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) { getMapData(this, key).set(key, value); return this; } // Add methods to `MapCache`. MapCache.prototype.clear = mapCacheClear; MapCache.prototype['delete'] = mapCacheDelete; MapCache.prototype.get = mapCacheGet; MapCache.prototype.has = mapCacheHas; MapCache.prototype.set = mapCacheSet; /** * * Creates an array cache object to store unique values. * * @private * @constructor * @param {Array} [values] The values to cache. */ function SetCache(values) { var index = -1, length = values ? values.length : 0; this.__data__ = new MapCache; while (++index < length) { this.add(values[index]); } } /** * 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. */ 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`. */ function setCacheHas(value) { return this.__data__.has(value); } // Add methods to `SetCache`. SetCache.prototype.add = SetCache.prototype.push = setCacheAdd; SetCache.prototype.has = setCacheHas; /** * 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`. */ function assocIndexOf(array, key) { var length = array.length; while (length--) { if (eq(array[length][0], key)) { return length; } } return -1; } /** * 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`. */ function baseHasIn(object, key) { 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); objTag = objTag == argsTag ? objectTag : objTag; } if (!othIsArr) { othTag = getTag(other);//若是object不是数组,就获取object的数据类型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`. */ 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 `_.slice` without an iteratee call guard. * * @private * @param {Array} array The array to slice. * @param {number} [start=0] The start position. * @param {number} [end=array.length] The end position. * @returns {Array} Returns the slice of `array`. */ //建立一个数组array的切片,从起始索引到结束索引,不包括结束索引 function baseSlice(array, start, end) { var index = -1,//循环索引 length = array.length;//数组长度 if (start < 0) {//起始索引是负数处理,是负数就至关于从末尾往开头数,也就是和lengh相加 start = -start > length ? 0 : (length + start);//和length相加后若是仍是小于0就等于0 } end = end > length ? length : end;//结束索引若是大于length就让它等于length if (end < 0) {//处理结束索引是负数 end += length; } length = start > end ? 0 : ((end - start) >>> 0);//根据start和end计算这个切片的长度,若是起始在结束后面那么切片长度为0,不然相减而且取整 start >>>= 0;//start取整 var result = Array(length);//结果切片数组 while (++index < length) {//循环切片长度的次数,给结果数组每一项赋值 result[index] = array[index + start]; } return result; } /** * 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的状况 } /** * The base implementation of methods like `_.dropWhile` and `_.takeWhile` * without support for iteratee shorthands. * * @private * @param {Array} array The array to query. * @param {Function} predicate The function invoked per iteration. * @param {boolean} [isDrop] Specify dropping elements instead of taking them. * @param {boolean} [fromRight] Specify iterating from right to left. * @returns {Array} Returns the slice of `array`. */ //dropWhile的基础实现 //array须要处理的数组,predicate遍历器函数,isDrop指定丢弃或者获取元素,fromRight指定循环的方向 function baseWhile(array, predicate, isDrop, fromRight) { var length = array.length,//数组的长度 index = fromRight ? length : -1;//循环索引,若是fromRight为true,从右往左循环,起始索引是数组长度;若是fromRight为false,从左往右循环,起始索引是-1 while ((fromRight ? index-- : ++index < length) && predicate(array[index], index, array)) {} //循环数组,length--从右往左循环,-1++从左往右循环 //为数组循环值调用predicate,直到predicate返回false,循环索引index后面slice的时候有用 return isDrop ? baseSlice(array, (fromRight ? 0 : index), (fromRight ? index + 1 : length)) : baseSlice(array, (fromRight ? index + 1 : 0), (fromRight ? length : index)); //isDrop为true的时候,若是从右往左丢弃,起始slice为0,结束slice为index+1;若是从左往右丢弃,起始slice为index,结束slice为length //isDrop为false的时候,若是从右往左获取,起始slice为index+1,结束slice为length;若是从左往右获取,起始slice为0,结束slice为index } /** * 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. */ function getMapData(map, key) { var data = map.__data__; return isKeyable(key) ? data[typeof key == 'string' ? 'string' : 'hash'] : data.map; } /** * 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; // 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)) { getTag = function(value) { var result = objectToString.call(value), Ctor = result == objectTag ? value.constructor : undefined, ctorString = Ctor ? toSource(Ctor) : undefined; 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; return !!length && isLength(length) && isIndex(key, length) && (isArray(object) || isArguments(object)); //判断object是数组的状况 //有length属性,length属性是有效数字,判断最后一个key值是不是一个数组有效索引,object是数组或者是一个arguments对象 } /** * 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`. */ 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`. */ 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`. */ function isPrototype(value) { var Ctor = value && value.constructor, proto = (typeof Ctor == 'function' && Ctor.prototype) || objectProto; return value === proto; } /** * 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 ''; } /** * Creates a slice of `array` excluding elements dropped from the end. * Elements are dropped until `predicate` returns falsey. The predicate is * invoked with three arguments: (value, index, array). * * @static * @memberOf _ * @since 3.0.0 * @category Array * @param {Array} array The array to query. * @param {Function} [predicate=_.identity] The function invoked per iteration. * @returns {Array} Returns the slice of `array`. * @example * * var users = [ * { 'user': 'barney', 'active': true }, * { 'user': 'fred', 'active': false }, * { 'user': 'pebbles', 'active': false } * ]; * * _.dropRightWhile(users, function(o) { return !o.active; }); * // => objects for ['barney'] * * // The `_.matches` iteratee shorthand. * _.dropRightWhile(users, { 'user': 'pebbles', 'active': false }); * // => objects for ['barney', 'fred'] * * // The `_.matchesProperty` iteratee shorthand. * _.dropRightWhile(users, ['active', false]); * // => objects for ['barney'] * * // The `_.property` iteratee shorthand. * _.dropRightWhile(users, 'active'); * // => objects for ['barney', 'fred', 'pebbles'] */ //建立一个数组的切片,从结尾开始排除掉一些元素,直到根据predicate参数返回false的时候的index就是slice的结束索引 function dropRightWhile(array, predicate) { return (array && array.length) ? baseWhile(array, baseIteratee(predicate, 3), true, true) : []; //若是数组不为空且长度不为0,就调用baseWhile来处理,不然返回空数组 //baseWhile第一个参数array为处理的数组,第二个参数调用baseIteratee生成一个为判断须要丢弃的元素的函数,第三个参数isDrop丢弃仍是获取元素,第四个参数FromRight是否从右往左判断要丢弃的元素 } /** * 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对象 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) : ''; //获取value的toString tag return tag == funcTag || tag == genTag; //若是toString tag等于[object Function],或者[object GeneratorFunction],说明是function } /** * 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 = dropRightWhile;