上一阵子一直在忙着对前端界做出一点微小的贡献,再过两个多月就能揭晓。现在打算开一个天坑,把 lodash 的源码挨个解析一遍,学习下 npm 下载量最大、依赖最多的库的源码逻辑。
解析的代码为 2020 年 7 月 18 日的lodash 源码,版本是4.17.15,fork 到了自己的仓库中,顺序按照官网文档的顺序,在解析时会将该方法依赖的子方法也会全部分析下。有很多自己难理解或理解错的地方,抛砖引玉。
chunk
文档地址:https://lodash.com/docs/4.17.15#chunk
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
| import slice from './slice.js'; import toInteger from './toInteger.js';
function chunk(array, size = 1) { size = Math.max(toInteger(size), 0); const length = array == null ? 0 : array.length; if (!length || size < 1) { return []; } let index = 0; let resIndex = 0; const result = new Array(Math.ceil(length / size));
while (index < length) { result[resIndex++] = slice(array, index, (index += size)); } return result; }
export default chunk;
|
slice
文档地址:https://lodash.com/docs/4.17.15#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
|
function slice(array, start, end) { let length = array == null ? 0 : array.length; if (!length) { return []; } start = start == null ? 0 : start; end = end === undefined ? length : end; if (start < 0) { start = -start > length ? 0 : length + start; } end = end > length ? length : end; if (end < 0) { end += length; } length = start > end ? 0 : (end - start) >>> 0; start >>>= 0; let index = -1; const result = new Array(length); while (++index < length) { result[index] = array[index + start]; } return result; }
export default slice;
|
toInteger
文档地址:https://lodash.com/docs/4.17.15#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';
function toInteger(value) { const result = toFinite(value); const remainder = result % 1; return remainder ? result - remainder : result; }
export default toInteger;
|
toFinite
文档地址:https://lodash.com/docs/4.17.15#toFinite
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
| import toNumber from './toNumber.js';
const INFINITY = 1 / 0; const MAX_INTEGER = 1.7976931348623157e308;
function toFinite(value) { if (!value) { return value === 0 ? value : 0; } value = toNumber(value); if (value === INFINITY || value === -INFINITY) { const sign = value < 0 ? -1 : 1; return sign * MAX_INTEGER; } return value === value ? value : 0; }
export default toFinite;
|
toNumber
文档地址:https://lodash.com/docs/4.17.15#toNumber
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 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
| import isObject from './isObject.js'; import isSymbol from './isSymbol.js';
const NAN = 0 / 0;
const reTrim = /^\s+|\s+$/g;
const reIsBadHex = /^[-+]0x[0-9a-f]+$/i;
const reIsBinary = /^0b[01]+$/i;
const reIsOctal = /^0o[0-7]+$/i;
const freeParseInt = parseInt;
function toNumber(value) { if (typeof value === 'number') { return value; } if (isSymbol(value)) { return NAN; } if (isObject(value)) { const other = typeof value.valueOf === 'function' ? value.valueOf() : value; value = isObject(other) ? `${other}` : other; } if (typeof value !== 'string') { return value === 0 ? value : +value; } value = value.replace(reTrim, ''); const isBinary = reIsBinary.test(value); return isBinary || reIsOctal.test(value) ? freeParseInt(value.slice(2), isBinary ? 2 : 8) : reIsBadHex.test(value) ? NAN : +value; }
export default toNumber;
|
isObject
文档地址:https://lodash.com/docs/4.17.15#isObject
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
|
function isObject(value) { const type = typeof value; return value != null && (type === 'object' || type === 'function'); }
export default isObject;
|
isSymbol
文档地址:https://lodash.com/docs/4.17.15#isSymbol
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
| import getTag from './.internal/getTag.js';
function isSymbol(value) { const type = typeof value; return ( type == 'symbol' || (type === 'object' && value != null && getTag(value) == '[object Symbol]') ); }
export default isSymbol;
|
getTag
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| const toString = Object.prototype.toString;
function getTag(value) { if (value == null) { return value === undefined ? '[object Undefined]' : '[object Null]'; } return toString.call(value); }
export default getTag;
|
纯JS实现
在找lodash源码的时候发现了一个很有意思的仓库叫You-Dont-Need-Lodash-Underscore,使用纯JS实现了Lodash/Underscore的很多方法。在能明确自己的变量类型并且不想很重的引入lodash时可以自己写这些方法,同样来解析下它。
chunk
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
| _.chunk(['a', 'b', 'c', 'd'], 2);
_.chunk(['a', 'b', 'c', 'd'], 3);
const chunk = (input, size) => { return input.reduce((arr, item, idx) => { return idx % size === 0 ? [...arr, [item]] : [...arr.slice(0, -1), [...arr.slice(-1)[0], item]]; }, []); };
chunk(['a', 'b', 'c', 'd'], 2);
chunk(['a', 'b', 'c', 'd'], 3);
|
slice
slice就很尴尬了,Array.prototype
上的原生方法。
1 2 3 4 5 6 7 8 9
| var array = [1, 2, 3, 4] console.log(_.slice(array, 1, 3))
var array = [1, 2, 3, 4] console.log(array.slice(1, 3));
|