欢迎在下方发表您的优质见解
在 js 中经常会出现嵌套调用这种情况,如 a.b.c.d.e,但是这么写很容易抛出异常。你需要这么写 a && a.b && a.b.c && a.b.c.d && a.b.c.d.e,但是显得有些啰嗦与冗长了。特别是在 graphql 中,这种嵌套调用更是难以避免。
这时就需要一个 get 函数,使用 get(a, 'b.c.d.e') 简单清晰,并且容错性提高了很多。
1)代码实现
function get(source, path, defaultValue = undefined) {
// a[3].b -> a.3.b -> [a,3,b]
// path 中也可能是数组的路径,全部转化成 . 运算符并组成数组
const paths = path.replace(/\[(\d+)\]/g, ".$1").split(".");
let result = source;
for (const p of paths) {
// 注意 null 与 undefined 取属性会报错,所以使用 Object 包装一下。
result = Object(result)[p];
if (result == undefined) {
return defaultValue;
}
}
return result;
}
// 测试用例
console.log(get({ a: null }, "a.b.c", 3)); // output: 3
console.log(get({ a: undefined }, "a", 3)); // output: 3
console.log(get({ a: null }, "a", 3)); // output: 3
console.log(get({ a: [{ b: 1 }] }, "a[0].b", 3)); // output: 1
2)代码实现
不考虑数组的情况
const _get = (object, keys, val) => {
return keys.split(/\./).reduce(
(o, j)=>( (o || {})[j] ),
object
) || val
}
console.log(get({ a: null }, "a.b.c", 3)); // output: 3
console.log(get({ a: undefined }, "a", 3)); // output: 3
console.log(get({ a: null }, "a", 3)); // output: 3
console.log(get({ a: { b: 1 } }, "a.b", 3)); // output: 1
//匹配'.'或者'[]'的深层属性
const reIsDeepProp = /\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/
//匹配普通属性:数字+字母+下划线
const reIsPlainProp = /^\w*$/
const reEscapeChar = /\\(\\)?/g
const rePropName = RegExp(
//匹配没有点号和括号的字符串,'\\]'为转义']' 例如:'a'
'[^.[\\]]+' + '|' +
// Or match property names within brackets.
'\\[(?:' +
// 匹配一个非字符的表达式,例如 '[index]'
'([^"\'][^[]*)' + '|' +
// 匹配字符串,例如 '["a"]'
'(["\'])((?:(?!\\2)[^\\\\]|\\\\.)*?)\\2' +
')\\]' + '|' +
'(?=(?:\\.|\\[\\])(?:\\.|\\[\\]|$))'
, 'g')
function get(object, path, defaultValue) {
let result;
if (object == null) return result;
if (!Array.isArray(path)) {
//是否为key
const type = typeof path
if (type == 'number' || type == 'boolean' || path == null || toString.call(path) == '[object Symbol]' || reIsPlainProp.test(path) || !reIsDeepProp.test(path)) {
path = [path];
} else {
//字符串转化为数组
const tmp = []
//第一个字符是'.'
if (path.charCodeAt(0) === '.') {
tmp.push('')
}
path.replace(rePropName, (match, expression, quote, subString) => {
let key = match
if (quote) {
key = subString.replace(reEscapeChar, '$1')
} else if (expression) {
key = expression.trim()
}
tmp.push(key)
})
path = tmp;
}
}
//转化为数组后的通用部分
let index = 0
const length = path.length
while (object != null && index < length) {
let value = path[index++];
object = object[value]
}
result = (index && index == length) ? object : undefined
return result === undefined ? defaultValue : result
}
function _get(obj, path, defaultVal) {
let newPath = path;
let result = { ...obj };
if (typeof path === "string") {
if (path.includes(".")) {
// 如果含有数组选项
if (path.includes("[")) {
newPath = newPath.replace(/\[(\w)+\]/g, ".$1");
}
newPath = newPath.split(".");
} else {
newPath = [path];
}
}
for (let i = 0; i < newPath.length; i++) {
let currentKey = newPath[i];
result = result[currentKey];
if (typeof result === "undefined") {
result = defaultVal;
break;
}
}
return result;
};
const data = {
a: {
b: [1, 2, 3]
}
};
console.log(_get(data, "a.b[4]", 20));
data?.b?.[4] ?? 20
在 js 中经常会出现嵌套调用这种情况,如 a.b.c.d.e,但是这么写很容易抛出异常。你需要这么写 a && a.b && a.b.c && a.b.c.d && a.b.c.d.e,但是显得有些啰嗦与冗长了。特别是在 graphql 中,这种嵌套调用更是难以避免。
这时就需要一个 get 函数,使用 get(a, 'b.c.d.e') 简单清晰,并且容错性提高了很多。1)代码实现
function get(source, path, defaultValue = undefined) { // a[3].b -> a.3.b -> [a,3,b] // path 中也可能是数组的路径,全部转化成 . 运算符并组成数组 const paths = path.replace(/\[(\d+)\]/g, ".$1").split("."); let result = source; for (const p of paths) { // 注意 null 与 undefined 取属性会报错,所以使用 Object 包装一下。 result = Object(result)[p]; if (result == undefined) { return defaultValue; } } return result; } // 测试用例 console.log(get({ a: null }, "a.b.c", 3)); // output: 3 console.log(get({ a: undefined }, "a", 3)); // output: 3 console.log(get({ a: null }, "a", 3)); // output: 3 console.log(get({ a: [{ b: 1 }] }, "a[0].b", 3)); // output: 12)代码实现
不考虑数组的情况const _get = (object, keys, val) => { return keys.split(/\./).reduce( (o, j)=>( (o || {})[j] ), object ) || val } console.log(get({ a: null }, "a.b.c", 3)); // output: 3 console.log(get({ a: undefined }, "a", 3)); // output: 3 console.log(get({ a: null }, "a", 3)); // output: 3 console.log(get({ a: { b: 1 } }, "a.b", 3)); // output: 1
path参数还有数组的可能
function _get(obj, path, defaultVal) { let newPath = path; let result = { ...obj }; if (typeof path === "string") { if (path.includes(".")) { // 如果含有数组选项 if (path.includes("[")) { newPath = newPath.replace(/\[(\w)+\]/g, ".$1"); } newPath = newPath.split("."); } else { newPath = [path]; } } for (let i = 0; i < newPath.length; i++) { let currentKey = newPath[i]; result = result[currentKey]; if (typeof result === "undefined") { result = defaultVal; break; } } return result; }; const data = { a: { b: [1, 2, 3] } }; console.log(_get(data, "a.b[4]", 20));newPath.replace(/[(\w)+]/g, ".$1"); 这里是否应该是 newPath.replace(/[(d+)]/g, ".$1");
function get(target, path, defaultValue) {
let keys = path.replace(/\[(\d+)\]/g, ".$1").split(".");
return keys.reduce((t, key) => (t || {})[key], target) ?? defaultValue;
}
这道题还是非常不错的,可以锻炼正则和错误处理
function get(obj, path) {
const keys = path.split('.');
let res = obj;
for(let i = 0; i < keys.length; i++) {
const reg = /^(\w+)\[(\d+)\]$/;
const key = keys[i];
if(reg.test(key)) { // 数组索引
const tmp = key.match(reg);
const k = tmp[1]; // 键
const i = Number(tmp[2]); // 索引
let isError = false;
try {
res = res[k][i];
} catch (error) {
isError = true;
} finally {
if(res == undefined) isError = true;
}
if(isError) break;
}else { // 对象键
res = res[key];
if(res == undefined) return '';;
}
}
return res;
}
get({ 'a': [{ 'b': { 'c': 3 } }] }, 'a[0].b.c');
Most helpful comment
在 js 中经常会出现嵌套调用这种情况,如 a.b.c.d.e,但是这么写很容易抛出异常。你需要这么写 a && a.b && a.b.c && a.b.c.d && a.b.c.d.e,但是显得有些啰嗦与冗长了。特别是在 graphql 中,这种嵌套调用更是难以避免。
这时就需要一个 get 函数,使用 get(a, 'b.c.d.e') 简单清晰,并且容错性提高了很多。
1)代码实现
2)代码实现
不考虑数组的情况