lodash库_.get方法的实现走读

5,605 阅读2分钟

lodash的get方法

参数

  • object (Object): 要检索的对象。
  • path (Array|string): 要获取属性的路径。
  • [defaultValue] (*): 如果解析值是 undefined ,这值会被返回。

例子

var object = { 'a': [{ 'b': { 'c': 3 } }] };
 
_.get(object, 'a[0].b.c');
// => 3
 
_.get(object, ['a', '0', 'b', 'c']);
// => 3
 
_.get(object, 'a.b.c', 'default');
// => 'default'

get

  • 判断了对象为null返回undefined
  • baseGet,实际实现也在baseGet中
  • 第三个参数default 即result为undefined时默认值
function get(object, path, defaultValue) {
  const result = object == null ? undefined : baseGet(object, path)
  return result === undefined ? defaultValue : result
}

baseGet

  • cashPath 实际上就是把字符串转成数组,如果传入的就是数组就直接返回
  • tokey 对string类型和Symbol类型的key和数字类型做一层处理
  • 通过while读取传入的path==>object下的参数
function baseGet(object, path) {
  path = castPath(path, object)

  let index = 0
  const length = path.length

  while (object != null && index < length) {
    object = object[toKey(path[index++])]
  }
  return (index && index == length) ? object : undefined
}

cashPath

  • isKey 判断path是不是当前对象的key
  • stringToPath 如果传入的path不是当前对象的key,就调用该方法把字符串转成对应的数组
function castPath(value, object) {
  if (Array.isArray(value)) {
    return value
  }
  return isKey(value, object) ? [value] : stringToPath(value)
}

isKey

  • reIsDeepProp 这个正则匹配 'a[0].b.c' 这类字符串 (ps: 正则我看着就懵)
  • value in Object(object) 就是遍历value在不在当前对象内
  • isSymbol 判断当前对象是不是symbol类型 Object.prototype.toString.call
const reIsDeepProp = /\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/
const reIsPlainProp = /^\w*$/

/**
 * Checks if `value` is a property name and not a property path.
 *
 * @private
 * @param {*} value The value to check.
 * @param {Object} [object] The object to query keys on.
 * @returns {boolean} Returns `true` if `value` is a property name, else `false`.
 */
function isKey(value, object) {
  if (Array.isArray(value)) {
    return false
  }
  const type = typeof value
  if (type === 'number' || type === 'boolean' || value == null || isSymbol(value)) {
    return true
  }
  return reIsPlainProp.test(value) || !reIsDeepProp.test(value) ||
    (object != null && value in Object(object))
}

stringToPath

  • memoizeCapped 用来做缓存的
  • 通过rePropName正则replace操作 (ps: 这个正则我真有点懵)
const charCodeOfDot = '.'.charCodeAt(0)
const reEscapeChar = /\\(\\)?/g
const rePropName = RegExp(
  // Match anything that isn't a dot or bracket.
  '[^.[\\]]+' + '|' +
  // Or match property names within brackets.
  '\\[(?:' +
    // Match a non-string expression.
    '([^"\'][^[]*)' + '|' +
    // Or match strings (supports escaping characters).
    '(["\'])((?:(?!\\2)[^\\\\]|\\\\.)*?)\\2' +
  ')\\]'+ '|' +
  // Or match "" as the space between consecutive dots or empty brackets.
  '(?=(?:\\.|\\[\\])(?:\\.|\\[\\]|$))'
  , 'g')

/**
 * Converts `string` to a property path array.
 *
 * @private
 * @param {string} string The string to convert.
 * @returns {Array} Returns the property path array.
 */
const stringToPath = memoizeCapped((string) => {
  const result = []
  if (string.charCodeAt(0) === charCodeOfDot) {
    result.push('')
  }
  string.replace(rePropName, (match, expression, quote, subString) => {
    let key = match
    if (quote) {
      key = subString.replace(reEscapeChar, '$1')
    }
    else if (expression) {
      key = expression.trim()
    }
    result.push(key)
  })
  return result
})

memoizeCapped

  • memoize传入func 和 缓存大小限制回调
  • MAX_MEMOIZE_SIZE 最大限制缓存500条
const MAX_MEMOIZE_SIZE = 500

/**
 * A specialized version of `memoize` which clears the memoized function's
 * cache when it exceeds `MAX_MEMOIZE_SIZE`.
 *
 * @private
 * @param {Function} func The function to have its output memoized.
 * @returns {Function} Returns the new memoized function.
 */
function memoizeCapped(func) {
  const result = memoize(func, (key) => {
    const { cache } = result
    if (cache.size === MAX_MEMOIZE_SIZE) {
      cache.clear()
    }
    return key
  })

  return result

memoize

  • 通过Map缓存func,如果存在当前key就直接读缓存
function memoize(func, resolver) {
    if (typeof func !== 'function' || (resolver != null && typeof resolver !== 'function')) {
      throw new TypeError('Expected a function')
    }
    const memoized = function(...args) {
      const key = resolver ? resolver.apply(this, args) : args[0]
      const cache = memoized.cache
  
      if (cache.has(key)) {
        return cache.get(key)
      }
      const result = func.apply(this, args)
      memoized.cache = cache.set(key, result) || cache
      return result
    }
    memoized.cache = new (memoize.Cache || Map)
    return memoized
  }
  
  memoize.Cache = Map