Lodash 源码大全

1,207 阅读4分钟

这个文章的意义在于 想学习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, or String.

  • @private

  • @param {Object} object The object to clone.

  • @param {string} tag The toStringTag of 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 clone and cloneDeep which 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 }