这个文章的意义在于 想学习loadsh的所有源码 从github的顺序开始看(严格按照顺序的,所以难免有集合函数数组这样的跳跃) loadsh很多运算我们也可以写出来,但是严谨性可复用性却没有loadsh强. __________________是分界线,因为lodash里面有很多内部函数,先解读内部函数再写主源码 1.add.js import createMathOperation createMathOperation 是lodash内部源码的一个创建Math的一个运算函数 验证参数将其转化为数字,如果其中有一个不能转换就返回可以转换的那个.如果两个都不行,就返回默认的,这里的0相当于报错. ^(* ̄(oo) ̄)^(安利一波TS TS的类型检测就应该不用这些代码了.) function createMathOperation(operator, defaultValue) { return (value, other) => { if (value === undefined && other === undefined) { return defaultValue } if (value !== undefined && other === undefined) { return value } if (other !== undefined && value === undefined) { return other } if (typeof value === 'string' || typeof other === 'string') { value = baseToString(value) other = baseToString(other) } else { value = baseToNumber(value) other = baseToNumber(other) } return operator(value, other) } }
const add = createMathOperation((augend, addend) => augend + addend, 0) //这里就是调用了 2.after.js 调用>n次后 运行 function after(n, func) { if (typeof func != 'function') { throw new TypeError('Expected a function') } return function(...args) { if (--n < 1) { return func.apply(this, args) } } } //基本的闭包和柯里化 这里注意的点就是 --这个自减顺序 3.
4.attempt.js import isError function attempt(func, ...args) { try { return func(...args) } catch (e) { return isError(e) ? e : new Error(e) } } //调用函数返回错误或者结果,就是一个检测工具函数 5.before.js function before(n, func) { let result if (typeof func != 'function') { throw new TypeError('Expected a function') } return function(...args) { if (--n > 0) { result = func.apply(this, args) } if (n <= 1) { func = undefined } return result } } //跟after是一个相反类似函数 6. 7.castArray.js //转化值为数组 function castArray(...args) { if (!args.length) { return [] } const value = args[0] return Array.isArray(value) ? value : [value] } 8.ceil.js
9.chunk.js //切割数组 function chunk(array, size) { size = Math.max(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 } 10.clamp.js function clamp(number, lower, upper) { number = +number lower = +lower upper = +upper lower = lower === lower ? lower : 0 upper = upper === upper ? upper : 0 if (number === number) { number = number <= upper ? number : upper number = number >= lower ? number : lower } return number } 11.clone.js import baseclone.js 下面是baseclone.js源码 因为很长,我就概述一下
import Stack from './Stack.js'
import arrayEach from './arrayEach.js' import assignValue from './assignValue.js' import cloneBuffer from './cloneBuffer.js' import copyArray from './copyArray.js' import copyObject from './copyObject.js' import cloneArrayBuffer from './cloneArrayBuffer.js' import cloneDataView from './cloneDataView.js' import cloneRegExp from './cloneRegExp.js' import cloneSymbol from './cloneSymbol.js' import cloneTypedArray from './cloneTypedArray.js' import copySymbols from './copySymbols.js' import copySymbolsIn from './copySymbolsIn.js' import getAllKeys from './getAllKeys.js' import getAllKeysIn from './getAllKeysIn.js' import getTag from './getTag.js' import initCloneObject from './initCloneObject.js' import isBuffer from '../isBuffer.js' import isObject from '../isObject.js' import keys from '../keys.js' import keysIn from '../keysIn.js'
/** Used to compose bitmasks for cloning. */ const CLONE_DEEP_FLAG = 1 const CLONE_FLAT_FLAG = 2 const CLONE_SYMBOLS_FLAG = 4
/** Object#toString result references. */
const argsTag = '[object Arguments]'
const arrayTag = '[object Array]'
const boolTag = '[object Boolean]'
const dateTag = '[object Date]'
const errorTag = '[object Error]'
const mapTag = '[object Map]'
const numberTag = '[object Number]'
const objectTag = '[object Object]'
const regexpTag = '[object RegExp]'
const setTag = '[object Set]'
const stringTag = '[object String]'
const symbolTag = '[object Symbol]'
const weakMapTag = '[object WeakMap]'
const arrayBufferTag = '[object ArrayBuffer]' const dataViewTag = '[object DataView]' const float32Tag = '[object Float32Array]' const float64Tag = '[object Float64Array]' const int8Tag = '[object Int8Array]' const int16Tag = '[object Int16Array]' const int32Tag = '[object Int32Array]' const uint8Tag = '[object Uint8Array]' const uint8ClampedTag = '[object Uint8ClampedArray]' const uint16Tag = '[object Uint16Array]' const uint32Tag = '[object Uint32Array]'
/** Used to identify toStringTag values supported by clone. */
const cloneableTags = {}
cloneableTags[argsTag] = cloneableTags[arrayTag] =
cloneableTags[arrayBufferTag] = cloneableTags[dataViewTag] =
cloneableTags[boolTag] = cloneableTags[dateTag] =
cloneableTags[float32Tag] = cloneableTags[float64Tag] =
cloneableTags[int8Tag] = cloneableTags[int16Tag] =
cloneableTags[int32Tag] = cloneableTags[mapTag] =
cloneableTags[numberTag] = cloneableTags[objectTag] =
cloneableTags[regexpTag] = cloneableTags[setTag] =
cloneableTags[stringTag] = cloneableTags[symbolTag] =
cloneableTags[uint8Tag] = cloneableTags[uint8ClampedTag] =
cloneableTags[uint16Tag] = cloneableTags[uint32Tag] = true
cloneableTags[errorTag] = cloneableTags[weakMapTag] = false
/** Used to check objects for own properties. */ const hasOwnProperty = Object.prototype.hasOwnProperty
/**
-
Initializes an object clone based on its
toStringTag. -
Note: This function only supports cloning values with tags of
-
Boolean,Date,Error,Map,Number,RegExp,Set, orString. -
@private
-
@param {Object} object The object to clone.
-
@param {string} tag The
toStringTagof the object to clone. -
@param {boolean} [isDeep] Specify a deep clone.
-
@returns {Object} Returns the initialized clone. */ function initCloneByTag(object, tag, isDeep) { const Ctor = object.constructor switch (tag) { case arrayBufferTag: return cloneArrayBuffer(object)
case boolTag: case dateTag: return new Ctor(+object)
case dataViewTag: return cloneDataView(object, isDeep)
case float32Tag: case float64Tag: case int8Tag: case int16Tag: case int32Tag: case uint8Tag: case uint8ClampedTag: case uint16Tag: case uint32Tag: return cloneTypedArray(object, isDeep)
case mapTag: return new Ctor
case numberTag: case stringTag: return new Ctor(object)
case regexpTag: return cloneRegExp(object)
case setTag: return new Ctor
case symbolTag: return cloneSymbol(object) } }
/**
- Initializes an array clone.
- @private
- @param {Array} array The array to clone.
- @returns {Array} Returns the initialized clone. */ function initCloneArray(array) { const { length } = array const result = new array.constructor(length)
// Add properties assigned by RegExp#exec.
if (length && typeof array[0] == 'string' && hasOwnProperty.call(array, 'index')) {
result.index = array.index
result.input = array.input
}
return result
}
/**
- The base implementation of
cloneandcloneDeepwhich tracks - traversed objects.
- @private
- @param {*} value The value to clone.
- @param {number} bitmask The bitmask flags.
- 1 - Deep clone
- 2 - Flatten inherited properties
- 4 - Clone symbols
- @param {Function} [customizer] The function to customize cloning.
- @param {string} [key] The key of
value. - @param {Object} [object] The parent object of
value. - @param {Object} [stack] Tracks traversed objects and their clone counterparts.
- @returns {*} Returns the cloned value. */ function baseClone(value, bitmask, customizer, key, object, stack) { let result const isDeep = bitmask & CLONE_DEEP_FLAG const isFlat = bitmask & CLONE_FLAT_FLAG const isFull = bitmask & CLONE_SYMBOLS_FLAG
if (customizer) { result = object ? customizer(value, key, object, stack) : customizer(value) } if (result !== undefined) { return result } if (!isObject(value)) { return value } const isArr = Array.isArray(value) const tag = getTag(value) if (isArr) { result = initCloneArray(value) if (!isDeep) { return copyArray(value, result) } } else { const isFunc = typeof value == 'function'
if (isBuffer(value)) {
return cloneBuffer(value, isDeep)
}
if (tag == objectTag || tag == argsTag || (isFunc && !object)) {
result = (isFlat || isFunc) ? {} : initCloneObject(value)
if (!isDeep) {
return isFlat
? copySymbolsIn(value, copyObject(value, keysIn(value), result))
: copySymbols(value, Object.assign(result, value))
}
} else {
if (isFunc || !cloneableTags[tag]) {
return object ? value : {}
}
result = initCloneByTag(value, tag, isDeep)
}
} // Check for circular references and return its corresponding clone. stack || (stack = new Stack) const stacked = stack.get(value) if (stacked) { return stacked } stack.set(value, result)
if (tag == mapTag) { value.forEach((subValue, key) => { result.set(key, baseClone(subValue, bitmask, customizer, key, value, stack)) }) return result }
if (tag == setTag) { value.forEach((subValue) => { result.add(baseClone(subValue, bitmask, customizer, subValue, value, stack)) }) return result }
if (isTypedArray(value)) { return result }
const keysFunc = isFull ? (isFlat ? getAllKeysIn : getAllKeys) : (isFlat ? keysIn : keys)
const props = isArr ? undefined : keysFunc(value) arrayEach(props || value, (subValue, key) => { if (props) { key = subValue subValue = value[key] } // Recursively populate clone (susceptible to call stack limits). assignValue(result, key, baseClone(subValue, bitmask, customizer, key, value, stack)) }) return result }