Постановка задачи
Мне нужен доступ к глубоко вложенный объект. Я мог бы использовать стороннюю утилиту, такую как Лодаш но настоял на написании Ванильный JS решение с рекурсивная стратегия.
Код
Может ли это решение быть улучшен с лучше а также более эффективным подход? Есть ли край случай что было упущено и могло нарушить код?
const obj = {
foo: {
bar: {
baz: "value"
}
}
};
const getPathValue = (object, path) => {
if (
// ======= object checks ======= //
object === null ||
object === undefined ||
typeof object !== "object" ||
// ======= path checks ======= //
path === null ||
path === undefined ||
path.length === 0
) {
return undefined;
}
// key -> current key
// rest -> rest of the keys in path
const [key, ...rest] = path;
// value of current key
const value = object[key];
// if rest of the keys are exhausted &
// a value is found, return the value
if (rest.length === 0 && value) {
return value;
}
// as rest of key are not exhaused yet
// in the path, keep traversing deeper
return getPathValue(value, rest);
};
// ======= test cases with object edge cases ======= //
console.log(getPathValue()); // undefined
console.log(getPathValue(null)); // undefined
console.log(getPathValue(undefined)); // undefined
// ======= test cases with path edge cases ======= //
console.log(getPathValue(obj, [])); // undefined
console.log(getPathValue(obj, null)); // undefined
console.log(getPathValue(obj, [1, 2, 3])); // undefined
console.log(getPathValue(obj, ["non exisiting key"])); // undefined
console.log(getPathValue(obj, ["foo", "non exisiting key"])); // undefined
console.log(getPathValue(obj, ["foo", "bar", "baz", "non exisiting key"])); // undefined
console.log(getPathValue(obj, ["foo", "non exisiting key", "bar", "baz"])); // undefined
// ======= normal test case ======= //
console.log(getPathValue(obj, ["foo", "bar", "baz"])); // value 1 ответ
Рекурсия
Есть много причин избегать рекурсии в JavaScript.
Рекурсия — неэффективный способ реализации простых циклов.
Обычно вы используете рекурсию, чтобы следовать древовидным путям, где путь является динамическим или неизвестным, в этом случае путь заранее определен, и нет необходимости складывать предыдущее состояние цикла при повторении.
Сильный аргумент НИКОГДА не использовать рекурсию заключается в том, что JS имеет ограниченный стек вызовов, но тот же аргумент также означает, что НИКОГДА не вызывайте функцию (вы можете быть в конце стека вызовов), что является нелепым требованием.
Пока JS не поддерживает оптимизацию хвостового вызова, вы всегда должны использовать цикл вместо рекурсии.
Проверка аргументов
Аргумент проверки слишком сложен и может быть упрощен до typeof object === "object" && object !== null && Array.isArray(path) см. переписать.
Примечание «null плохо» Хороший JS никогда не должен устанавливать значение null и, таким образом, избежать нелепой необходимости проверять, является ли объект null, к сожалению, мы должны иметь дело с разработчиками DOM и CSS, которые упускают из виду то, что null значит в JS.
Циклический сейф
Как правило, при повторении путей следует опасаться циклических путей, однако это вызывает беспокойство только в том случае, если поиск является открытым (может выполняться бесконечно) и, следовательно, не является проблемой в этом случае.
Непоследовательный возврат
Если передан пустой массив, ваш пример возвращает undefined что не имеет смысла по сравнению с неполным путем, который вернет Object или же value. Пустой путь эквивалентен неполному пути.
Например, объекты
const o1 = {A: { B: {C: 0}}};
const o2 = {B: {C: 0}};
Не удается найти корневой объект
getPathValue(o1, ["A"]);// returns {B: {C: 0}};
getPathValue(o1, []); // returns undefined; One would expect {A: { B: {C: 0}}}
getPathValue(o2, []); // returns undefined; One would expect {B: {C: 0}}
getPathValue(getPathValue(o1, ["A"]), []); // returns undefined; One would expect {B: {C: 0}}
Переписать
Переписать
Возврат
undefinedесли путь не может быть найден.Возвращает объект в конце пути. например
getPathValue(obj, [])возвращаетсяobjнетundefinedТакже будет следовать индексному пути в массивы.
Использует ?? (Нулевой оператор объединения) чтобы упростить поиск по пути и, как таковой, вернет
undefinedскорее, чемnull. Установка «nullплохо» правило для JS.Если второй аргумент не является массивом, результатом будет
undefined.
function valueAtPath(obj, path) {
var i = 0;
if (typeof obj === "object" && obj !== null && Array.isArray(path)) {
while (i < path.length && obj !== undefined) { obj = obj[path[i++]] ?? undefined }
return obj;
}
}

Имел
o1был доступен динамически с помощьюo1[]или жеo1[''], это вызвало бы ошибка или вернулся неопределенный значение соответственно. Итак, уважая такое поведение, я ожидаюgetPathValue(o1, [])возвращаться неопределенный так же. Что вы думаете об этом?— Винай Шарма
@VinaySharma Массив с пустой строкой не является пустым массивом. Что вы вернете в этих случаях, зависит от вас, поскольку нет правильного или неправильного. Я считаю, что … Вы не предоставляете способ вернуть корневой объект и, следовательно, должны были бы добавить оператор к вызову, если бы вам потребовался корневой объект. Функция решает путь, если вы не делаете никаких шагов по пути, вы фактически остаетесь в начале (корне) пути. Если вы попытаетесь наступить на неопределенный путь, например, с именем
""пустая строка, значит, вы переместились в неопределенное место.— Слепой67