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;