Skip to content

traverse

深度枚举对象和任何嵌套对象

1284 bytes
since v12.2.0

使用方法

递归访问对象的每个属性(或数组的每个元素)及其嵌套对象或数组。要遍历非数组可迭代对象(例如 MapSet)和类实例,请参见遍历其他对象部分。

遍历以深度优先方式执行。这意味着最深的对象将在根对象的最后一个属性之前被访问。

import * as _ from "radashi";
const root = { a: { b: 2 }, c: [1, 2] };
_.traverse(root, (value, key, parent, context) => {
const depth = context.parents.length;
console.log(" ".repeat(depth * 2), key, "=>", value);
});
// 记录以下内容:
// a => { b: 2 }
// b => 2
// c => [1, 2]
// 0 => 1
// 1 => 2

提示: 查看高级部分,了解还有什么可能。

类型

TraverseVisitor

TraverseVisitor 类型表示作为其第二个参数传递给 traverse 的函数。如果您需要在 traverse 调用之外声明访问者,可以通过声明具有此类型签名的函数来实现。

import { TraverseVisitor } from "radashi";
const visitor: TraverseVisitor = (value, key, parent, context) => {
// ...
};

TraverseContext

每次访问都包含一个类型为 TraverseContext 的上下文对象,它包含以下属性:

  • key:当前正在访问的键。
  • parent:当前值的父对象。
  • parents:包含当前值的对象数组(从父到子)。
  • path:描述从根到当前值的键路径的数组。
  • skip:用于跳过对象遍历的函数。如果没有提供对象,则跳过当前值。有关更多详细信息,请参见跳过对象
  • skipped:已跳过的对象集合。
  • value:当前正在访问的值。

TraverseOptions

您可以使用对象作为其第三个参数为 traverse 设置这些选项。

  • ownKeys:返回对象自有可枚举属性名的函数。
  • rootNeedsVisit:指示是否应访问根对象的布尔值。

有关更多详细信息,请参见选项部分。

选项

遍历所有属性

默认情况下,非可枚举属性和符号属性会被跳过。您可以传入自定义的 ownKeys 实现来控制访问哪些对象属性。

此示例展示了如何使用 Reflect.ownKeys 来包含非可枚举属性和符号属性。请注意,使用 Reflect.ownKeys 时,符号属性总是最后遍历。

import * as _ from "radashi";
const symbol = Symbol("b");
const root = { [symbol]: 1 };
Object.defineProperty(root, "a", { value: 2, enumerable: false });
_.traverse(
root,
(value, key) => {
console.log(key, "=>", value);
},
{ ownKeys: Reflect.ownKeys }
);
// 记录以下内容:
// a => 2
// Symbol(b) => 1

访问根对象

默认情况下,您的 visitor 回调永远不会接收传递给 traverse 的对象。要覆盖此行为,请将 rootNeedsVisit 选项设置为 true。

当访问根对象时,key 将为 null

import * as _ from "radashi";
const root = { a: 1 };
_.traverse(
root,
(value, key) => {
console.log(key, "=>", value);
},
{ rootNeedsVisit: true }
);
// 记录以下内容:
// null => { a: 1 }
// a => 1

高级

遍历其他对象

如果遍历普通对象和数组还不够,请尝试从另一个 traverse 回调中调用 traverse,如下所示。这利用了根对象总是被遍历的事实。

import * as _ from "radashi";
// 注意我们使用命名访问者函数,这样它可以引用自己。
_.traverse(root, function visitor(value, key, parent, context, options) {
if (value instanceof MyClass) {
return _.traverse(value, visitor, options, context);
}
// TODO: 根据需要处理其他值。
});

如果您没有设置任何选项,options 参数可以为 null:

return _.traverse(root, visitor, null, context);

跳过对象

使用 TraverseContext::skip 方法,您可以防止对象被遍历。通过调用不带参数的 skip(),当前值不会被遍历。

import * as _ from "radashi";
const root = {
a: { b: 1 },
c: { d: 2 },
};
_.traverse(root, (value, key, parent, context) => {
console.log(key, "=>", value);
// 跳过'a'对象的遍历。
if (key === "a") {
context.skip();
}
});
// 记录以下内容:
// a => { b: 1 }
// c => { d: 2 }
// d => 2

您可以将任何对象传递给 skip() 来跳过该对象的遍历。

import * as _ from "radashi";
const root = {
a: {
b: {
c: 1,
},
},
};
_.traverse(root, (value, key, parent, context) => {
console.log(key, "=>", value);
// 访问当前对象的属性,但跳过其中嵌套的任何对象。
Object.values(value).forEach((nestedValue) => {
if (_.isObject(nestedValue)) {
context.skip(nestedValue);
}
});
});
// 记录以下内容:
// a => { b: { c: 1 } }
// b => { c: 1 }

提前退出

如果您的 visitor 回调返回 false,traverse 将提前退出并且也返回 false。这在您找到所需内容时很有用,因此您不需要遍历其余对象。

let found = null;
_.traverse(root, (value) => {
if (isWhatImLookingFor(value)) {
found = value;
return false;
}
});

离开回调

如果您的 visitor 回调返回一个函数,它将在 traverse 访问完当前对象中的每个可访问属性/元素后被调用。这被称为”离开回调”。

您的离开回调可以返回 false 来提前退出遍历。

_.traverse({ arr: ["a", "b"] }, (value, key) => {
if (isArray(value)) {
console.log("start of array");
return () => {
console.log("end of array");
return false;
};
} else {
console.log(key, "=>", value);
}
});
// 记录以下内容:
// start of array
// 0 => 'a'
// 1 => 'b'
// end of array