0%

lodash源码解析:drop、dropWhile、dropRight、dropRightWhile、baseWhile

本篇分析lodashdrop系列方法,包括dropdropWhiledropRightdropRightWhile

引用的私有方法

baseWhile

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import slice from '../slice.js'

/**
* 比如`dropWhile` 和 `takeWhile`之类的方法的基础实现
*
* @private
* @param {Array} array 要查询的数组
* @param {Function} predicate 每次迭代时调用的函数
* @param {boolean} [isDrop] 指示丢弃还是保留元素。
* @param {boolean} [fromRight] 指示从右向左迭代
* @returns {Array} 返回剪切后的数组
*/
function baseWhile(array, predicate, isDrop, fromRight) {
// 获取数组长度
const { length } = array
// 根据fromRight获取起始位置
let index = fromRight ? length : -1

// 开始迭代,把运算都写到了迭代条件里了
// 从头到尾或从尾到头是给index规定了个[0,length-1]的范围
// && 符号后面的内容是看看什么时候predicate返回假值就结束,就可以拿到当前的index了
while ((fromRight ? index-- : ++index < length) &&
predicate(array[index], index, array)) {}

// 真值表
// idDrop为真,fromRight为真,就把[index,length]内容删掉;
// idDrop为真,fromRight为假,就把[0,index]内容删掉;
// idDrop为假,fromRight为真,就把[index,length]内容保留;
// idDrop为假,fromRight为假,就把[0,index]内容保留;
return isDrop
? slice(array, (fromRight ? 0 : index), (fromRight ? index + 1 : length))
: slice(array, (fromRight ? index + 1 : 0), (fromRight ? length : index))
}

export default baseWhile

引用的公开方法

在之前的文章中已经详细的把toIntegerslice及其引用的方法都分析过。

toInteger

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import toFinite from './toFinite.js'

/**
* 转换值为整数
*
* **注意:** 这个方法大致基于
* [`ToInteger`](http://www.ecma-international.org/ecma-262/7.0/#sec-tointeger).
*
* @since 4.0.0
* @category Lang
* @param {*} value 需要转换的值
* @returns {number} 返回转换后的整数
* @see isInteger, isNumber, toNumber
* @example
*
* toInteger(3.2)
* // => 3
*
* toInteger(Number.MIN_VALUE)
* // => 0
*
* toInteger(Infinity)
* // => 1.7976931348623157e+308
*
* toInteger('3.2')
* // => 3
*/
function toInteger(value) {
// 转换为有限数字
const result = toFinite(value)
// 对1取余
const remainder = result % 1
// 能取到余数就减去余数,返回了整数值
return remainder ? result - remainder : result
}

export default toInteger

slice

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
/**
* 创建一个数组,来源是裁剪数组array,从 start 位置开始到 end 位置结束,但不包括 end 本身的位置。
*
* **注意:** 这个方法被用来代替
* [`Array#slice`](https://mdn.io/Array/slice)确保返回的是个稠密数组。
*
* @since 3.0.0
* @category Array
* @param {Array} array 要裁剪的数组
* @param {number} [start=0] 开始位置。负数索引将会被看作从数组结束位置的向前偏移。
* @param {number} [end=array.length] 结束位置。负数索引将会被看作从数组结束位置的向前偏移。
* @returns {Array} 返回剪切后的数组。
* @example
*
* var array = [1, 2, 3, 4]
*
* _.slice(array, 2)
* // => [3, 4]
*/
function slice(array, start, end) {
// array是否为undefined或null,是的话则length为0
let length = array == null ? 0 : array.length
// length为假(undefined或0),则返回空数组
if (!length) {
return []
}
// start是否为undefined或null,是的话则start赋值为0
start = start == null ? 0 : start
// start是否为undefined,是的话则end赋值为length
end = end === undefined ? length : end
// 如果start小于0
if (start < 0) {
// 防止真正的start变为负数
start = -start > length ? 0 : (length + start)
}
// 防止end比length还大
end = end > length ? length : end
// 如果end小于0
if (end < 0) {
end += length
}
// 如果start大于end时,length赋值0,否则就使用>>>移位0确保length是个正整数
length = start > end ? 0 : ((end - start) >>> 0)
// 确保start是个正整数
start >>>= 0
// 返回结果初始化
let index = -1
const result = new Array(length)
// 循环赋值
while (++index < length) {
result[index] = array[index + start]
}
// 返回
return result
}

export default slice

drop 系列

drop系列方法其实就是两个区别,第一个是从左向右还是从右向左,第二个是是否使用n还是predicate来判断。

drop

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import slice from './slice.js'
import toInteger from './toInteger.js'

