traverse
深度枚举对象和任何嵌套对象
使用方法
递归访问对象的每个属性(或数组的每个元素)及其嵌套对象或数组。要遍历非数组可迭代对象(例如 Map、Set)和类实例,请参见遍历其他对象部分。
遍历以深度优先方式执行。这意味着最深的对象将在根对象的最后一个属性之前被访问。
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