我想使用带有自定义匹配器回调的生成器遍历dom树,当产生值时,返回一个在其间有遍历节点的数组。说我有这个结构。
root
/ \
P1 P2
| |
T1 T2
我想做iter.next(isP)
或iter.next(isText)
更新匹配器,直到下一个节点匹配。
type Matcher = (node: INode) => boolean
export function* nextNode(node: INode, matcher: Matcher = () => true, store: []): Generator<INode, any, Matcher> {
let reset: Matcher = matcher
store = store.concat(node)
if (reset(node)) {
reset = yield store
store = []
}
if (node.children) {
for (let childNode of node.children) {
yield *nextNode(childNode, matcher, store)
}
}
return
}
我的代码问题是弹出函数调用堆栈时reset
丢失了。例如,如果我在T1中,而以前的堆栈是isText
,那么现在如果我这样做了,iter.next(isP)
将行不通。我该怎么办?
const iter = nextNode(root, isT)
iter.next() <-- this is T1
iter.next(isP) <-- this is T2 should be P2
答案 0 :(得分:1)
您可以使用生成器的返回值来穿越遍历状态。当yield*
从root的第一个孩子返回时,它将需要给您提供在产生root和p1之后在next
调用中可用的存储和匹配器。
…
if (node.children) {
for (let childNode of node.children) {
[reset, store] = yield* nextNode(childNode, reset, store)
// ^^^^^^^^^^^^^^^^^ ^^^^^
}
}
return [reset, store]
// ^^^^^^^^^^^^^^
完整的示例:
function* nextNode(node, matcher = () => true, store = []) {
store.push(node.name)
if (matcher(node)) {
matcher = yield store
store = []
}
if (node.children) {
for (let childNode of node.children) {
[matcher, store] = yield* nextNode(childNode, matcher, store)
}
}
return [matcher, store]
}
const node = (name, children) => ({name, children})
const is = c => n => n.name[0] == c
const iter = nextNode(node("root", [
node("p1", [node("t1")]),
node("p2", [node("t2")])
]), is("t"))
console.log("until next t:", iter.next())
console.log("until next p:", iter.next(is("p")))
console.log("until next p:", iter.next(is("p")))
答案 1 :(得分:0)
嗯,似乎一个简单的解决方案是只拥有一个全局匹配器。
type Matcher = (node: INode) => boolean
type TYield = INode[]
function* nextNode(
node: INode,
matcher: Matcher = () => true,
): Generator<TYield, TYield, Matcher> {
let store: INode[] = []
let reset = matcher
function* _nextNode(node: INode): Generator<TYield, any, Matcher> {
store.push(node)
if (reset(node)) {
reset = yield store
if (!reset) reset = matcher
store = []
}
if (node.children) {
for (const childNode of node.children) {
yield* _nextNode(childNode)
}
}
return
}
yield* _nextNode(node)
return store
}