/**
* 创建一个`array`的切片,删掉array前n个元素。(n默认值为1。)
*
* @since 0.5.0
* @category Array
* @param {Array} array 要查询的数组
* @param {number} [n=1] 要丢弃的元素数
* @returns {Array} 返回数组的切片
* @example
*
* drop([1, 2, 3])
* // => [2, 3]
*
* drop([1, 2, 3], 2)
* // => [3]
*
* drop([1, 2, 3], 5)
* // => []
*
* drop([1, 2, 3], 0)
* // => [1, 2, 3]
*/
function drop(array, n=1) {
// 初始化length
const length = array == null ? 0 : array.length
// 数组有内容时返回slice(n,length)的切片
return length
? slice(array, n < 0 ? 0 : toInteger(n), length)
: []
}

export default drop

dropWhile

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import baseWhile from './.internal/baseWhile.js'

/**
* 创建一个切片数组,删除array中从第一个元素开始到 predicate函数 返回假值的元素结束的部分。
* predicate 会传入3个参数: (value, index, array)。
*
* @since 3.0.0
* @category Array
* @param {Array} array 要查询的数组
* @param {Function} predicate 每次迭代时调用的函数
* @returns {Array} 返回array的切片
* @example
*
* const users = [
* { 'user': 'barney', 'active': true },
* { 'user': 'fred', 'active': true },
* { 'user': 'pebbles', 'active': false }
* ]
*
* dropWhile(users, ({ active }) => active)
* // => objects for ['pebbles']
*/
function dropWhile(array, predicate) {
// 检查数组是否正常
return (array != null && array.length)
// 为正常数组时执行核心方法baseWhile
// baseWhile(array, predicate, isDrop, fromRight)
// 现在的情况是丢弃选中的元素,并且顺序是从左向右,实现了dropWhile
? baseWhile(array, predicate, true)
// 数组为null、undefined或length为0时返回[]
: []
}

export default dropWhile

dropRight

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import slice from './slice.js'
import toInteger from './toInteger.js'

/**
* 创建一个`array`的切片,删掉array的后n个元素。(n默认值为1。)
*
* @since 3.0.0
* @category Array
* @param {Array} array 要查询的数组
* @param {number} [n=1] 要丢弃的元素数
* @returns {Array} 返回数组的切片
* @example
*
* dropRight([1, 2, 3])
* // => [1, 2]
*
* dropRight([1, 2, 3], 2)
* // => [1]
*
* dropRight([1, 2, 3], 5)
* // => []
*
* dropRight([1, 2, 3], 0)
* // => [1, 2, 3]
*/
function dropRight(array, n=1) {
// 初始化length
const length = array == null ? 0 : array.length
// 先把n转化为从前向后数的索引数
n = length - toInteger(n)
// 数组有内容时返回slice(0,n)的切片
return length ? slice(array, 0, n < 0 ? 0 : n) : []
}

export default dropRight

dropRightWhile

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import baseWhile from './.internal/baseWhile.js'

/**
* 创建一个array的切片,去除array中从 predicate函数 返回假值的元素开始开始到最后一个元素的部分。
* predicate 会传入3个参数: (value, index, array)。
*
* @since 3.0.0
* @category Array
* @param {Array} array 要查询的数组
* @param {Function} predicate 每次迭代时调用的函数
* @returns {Array} 返回array的切片
* @example
*
* const users = [
* { 'user': 'barney', 'active': false },
* { 'user': 'fred', 'active': true },
* { 'user': 'pebbles', 'active': true }
* ]
*
* dropRightWhile(users, ({ active }) => active)
* // => objects for ['barney']
*/
function dropRightWhile(array, predicate) {
// 检查数组是否正常
return (array != null && array.length)
// 为正常数组时执行核心方法baseWhile
// baseWhile(array, predicate, isDrop, fromRight)
// 现在的情况是丢弃选中的元素,并且顺序是从右向左,实现了dropRightWhile
? baseWhile(array, predicate, true, true)
// 数组为null、undefined或length为0时返回[]
: []
}

export default dropRightWhile

原生实现

原生实现drop时,其实使用Array.prototype.slice方法就可以,主要记住三个要点:

  1. slice方法不改变原数组,返回一个新的数组。
  2. slice方法返回的新数组中的元素是原数对应元素的浅拷贝
  3. slice方法剪切时包含start的元素,不包含end的元素

drop

1
2
3
4
5
6
// 原生写法
[1, 2, 3].slice(1);
// => [2, 3]

[1, 2, 3].slice(2);
// => [3]

dropRight

1
2
3
4
5
6
// 原生写法
[1, 2, 3].slice(0, -1);
// => [1, 2]

[1, 2, 3].slice(0, -2);
// => [1]
👆 全文结束,棒槌时间到 👇