
本文详解 Eloquent JavaScript 中 talksAbout 递归函数的执行逻辑,揭示其在未匹配目标字符串时仍返回 true 的根本原因——误将 标签内 console.log("book") 的字符串字面量当作 DOM 文本节点匹配。
本文详解 eloquent javascript 中 `talksabout` 递归函数的执行逻辑,揭示其在未匹配目标字符串时仍返回 `true` 的根本原因——误将 `<script>` 标签内 `console.log("book")` 的字符串字面量当作 dom 文本节点匹配。</script>
talksAbout 是一个典型的 DOM 递归文本搜索函数,其设计目标是:遍历 DOM 树中所有文本节点(Node.TEXT_NODE),检查其 nodeValue 是否包含指定字符串。它通过区分节点类型(元素节点 vs 文本节点)实现深度优先遍历:
- 若为 元素节点(Node.ELEMENT_NODE):递归检查其每个子节点;
- 若为 文本节点(Node.TEXT_NODE):直接调用 nodeValue.indexOf(string) > -1 判断是否包含目标字符串。
然而,问题在于:HTML 中 <script> 标签的内容(包括 console.log("book"))本身会被浏览器解析为 DOM 中的一个文本节点</script>。即使你从
内容中删除了 "book",只要 <script> 标签内存在该字符串(如 console.log("book") 或 talksAbout(document.body, "book")),函数就会在遍历到该脚本文本节点时命中匹配,返回 true。<p>以下是修正后的安全版本,明确排除 <script> 和 <style> 等非渲染文本节点:<pre class="brush:php;toolbar:false;">function talksAbout(node, string) { // 跳过 script 和 style 元素及其后代(避免匹配代码中的字符串) if (node.nodeType === Node.ELEMENT_NODE) { if (node.tagName === 'SCRIPT' || node.tagName === 'STYLE') { return false; } for (let child of node.childNodes) { if (talksAbout(child, string)) { return true; } } return false; } else if (node.nodeType === Node.TEXT_NODE) { // 只检查可见/语义文本(可选:trim 后判断非空) const text = node.nodeValue.trim(); return text && text.indexOf(string) > -1; } return false; }<p>✅ <strong>关键注意事项:<ul><li>浏览器将 <script> 标签的原始内容(含 JS 字符串字面量)作为 TEXT_NODE 插入 DOM,这是导致“假阳性”的根源;<li>document.body 并不包含 <script> 节点(它属于 <body> 的兄弟节点),但若脚本置于 <body> 内部(如示例所示),则 document.body 的子节点会包含该 <script> 元素,进而递归进入其文本内容;<li>实际应用中,应结合 node.parentElement 或提前过滤特定标签,避免代码逻辑干扰语义搜索;<li>可进一步增强健壮性:忽略注释节点(Node.COMMENT_NODE)、跳过空白文本节点、或使用 textContent 替代遍历(但需注意 textContent 会合并所有文本,丢失结构信息)。<p>总结:理解 DOM 节点树的真实构成(包括 <script> 的文本内容)是正确编写 DOM 遍历逻辑的前提。调试时善用 console.warn 输出节点类型与值,能快速定位此类隐蔽匹配源。 </script>










