手撸一个码—lodash.get

1,560 阅读2分钟

每次使用lodash直接梭还是真香,但要是只会梭,那就是API搬砖机器了。今天咱们来看看砖是啥做的,学门手艺。

需求

介绍

来我们看看lodash的get函数的介绍

 /**
 * Gets the value at `path` of `object`. If the resolved value is
 * `undefined`, the `defaultValue` is returned in its place.
 *
 * @since 3.7.0
 * @category Object
 * @param {Object} object The object to query.
 * @param {Array|string} path The path of the property to get.
 * @param {*} [defaultValue] The value returned for `undefined` resolved values.
 * @returns {*} Returns the resolved value.
 * @see has, hasIn, set, unset
 * @example
 *
 * const 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'
 */

分析

  1. get函数主要解决的问题就是在取层级较深的对象属性的时候,可以不用一层层校验是否存在,做了一个降错处理。
  2. 第二个参数可能是数组或者是字符串,实现的时候应该把字符串也拆分成数组去做,因为也是要一层层去拿想要的值的。
  3. 第三个参数是默认参数,这个应该比较好做,当拿不到对应的值是undefined的时候就改成默认的值就可以。

实现

仔细分析了一波,发现这个题咱好像会。产品,这个需求不用放下迭代了(骄傲)。

对参数拆解

'a[0].b.c' => ['a', '0', 'b', 'c']

实现一个handleStrToAry函数实现上面的字符串到数组的转换

/**
 * 将输入的字符串以指定的格式输出
 * @param {String} string 
 */
function handleStrToAry(string) {
    let result = [];

    // 'a[0].b.c' => ['a[0]', 'b', 'c'] 
    let keyAry = string.split('.');
    keyAry.forEach(key => {
        
        if(key.match(/\[(.+?)\]/g)){
            let key1 = key.split('[')[0]; //a
            let key2 = RegExp.$1; // 0
            result = result.concat([key1, key2]);
            return;
        }
        result.push(key);
    })
    return result;
}
handleStrToAry('a[0].b.c'); //  ['a', '0', 'b', 'c'] 

key.split('[')[0]这个是很挫的操作,实在正则不太熟,搞不了骚操作。

但是可以满足当前的a[0]操作,又不是不能用

获取层级属性值

/**
 * 
 * @param {Object} obj 默认值
 * @param {Array} key 属性数组
 * @param {*} defaultValue 默认值
 * */
function _get(obj, key, defaultValue) {
    let result = obj;
    key.forEach(item => {
        let value = result[item];
        if(value){
            result = value;
        }else{
            result = defaultValue;
        }
    })
    return result;
}

包装一下

function get(obj, key, defaultValue) {
    let keyType = typeof key;
    if (keyType === 'string') {

        // 'a[0].b.c' => ['a', '0', 'b', 'c'] 
        // 输入的字符串转换为数组
        key = handleStrToAry(key);
    }
    if (!Array.isArray(key)) {
        return defaultValue;
    }
    return _get(obj, key, defaultValue);
}

跑一跑

onst object = {
    'a': [{
        'b': {
            'c': 3
        }
    }]
};
console.log(get(object, 'a[0].b.c'));
console.log(get(object, ['a', '0', 'b', 'c']));
console.log(get(object, 'a.b.c', 'default'));

友商分析

友商躺枪。

写了那么多,也不知道lodash是咋样花里胡哨的,但应该也能用。

Don't talk, show me your code

拆解属性值

lodash\.internal\stringToPath.js

import memoizeCapped from './memoizeCapped.js'

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
})

export default stringToPath

stringToPath('a[0][0].b') => ['a', 0, 0, 'b']

这...真香,不仅可以满足a[0]还可以a[0][0]这样的操作。

会正则真的是秀啊

其他

一个get函数,lodash是get,baseGet,stringToPath等几个函数文件一起组合出来的。

在解耦上,保持函数专一功能,低耦合。lodash还是强。

最后

lodash.get这个函数实现很简单,功能也很简单。但是我跟lodash就不是一个level上的。

站在巨人的肩膀上,但是记得有一颗成为巨人的心。