每次使用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'
*/
分析
- get函数主要解决的问题就是在取层级较深的对象属性的时候,可以不用一层层校验是否存在,做了一个降错处理。
- 第二个参数可能是数组或者是字符串,实现的时候应该把字符串也拆分成数组去做,因为也是要一层层去拿想要的值的。
- 第三个参数是默认参数,这个应该比较好做,当拿不到对应的值是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上的。
站在巨人的肩膀上,但是记得有一颗成为巨人的心。