本篇继续分析下 zip
家族的方法,zip
方法主要是把目标数组群合成为数组或对象。合成数组的方法包括zip
、unzip
、zipWith
、unzipWith
,合成对象的方法包括zipObject
、zipObjectDeep
及核心方法baseZipObject
和baseSet
。
对应源码分析已推到 github
仓库: https://github.com/MageeLin/lodash-source-code-analysis
zip
家族方法的依赖路径图如下所示:
zip
zip
方法比较有意思,它和 unzip
既是互为逆操作,却又是相同的操作。在 zip
方法内部就是调用的 return unzip(arrays)
。
unzip
unzip
是 zip
、zipWith
和 unzipWith
实现的基础。但是 unzip
本质上其实只有两步:
- 找到
arrays
中最长的 array
的长度;
- 从
arrays
的每个 array
中拿出对应位置的元素组装到一起,形成新的 arrays
;
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
| import filter from './filter.js'; import map from './map.js'; import baseProperty from './.internal/baseProperty.js'; import isArrayLikeObject from './isArrayLikeObject.js';
function unzip(array) { if (!(array != null && array.length)) { return []; } let length = 0; array = filter(array, (group) => { if (isArrayLikeObject(group)) { length = Math.max(group.length, length); return true; } }); let index = -1; const result = new Array(length); while (++index < length) { result[index] = map(array, baseProperty(index)); } return result; }
export default unzip;
|
zip
刚才说zip
方法和 unzip
既是互为逆操作,却又是相同的操作。就是因为本质上都是调用 unzip
,但是对同一个 arrays
不停的用 unzip
来调用时,其实就是在不停的重复形成两个数组。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| import unzip from './unzip.js';
function zip(...arrays) { return unzip(arrays); }
export default zip;
|
unZipWith
同样的原理 unZipWith
和 zipWith
也是同样的关系。但是他们都依赖了 unZip
,只不过是每次循环的时候调用了一次 iteratee
。
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
| import map from './map.js'; import unzip from './unzip.js';
function unzipWith(array, iteratee) { if (!(array != null && array.length)) { return []; } const result = unzip(array); return map(result, (group) => iteratee.apply(undefined, group)); }
export default unzipWith;
|
zipWith
zipWith
中需要注意的地方是对 iteratee
参数的判断,如果最后一个参数是函数,就把他从 arrays
中 pop
出去。
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
| import unzipWith from './unzipWith.js';
function zipWith(...arrays) { const length = arrays.length; let iteratee = length > 1 ? arrays[length - 1] : undefined; iteratee = typeof iteratee === 'function' ? (arrays.pop(), iteratee) : undefined; return unzipWith(arrays, iteratee); }
export default zipWith;
|
zipObject
zipObject
方法和 zip
系列的功能不太一样,zipObject
接受的两个参数,第一个是键的数组,第二个是值的数组,最后拼成一个对象。
下面是前置依赖的方法 assignValue
和 baseSet
,前者用于普通赋值,后者用于深度赋值。
baseAssignValue
这个地方不太懂为什么要给 enumerable: true
,__proto__
的 enumerable
不应该是 false
吗?需要再思考思考。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
|
function baseAssignValue(object, key, value) { if (key == '__proto__') { Object.defineProperty(object, key, { configurable: true, enumerable: true, value: value, writable: true, }); } else { object[key] = value; } }
export default baseAssignValue;
|
assignValue
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
| import baseAssignValue from './baseAssignValue.js'; import eq from '../eq.js';
const hasOwnProperty = Object.prototype.hasOwnProperty;
function assignValue(object, key, value) { const objValue = object[key];
if (!(hasOwnProperty.call(object, key) && eq(objValue, value))) { if (value !== 0 || 1 / value === 1 / objValue) { baseAssignValue(object, key, value); } } else if (value === undefined && !(key in object)) { baseAssignValue(object, key, value); } }
export default assignValue;
|
baseSet
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
| import assignValue from './assignValue.js'; import castPath from './castPath.js'; import isIndex from './isIndex.js'; import isObject from '../isObject.js'; import toKey from './toKey.js';
function baseSet(object, path, value, customizer) { if (!isObject(object)) { return object; }
path = castPath(path, object);
const length = path.length; const lastIndex = length - 1;
let index = -1; let nested = object;
while (nested != null && ++index < length) { const key = toKey(path[index]); let newValue = value;
if (index != lastIndex) { const objValue = nested[key]; newValue = customizer ? customizer(objValue, key, nested) : undefined; if (newValue === undefined) { newValue = isObject(objValue) ? objValue : isIndex(path[index + 1]) ? [] : {}; } } assignValue(nested, key, newValue); nested = nested[key]; } return object; }
export default baseSet;
|
baseZipObject
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
|
function baseZipObject(props, values, assignFunc) { let index = -1; const length = props.length; const valsLength = values.length; const result = {};
while (++index < length) { const value = index < valsLength ? values[index] : undefined; assignFunc(result, props[index], value); } return result; }
export default baseZipObject;
|
zipObject
zipObject
方法在调用 baseZipObject
时,传的参数为 assignValue
,所以是根据 props
的每个元素直接当 key
,实现赋值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| import assignValue from './.internal/assignValue.js'; import baseZipObject from './.internal/baseZipObject.js';
function zipObject(props, values) { return baseZipObject(props || [], values || [], assignValue); }
export default zipObject;
|
zipObjectDeep
zipObjectDeep
方法在调用 baseZipObject
时,传的参数为 baseSet
,所以是根据 props
的每个元素的不同形式来进行深度赋值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| import baseSet from './.internal/baseSet.js'; import baseZipObject from './.internal/baseZipObject.js';
function zipObjectDeep(props, values) { return baseZipObject(props || [], values || [], baseSet); }
export default zipObjectDeep;
|
前端记事本,不定期更新,欢迎关注!