JavaScript实用库:Lodash源码数组函数解析(二)
本章内容主要是:difference、baseDifference、isArrayLikeObject、isArrayLike、baseRest
Lodash是一个非常好用方便的JavaScript的工具库,使得我们对数据处理能够更加得心应手
接下来我要对Lodash的源码进行剖析学习
每天几个小方法,跟着我一起来学lodash吧
1、_.difference(array, [values])
根据中文文档介绍,该函数就是可以将 array中的排除掉[values]中的元素,这是我个人的话语解释,我们来看中文文档解释
以下是例子:
例子中我们输入了两个数组,第一个数组保持了存在的唯一值
接下来我们看源码:
/**
* Creates an array of `array` values not included in the other given arrays
* using [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)
* for equality comparisons. The order and references of result values are
* determined by the first array.
*
* **Note:** Unlike `_.pullAll`, this method returns a new array.
*
* @static
* @memberOf _
* @since 0.1.0
* @category Array
* @param {Array} array The array to inspect.
* @param {...Array} [values] The values to exclude.
* @returns {Array} Returns the new array of filtered values.
* @see _.without, _.xor
* @example
*
* _.difference([2, 1], [2, 3]);
* // => [1]
*/
var difference = baseRest(function(array, values) {
return isArrayLikeObject(array)
? baseDifference(array, baseFlatten(values, 1, isArrayLikeObject, true))
: [];
});
module.exports = difference;
一上来我们就可以看到还使用了其他的很多方法,就像我们昨天的老朋友:扁平化数组 baseFlatten
这里的话 baseFlatten我就不过多介绍了
- baseDifference
/** Used as the size to enable large array optimizations. */
var LARGE_ARRAY_SIZE = 200;
/**
* The base implementation of methods like `_.difference` without support
* for excluding multiple arrays or iteratee shorthands.
*
* @private
* @param {Array} array The array to inspect.
* @param {Array} values The values to exclude.
* @param {Function} [iteratee] The iteratee invoked per element.
* @param {Function} [comparator] The comparator invoked per element.
* @returns {Array} Returns the new array of filtered values.
*/
function baseDifference(array, values, iteratee, comparator) {
var index = -1,
includes = arrayIncludes,
isCommon = true,
length = array.length,
result = [],
valuesLength = values.length;
// 判断如果数组的长度是 0,则返回空数组
if (!length) {
return result;
}
if (iteratee) {
values = arrayMap(values, baseUnary(iteratee));
}
if (comparator) {
includes = arrayIncludesWith;
isCommon = false;
}
// 当数组的长度大于 200 时执行,这是前面给出的常量。
else if (values.length >= LARGE_ARRAY_SIZE) {
includes = cacheHas;
isCommon = false;
values = new SetCache(values);
}
// 标签语法
outer:
while (++index < length) {
var value = array[index],
// 当 iteratee == null 时返回 value,否则返回 iteratee(value)
computed = iteratee == null ? value : iteratee(value);
// 当 comparator 存在或者 value !==0 时,value = value
value = (comparator || value !== 0) ? value : 0;
if (isCommon && computed === computed) {
var valuesIndex = valuesLength;
while (valuesIndex--) {
if (values[valuesIndex] === computed) {
// 如果 values 中有值等于 computed,就退出到最外层的循环,这就是标签语法的作用
continue outer;
}
}
result.push(value);
}
else if (!includes(values, computed, comparator)) {
result.push(value);
}
}
return result;
}
module.exports = baseDifference;
比如 baseDifference([1, 2], [2, 3, 4]) 这里 array = [1, 2] , values = [2, 3, 4]
初始值 result = [], valuesLength = 3
for 遍历,第一个值 value = 1, computed = 1, valuesIndex = 3;
values[2] = 4 !== value, values[1] = 3 !== value, values[0] = 2 !== value
result = [1]
接下来,遍历第二个值, value = 2, computed = 2, vlauesIndex = 3;
values[2] = 4 !== value, values[1] = 3 !== value, values[0] = 2 == value
当存在值相等的时候,直接跳转到最外层,所以 result = [1]
最后也就是说baseDifference([1, 2], [2, 3, 4]) = [1];
看到 outer:是不是一下子懵了,这就是标签语法,形式是 label: statement,这里的标签可以是除了保留字以外的任意标识符,当然语句也可以是任意语句。它有什么用呢?这个标签就相当于一个定位符,在 break, continue 后面用于指定跳转的位置。
作者:就想叫菜鸟
链接:https://www.jianshu.com/p/8946dc5d884e
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
没错,这个并非我的解释,我觉得这位作者写得挺好的就引用了一下
- isArrayLikeObject
/**
* 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
*/
function isArrayLikeObject(value) {
return isObjectLike(value) && isArrayLike(value);
}
module.exports = isArrayLikeObject;
里面又有一个isArrayLike的方法
- isArrayLike
/**
* 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
*/
function isArrayLike(value) {
return value != null && isLength(value.length) && !isFunction(value);
}
module.exports = isArrayLike;
isArrayLike方法比较简单,你会发现里面蹦出来个 isLength方法,这个我在这里不过多介绍,是用来判断数据长度是否是一个有限长度
所以总的来说 isArrayLikeObject是判断我们输入的数据是不是一个数组或类数组结构,是则返回true,反之false
- baseRest
/**
* 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.
*/
function baseRest(func, start) {
return setToString(overRest(func, start, identity), func + '');
}
module.exports = baseRest;
这个函数主要是对剩余参数的处理,它不用验证参数或者强制输入参数,这个我不过多解释,大家理解就好
那么搞了这么久,我们终于可以解释 difference方法发执行过程了,首先就是传出参数并进行 baseRest剩余参数处理,让后就是直接输出,我们首先判断array是不是数组,如果不是则返回空数组,如果是则进行下一步,将我们的 [values]通过 baseFlatten方法一级扁平化处理,最后通过 baseDifference方法取出唯一值
今天虽然只写了一个方法,但是它依赖的方法太多了,一起解释了一遍,今天就这样了,明天继续