_.invokeMap(collection, path, [args])
79
_.invokeMap(collection, path, [args])
引用path参数对应的方法处理collection中的每一个元素,将引用方法处理后的结果组成一个数组返回。
任何额外的参数都被传递给每一次引用方法的调用
如果path参数是一个方法,直接调用,如果不是,就是路径数组或者路径字符串,就去那个路径下寻找对应的方法
调用引用方法的时候this被绑定到collection的每一个元素上
参数
collection (Array|Object): 需要遍历的集合
path (Array|Function|string): 遍历每个元素调用的方法或者那个方法的路径
[args] (...*): 每一次调用方法的额外参数
返回值
(Array): 返回结果数组
例子
_.invokeMap([[5, 1, 7], [3, 2, 1]], 'sort'); // => [[1, 5, 7], [1, 2, 3]] _.invokeMap([123, 456], String.prototype.split, ''); // => [['1', '2', '3'], ['4', '5', '6']]
源代码:
/** * 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 `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 as references for various `Number` constants. */ var INFINITY = 1 / 0, MAX_SAFE_INTEGER = 9007199254740991; /** `Object#toString` result references. */ var argsTag = '[object Arguments]', funcTag = '[object Function]', genTag = '[object GeneratorFunction]', symbolTag = '[object Symbol]'; /** 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*)$/; /** 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')(); /** * A faster alternative to `Function#apply`, this function invokes `func` * with the `this` binding of `thisArg` and the arguments of `args`. * * @private * @param {Function} func The function to invoke. * @param {*} thisArg The `this` binding of `func`. * @param {Array} args The arguments to invoke `func` with. * @returns {*} Returns the result of `func`. */ //一个更快的apply方法 function apply(func, thisArg, args) { switch (args.length) { case 0: return func.call(thisArg); case 1: return func.call(thisArg, args[0]); case 2: return func.call(thisArg, args[0], args[1]); case 3: return func.call(thisArg, args[0], args[1], args[2]); } return func.apply(thisArg, args); } /** * 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次,返回iteratee处理的index组成的数组 function baseTimes(n, iteratee) { var index = -1,//循环索引 result = Array(n);//结果数组,长度为n while (++index < n) {//循环n次,结果数组每一个元素是当前index被iteratee处理后的结果 result[index] = iteratee(index); } return result; } /** * 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. */ //获取object上对应key的值 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`. */ //判断value是否是一个宿主对象,在IE9以下的浏览器中 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; } /** * 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. */ //创建一个只有一个参数的函数,引用func来处理,这个函数的参数会被transform处理 function overArg(func, transform) { return function(arg) { return func(transform(arg)); }; } /** 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, 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), nativeMax = Math.max; /* Built-in method references that are verified to be native. */ var Map = getNative(root, 'Map'), nativeCreate = getNative(Object, 'create'); /** Used to convert symbols to primitives and strings. */ var symbolProto = Symbol ? Symbol.prototype : 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 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对象的可枚举的属性名组成的数组 //inherited布尔值,是否指定需要返回继承来的属性 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是数组或者arguments对象,使用baseTimes处理,baseTimes返回的数组的元素是索引的字符串值组成的数组,否则为空数组 var length = result.length,//结果数组长度 skipIndexes = !!length;//是否需要跳过数字索引 for (var key in value) {//for in循环value的可枚举属性 if ((inherited || hasOwnProperty.call(value, key)) && !(skipIndexes && (key == 'length' || isIndex(key, length)))) { //如果需要返回继承来的属性值,或者当前key是value自身属性 //并且判断是否是不是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 `_.forEach` without support for iteratee shorthands. * * @private * @param {Array|Object} collection The collection to iterate over. * @param {Function} iteratee The function invoked per iteration. * @returns {Array|Object} Returns `collection`. */ //forEach基础实现 var baseEach = createBaseEach(baseForOwn); /** * The base implementation of `baseForOwn` which iterates over `object` * properties returned by `keysFunc` and invokes `iteratee` for each property. * Iteratee functions may exit iteration early by explicitly returning `false`. * * @private * @param {Object} object The object to iterate over. * @param {Function} iteratee The function invoked per iteration. * @param {Function} keysFunc The function to get the keys of `object`. * @returns {Object} Returns `object`. */ //创建循环对象自身属性的基础方法 var baseFor = createBaseFor(); /** * The base implementation of `_.forOwn` without support for iteratee shorthands. * * @private * @param {Object} object The object to iterate over. * @param {Function} iteratee The function invoked per iteration. * @returns {Object} Returns `object`. */ //forOwn的基础实现,循环对象自身属性 function baseForOwn(object, iteratee) { return object && baseFor(object, iteratee, keys);//调用baseFor处理 } /** * 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. */ //_.get方法的基础实现,获取object上path路径对应的值 function baseGet(object, path) { path = isKey(path, object) ? [path] : castPath(path); //判断path是不是合法key,如果是,就变成[path],否则用castPath处理 var index = 0,//循环路径数组的索引 length = path.length;//路径数组的长度 while (object != null && index < length) {//如果object不为空,循环路径数组 object = object[toKey(path[index++])];//根据当前路径获取到深一层的对象或值赋值给object } return (index && index == length) ? object : undefined; //循环结束后返回找到的值,找不到返回undefined } /** * The base implementation of `_.invoke` without support for individual * method arguments. * * @private * @param {Object} object The object to query. * @param {Array|string} path The path of the method to invoke. * @param {Array} args The arguments to invoke the method with. * @returns {*} Returns the result of the invoked method. */ //_.invoke的基础实现。通过path路径查找到对象上对应的function,然后在路径数组倒数第二个路径对应对象上调用这个方法 function baseInvoke(object, path, args) { if (!isKey(path, object)) {//如果path不是合法object的key,说明是路径字符串 path = castPath(path);//将path转换成路径数组 object = parent(object, path); path = last(path);//最后一个路径,也就是要调用的方法对应的路径名 } var func = object == null ? object : object[toKey(path)]; return func == null ? undefined : apply(func, object, args); } /** * 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 `_.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的基础实现,类似于Object.keys() function baseKeys(object) { if (!isPrototype(object)) {//如果object不是一个prototype对象,用nativekeys处理,也就是原生的Object.keys return nativeKeys(object); } var result = [];//结果数组 for (var key in Object(object)) {//遍历object的属性 if (hasOwnProperty.call(object, key) && key != 'constructor') { //如果当前key是object自身属性,并且不是构造函数,就插入结果数组 result.push(key); } } return result; } /** * The base implementation of `_.rest` which doesn't validate or coerce arguments. * * @private * @param {Function} func The function to apply a rest parameter to. * @param {number} [start=func.length-1] The start position of the rest parameter. * @returns {Function} Returns the new function. */ //_.rest方法的基础实现,为函数实现ES6的rest参数 //rest参数的起始位置,默认是形参个数减去1,func.length - 1 function baseRest(func, start) { start = nativeMax(start === undefined ? (func.length - 1) : start, 0); //处理rest参数的起始位置,如果没传递start,就用函数形参个数减一,如果传递了,就在start和0之间选一个最大值 return function() { var args = arguments,//函数接收到的参数数组 index = -1,//循环索引 length = nativeMax(args.length - start, 0),//rest参数的长度 array = Array(length);//创建一个长度为rest参数长度的数组 while (++index < length) {//循环rest参数长度,将rest参数全部存入array变量中 array[index] = args[start + index]; } index = -1;//循环参数 var otherArgs = Array(start + 1);//创建一个数组存放除了rest参数以外的其他参数 while (++index < start) {//循环获取其他参数存入otherArgs中 otherArgs[index] = args[index]; } otherArgs[start] = array;//otherArgs的最后一个参数就是rest参数数组 return apply(func, this, otherArgs);//调用func,参数已处理成带有rest参数的形式 }; } /** * The base implementation of `_.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`. */ //_.slice方法基础实现 //创建一个数组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. */ //toString方法的基础实现 function baseToString(value) { // Exit early for strings to avoid a performance hit in some environments. if (typeof value == 'string') {//如果value已经是字符串,直接返回 return value; } if (isSymbol(value)) {//如果value是symbol对象,就调用symbol原型上的toString方法转换成字符串 return symbolToString ? symbolToString.call(value) : ''; } var result = (value + '');//转换成字符串 return (result == '0' && (1 / value) == -INFINITY) ? '-0' : result;//处理-0情况后返回结果字符串 } /** * Casts `value` to a path array if it's not one. * * @private * @param {*} value The value to inspect. * @returns {Array} Returns the cast property path array. */ //将值计算成一个路径数组 function castPath(value) { return isArray(value) ? value : stringToPath(value); } /** * Creates a `baseEach` or `baseEachRight` function. * * @private * @param {Function} eachFunc The function to iterate over a collection. * @param {boolean} [fromRight] Specify iterating from right to left. * @returns {Function} Returns the new base function. */ //创建一个baseEach或者baseEachRight方法 function createBaseEach(eachFunc, fromRight) { return function(collection, iteratee) { if (collection == null) {//如果collection是空,直接返回 return collection; } if (!isArrayLike(collection)) {//如果collection不是array-like对象,就用eachFunc return eachFunc(collection, iteratee); } var length = collection.length,//collection长度 index = fromRight ? length : -1,//循环索引,如果fromRight就从结尾开始,否则从开头开始 iterable = Object(collection); while ((fromRight ? index-- : ++index < length)) {//循环,如果iteratee返回false,跳出循环 if (iteratee(iterable[index], index, iterable) === false) { break; } } return collection; }; } /** * Creates a base function for methods like `_.forIn` and `_.forOwn`. * * @private * @param {boolean} [fromRight] Specify iterating from right to left. * @returns {Function} Returns the new base function. */ //类似forIn或者forOwn方法,用于循环对象的属性 function createBaseFor(fromRight) { return function(object, iteratee, keysFunc) { var index = -1,//循环索引 iterable = Object(object), props = keysFunc(object),//对象的键组成的数组 length = props.length;//键数组的长度 while (length--) {//循环键数组 var key = props[fromRight ? length : ++index]; if (iteratee(iterable[key], key, iterable) === false) { //如果iteratee调用后返回false,就跳出循环 break; } } return object; }; } /** * 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 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; } /** * 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; return !!length && (typeof value == 'number' || reIsUint.test(value)) && (value > -1 && value % 1 == 0 && 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`. */ //检查一个值是一个合法属性名而不是属性路径 //value需要判断的值,object需要查询键的对象 function isKey(value, object) { if (isArray(value)) {//如果value是数组,返回false return false; } var type = typeof value;//value的typeof值 if (type == 'number' || type == 'symbol' || type == 'boolean' || value == null || isSymbol(value)) { //如果value是number、sumbol、boolean、null,说明是合法key,返回true return true; } return reIsPlainProp.test(value) || !reIsDeepProp.test(value) || (object != null && value in Object(object)); //reIsPlainProp = /^\w*$/ 匹配单词,说明是属性名 //reIsDeepProp = /\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/ 如果含有点.或者含有数组说明不是属性名 //如果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`. */ //判断一个值是否类似一个prototype对象 function isPrototype(value) { var Ctor = value && value.constructor, proto = (typeof Ctor == 'function' && Ctor.prototype) || objectProto; return value === proto; } /** * Gets the parent value at `path` of `object`. * * @private * @param {Object} object The object to query. * @param {Array} path The path to get the parent value of. * @returns {*} Returns the parent value. */ //根据对象的路径数组获取到倒数第二个父级值 function parent(object, path) { return path.length == 1 ? object : baseGet(object, baseSlice(path, 0, -1)); //如果路径数组长度只有1,则父级就是object,否则调用baseGet获取到对应元素,用baseSlice截掉路径数组的最后一项 } /** * Converts `string` to a property path array. * * @private * @param {string} string The string to convert. * @returns {Array} Returns the property path array. */ //将路径字符串转换成路径数组 var stringToPath = memoize(function(string) { string = toString(string);//将string转换成字符串格式 var result = [];//结果数组 if (reLeadingDot.test(string)) { //reLeadingDot = /^\./ //如果字符串的开头如果是一个点,结果数组第一个路径值就push一个空字符串 result.push(''); } //rePropName = /[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|$))/g string.replace(rePropName, function(match, number, quote, string) { result.push(quote ? string.replace(reEscapeChar, '$1') : (number || match)); //如果捕获到了quote,处理转义字符后赋值给key然后push进结果 //否则插入捕获到的number }); 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. */ //将一个值转换成字符串键,如果它不是字符串或者symbol对象 function toKey(value) { if (typeof value == 'string' || isSymbol(value)) {//如果value已经是字符串或者symbol,直接返回 return value; } var result = (value + '');//转成字符串 return (result == '0' && (1 / value) == -INFINITY) ? '-0' : result;//处理-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 ''; } /** * Gets the last element of `array`. * * @static * @memberOf _ * @since 0.1.0 * @category Array * @param {Array} array The array to query. * @returns {*} Returns the last element of `array`. * @example * * _.last([1, 2, 3]); * // => 3 */ //获取到数组最后一个元素 function last(array) { var length = array ? array.length : 0;//数组长度 return length ? array[length - 1] : undefined;//返回最后一个元素,没有长度就返回undefined } /** * Invokes the method at `path` of each element in `collection`, returning * an array of the results of each invoked method. Any additional arguments * are provided to each invoked method. If `path` is a function, it's invoked * for, and `this` bound to, each element in `collection`. * * @static * @memberOf _ * @since 4.0.0 * @category Collection * @param {Array|Object} collection The collection to iterate over. * @param {Array|Function|string} path The path of the method to invoke or * the function invoked per iteration. * @param {...*} [args] The arguments to invoke each method with. * @returns {Array} Returns the array of results. * @example * * _.invokeMap([[5, 1, 7], [3, 2, 1]], 'sort'); * // => [[1, 5, 7], [1, 2, 3]] * * _.invokeMap([123, 456], String.prototype.split, ''); * // => [['1', '2', '3'], ['4', '5', '6']] */ //引用path参数对应的方法处理collection中的每一个元素,将引用方法处理后的结果组成一个数组返回。 //任何额外的参数都被传递给每一次引用方法的调用 //如果path参数是一个方法,直接调用,如果不是,就是路径数组或者路径字符串,就去那个路径下寻找对应的方法 //调用引用方法的时候this被绑定到collection的每一个元素上 var invokeMap = baseRest(function(collection, path, args) { var index = -1,//循环索引 isFunc = typeof path == 'function',//path是否本身就是function isProp = isKey(path),//path是否本身就是对象的合法key result = isArrayLike(collection) ? Array(collection.length) : [];//结果数组 baseEach(collection, function(value) {//调用baseEach循环collection var func = isFunc ? path : ((isProp && value != null) ? value[path] : undefined); //如果path本身就是function,直接用;如果path本身就是合法key,就取value[path],否则func是undefined result[++index] = func ? apply(func, value, args) : baseInvoke(value, path, args); //结果数组的每个元素是调用func的返回值,如果有func直接apply调用,否则调用baseInvoke去通过路径查找到方法 }); return result; }); /** * 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参数,它被用来计算缓存对象上的key,基于传递给已经memoize化的函数的参数。 //将计算结果缓存在闭包中的私有变量中,如果再次计算同样的值就直接获取 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对象被定义到memoized方法上,cache使用自定义的MapCache类型 return memoized;//返回被memoize化的方法 } // 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 */ //使用SameValueZero规则判断两个值是否相等 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对象 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) : ''; return tag == funcTag || tag == genTag; } /** * Checks if `value` is a valid array-like length. * * **Note:** This method is loosely based on * [`ToLength`](http://ecma-international.org/ecma-262/7.0/#sec-tolength). * * @static * @memberOf _ * @since 4.0.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is a valid length, else `false`. * @example * * _.isLength(3); * // => true * * _.isLength(Number.MIN_VALUE); * // => false * * _.isLength(Infinity); * // => false * * _.isLength('3'); * // => false */ //判断一个值是否是一个有效的array-like对象的length属性 function isLength(value) { return typeof value == 'number' && value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER; } /** * 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对象 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); } /** * 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' */ //将值转换为字符串,如果value是null或者undefined,返回空字符串。-0的符号会被保留 function toString(value) { return value == null ? '' : baseToString(value); } /** * 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'] */ //创建一个对象的自身可枚举属性名组成的数组,类似原生的Object.keys() function keys(object) { return isArrayLike(object) ? arrayLikeKeys(object) : baseKeys(object); //如果object是array-like对象,调用arrayLikeKeys处理,否则使用baseKeys处理 } module.exports = invokeMap;

浙公网安备 33010602011771号