2

百忙之中(闲来无事)想抽点时间好好读一下源码,于是就选了Lodash来写一个系列罢。写了几篇笔记以后发现很多函数大量依赖了内部的基础函数,一次性读完有点麻烦,所以还是决定从基础函数(没有或者很少依赖其他函数)看起。

文档地址:中文文档   英文文档
源码地址:gayhub


baseGetTag

baseGetTag,判断变量类型。因为typeof在面对new Number(1)这种情况时力有不逮,所以lodash重写了类型判断。 javascript秘密花园中也提到了JavaScript 标准库 推荐的是Object.prototype.toString.call(value)这种方式来判断类型,不过lodash明显做的更多,前半段是推荐的方式,后半段目测是针对Symbol类型做了优化,具体步骤待研究,有了解的同学欢迎告诉我。

const objectProto = Object.prototype
const hasOwnProperty = objectProto.hasOwnProperty
const toString = objectProto.toString
const symToStringTag = typeof Symbol != 'undefined'
    ? Symbol.toStringTag
    : undefined

function baseGetTag(value) {
    if (value == null) {
        return value === undefined
            ? '[object Undefined]'
            : '[object Null]'
    }
    if (!(symToStringTag && symToStringTag in Object(value))) {
        return toString.call(value)
    }
    const isOwn = hasOwnProperty.call(value, symToStringTag)
    const tag = value[symToStringTag]
    let unmasked = false
    try {
        value[symToStringTag] = undefined
        unmasked = true
    } catch (e) {}

    const result = toString.call(value)
    if (unmasked) {
        if (isOwn) {
            value[symToStringTag] = tag
        } else {
            delete value[symToStringTag]
        }
    }
    return result
}

export default baseGetTag

getTag

getTag是在baseGetTag基础上的包装,主要是为了兼容IE 11中的 data views, maps, sets, weak maps 和当Node.js < 6时的promises,这一点在源码注释中已有体现

import baseGetTag from './baseGetTag.js'

/** `Object#toString` result references. */
const dataViewTag = '[object DataView]'
const mapTag = '[object Map]'
const objectTag = '[object Object]'
const promiseTag = '[object Promise]'
const setTag = '[object Set]'
const weakMapTag = '[object WeakMap]'

/** Used to detect maps, sets, and weakmaps. */
const dataViewCtorString = `${DataView}`
const mapCtorString = `${Map}`
const promiseCtorString = `${Promise}`
const setCtorString = `${Set}`
const weakMapCtorString = `${WeakMap}`

let getTag = baseGetTag

// Fallback for data views, maps, sets, and weak maps in IE 11 and promises in
// Node.js < 6.
if ((DataView && getTag(new DataView(new ArrayBuffer(1))) != dataViewTag) || (getTag(new Map) != mapTag) || (getTag(Promise.resolve()) != promiseTag) || (getTag(new Set) != setTag) || (getTag(new WeakMap) != weakMapTag)) {
    getTag = (value) => {
        const result = baseGetTag(value)
        const Ctor = result == objectTag
            ? value.constructor
            : undefined
        const ctorString = Ctor
            ? `${Ctor}`
            : ''

        if (ctorString) {
            switch (ctorString) {
                case dataViewCtorString:
                    return dataViewTag
                case mapCtorString:
                    return mapTag
                case promiseCtorString:
                    return promiseTag
                case setCtorString:
                    return setTag
                case weakMapCtorString:
                    return weakMapTag
            }
        }
        return result
    }
}

export default getTag

isObjectLike

isObjectLike, 判断是否是一个对象

function isObjectLike(value) {
  return typeof value == 'object' && value !== null
}
export default isObjectLike

isArguments

isArguments调用了getTagisObjectLike用来判断参数是否是一个Arguments对象。

import getTag from './.internal/getTag.js'
import isObjectLike from './isObjectLike'
            
function isArguments(value) {
  return isObjectLike(value) && getTag(value) == '[object Arguments]'
}
            
export default isArguments

isFlattenable

isFlattenable调用isArguments来判断是否为一个Arguments对象或数组或是一个能展开的Symbol

import isArguments from '../isArguments.js'

const spreadableSymbol = Symbol.isConcatSpreadable

function isFlattenable(value) {
  return Array.isArray(value) || isArguments(value) ||
    !!(spreadableSymbol && value && value[spreadableSymbol])
}

export default isFlattenable

baseFlatten

baseFlatten可以展开数组的n层嵌套(扁平处理),依赖了isFlattenable

    // 例:
    baseFlatten([[[1,2,3],[2,2,3]],4,5,6], 2) // => [1, 2, 3, 2, 2, 3, 4, 5, 6]
    // isStrict == true
    baseFlatten([[1,2,3],[2,2,3],4,5,6], 1, null, true) // => [1, 2, 3, 2, 2, 3]
import isFlattenable from './isFlattenable.js'

function baseFlatten(array, depth, predicate, isStrict, result) {
  predicate || (predicate = isFlattenable) // predicate参数兜底
  result || (result = []) // 返回新数组

  if (array == null) {
    return result
  }

    // 递归调用本身,严格模式`isStrict = true`直接退出循环
  for (const value of array) {
    if (depth > 0 && predicate(value)) {
      if (depth > 1) {
        // Recursively flatten arrays (susceptible to call stack limits).
        baseFlatten(value, depth - 1, predicate, isStrict, result)
      } else {
        result.push(...value)
      }
    } else if (!isStrict) {
      result[result.length] = value
    }
  }
  return result
}

export default baseFlatten

isLength

isLength,判断是否是一个有效数字

const MAX_SAFE_INTEGER = 9007199254740991 // 最大数
function isLength(value) {
  // type是number并且大于等于0且小于等于最大数
  return typeof value == 'number' &&
    value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER
}

export default isLength

isArrayLike

isArrayLike, 判断是否是一个数组,依赖了isLength

import isLength from './isLength.js'

function isArrayLike(value) {
   // value不为空,不是function且有length属性
  return value != null && typeof value != 'function' && isLength(value.length)
}

export default isArrayLike

isArrayLikeObject

isArrayLikeObject,判断是否是一个数组或者对象,依赖了isArrayLikeisObjectLike

import isArrayLike from './isArrayLike.js'
import isObjectLike from './isObjectLike.js'

function isArrayLikeObject(value) {
  return isObjectLike(value) && isArrayLike(value)
}

export default isArrayLikeObject

eq

eq,判断两个值是否相等。

function eq(value, other) {
  return value === other || (value !== value && other !== other)
}

export default eq

||前面好理解,判断两个值全等
||后面是为了判断 NaN

例:

const object = { 'a': 1 }
const other = { 'a': 1 }

eq(object, object)
// => true

eq(object, other)
// => false

eq('a', 'a')
// => true

eq('a', Object('a'))
// => false

eq(NaN, NaN)
// => true

assocIndexOf

获取键值对数组中的“键”的索引。例:

assocIndexOf([[1,2],[3,4],[5,6]], 2) // => -1
assocIndexOf([[1,2],[3,4],[5,6]], 3) // => 1
import eq from '../eq.js'

function assocIndexOf(array, key) {
  let { length } = array
  while (length--) {
    if (eq(array[length][0], key)) {
      return length
    }
  }
  return -1
}

export default assocIndexOf

我有一只小腊肠
304 声望17 粉丝

曾经真的以为自己永远都不会胖..