ARTICLE DETAIL

资讯详情

深耕商务建站与企业官网运营的一线实战洞察。

Chrome控制台调试命令速查:Console API与命令行API实战手册

Chrome控制台调试命令速查:Console API与命令行API实战手册 我一直觉得Chrome 控制台Console是前端开发里最值得花时间摸透的工具。很多人对它的印象停留在“看报错、点一下元素、跑两行小脚本”但如果你把它当成一个完整的调试命令行来用会发现里面藏着一整套 API 和快捷命令从选择元素、监听函数、查看事件绑定到性能计时和断点设置几乎能覆盖日常排查的绝大部分需求。这篇文章可以当作一份通用版的 Console 命令速查文档适合前端开发者、爬虫调试人员以及对浏览器工具感兴趣的产品和技术同学。我尽量把每条命令的用法、适用场景和容易踩的坑一起写进去争取让新手看完能直接上手老手也能翻到一些平时没注意到的细节。1. 打开控制台之前先搞清 Console 对象与命令行 API 的关系1.1 三种打开方式和最容易被忽略的两个设置打开控制台这件事听起来没什么好讲的但不同场景下效率差异很大。Windows 和 Linux 下可以用 CtrlShiftJ 直接进入 Console 面板macOS 上是 CommandOptionJ按 F12 打开开发者工具后再切到 Console 也完全没问题。我个人习惯用快捷键直达因为调试时往往要反复切换 Elements、Sources、Network 面板每次都拿鼠标点一遍真的很浪费时间。进入控制台之后有两处设置建议根据场景调整。第一处是日志级别过滤Console 默认会把 Verbose详细级别的日志折叠起来如果你想看console.debug()的输出需要把日志级别切到 Verbose否则大概率会以为自己的打印语句没生效。第二处是“Selected context only”如果你的页面里有 iframe、扩展脚本或者 service worker 在输出日志Consonle 会很吵勾选这个选项可以只看当前执行上下文的内容排查问题的时候干净很多。1.2 Console 对象与命令行 API 的边界这是很多人搞混的第一个点。console.log()、console.table()、console.time()这一系列属于 Console API本质是console对象上的方法写在页面代码里一样能执行。而$_、$0、debug()、monitor()、copy()这些属于命令行 API它们是 DevTools 注入到当前执行环境里的工具函数只存在于控制台交互上下文页面脚本里访问不到。理解这个区别很重要。比如你在控制台里输入$0能拿到 Elements 面板当前选中的元素但你不能把$0写进项目代码里上线之后ReferenceError直接砸脸。反过来console对象上的方法可以被代码覆盖和改写而命令行 API 通常不太容易受页面脚本影响。调试的时候遇到“为什么这个函数在页面上没定义、在控制台里却能执行”这类问题十有八九就是触到了这个边界。另一个容易被忽略的点是控制台本质上是一个 REPL 环境你输入的每一行都会先被 JavaScript 引擎执行然后再把结果格式化输出。所以像document.querySelector这种表达式会显示函数源码而如果直接输入document控制台会把整个文档对象折叠成一个可点击的树形结构。这个“先执行、再格式化”的机制是所有控制台命令行为的基础后面的很多命令都是在这个逻辑上展开的。2. 高频命令速查从 $_ 到 $0-$4再到 $() 与 $$()2.1 $_重复使用上一个计算结果$_是控制台里非常实用的快捷变量它永远保存着上一个表达式的执行结果。比如你在控制台输入1 2回车后得到 3此时输入$_ * 4控制台会直接返回 12。在计算嵌套数据、临时修改某个值时这个变量能省掉很多重复输入的功夫。实际用的时候有几个细节要注意。$_保存的是最近一次表达式的结果如果你中间执行了某个返回undefined的语句$_就被覆盖成undefined了。另外在监听器里用$_还可能踩到坑比如你先执行了一个异步操作然后又去读$_拿到的不一定是预期的中间值。所以我的习惯是$_适合临时交互不适合写多步骤脚本复杂的中间值老老实实赋给自定义变量更稳。2.2 $0-$4直接引用 Elements 面板选中的元素$0到$4是一组历史引用$0返回 Elements 面板里当前选中的元素$1返回上一次选中的元素依此类推总共保留最近五次选择记录。这个功能配合 Elements 面板使用堪称神器先在页面上右键“检查”选中一个按钮然后切到 Console 输入$0.textContent马上就能看到按钮里的文案输入$0.offsetParent能快速判断它被哪个父级定位容器约束。我经常用$0做“先选中、后操作”的调试流程。比如怀疑某个元素的样式不对先在 Elements 里选中它再到 Console 里执行$0.style.outline 2px solid red瞬间画出边框确认边界。如果发现改错了还能从$1、$2里找回之前选过的元素不需要跳回面板重新点选。唯一要注意的是这些引用只在当前 DevTools 会话内有效刷新页面之后引用链会清空$0会变成undefined。2.3 $() 与 $$()选择器函数的便捷替代$()是document.querySelector()的简写$$()是document.querySelectorAll()的简写但$$()返回的是数组不是 NodeList所以可以直接用map()、filter()、forEach()这些数组方法。举个例子我想快速拿到页面里所有外链的域名输入$$(a[href^http]).map(a new URL(a.href).hostname)回车之后立刻能看到去重的域名列表。这在爬虫调试和页面结构分析场景里特别顺手不需要打开一个临时编辑器写循环、再粘贴回控制台执行。这里有一个经典冲突如果页面本身引入了 jQuery$这个标识符会被 jQuery 占用控制台里的$()可能就变成 jQuery 的选择器了。遇到这种情况可以直接用document.querySelector()来兜底或者把函数重命名一下再使用比如const $ document.querySelector.bind(document)不过要注意在某些版本的 DevTools 里内置$()和页面全局$是会互相覆盖的具体行为跟 DevTools 版本和页面加载时机有关。我建议在团队项目里统一约定调试时优先用$$()和document.querySelector避免被页面环境干扰。2.4 高频命令速查小抄为了方便日常翻阅我汇总了一张速查表覆盖本文后面会展开讲的一部分核心命令命令作用示例$_引用上一个表达式的结果$_$0引用 Elements 面板当前选中的元素$0.style.color #f60$()querySelector的简写$(.nav).innerHTML$$()querySelectorAll的数组版简写$$(img).length$x()用 XPath 选择元素$x(//div[classitem])copy()把内容复制到剪贴板copy(document.title)clear()清空控制台输出clear()这里面$x()在 XPath 场景下很有用尤其是嵌套层级深的页面结构用 XPath 比 CSS 选择器更灵活。但日常调试用 CSS 选择器居多XPath 当成补充工具记住就可以了。3. console.* 家族从 log 到 table 的完整用法3.1 log、info、warn、error、debug 的分级输出console.log()是最基础的输出方法但实际项目里我很少单独用log。更合理的做法是让不同类型的消息走不同的级别普通信息用console.info()警告用console.warn()错误用console.error()调试细节用console.debug()。这样做的直接好处是控制台自带滤镜可以把日志按级别过滤线上环境里warn和error会高亮显示一眼就能抓到重点。你可能会问console.info()和console.log()的输出样式看起来几乎一样有必要区分吗有。DevTools 的日志级别不仅仅影响显示样式还会影响性能监控、错误上报工具的过滤逻辑。很多团队的错误监控脚本会自动捕获console.error()的输出如果你所有日志都一律用log后续想按级别筛数据就无从下手了。一个小坑console.debug()输出在默认情况下是不显示的必须把日志级别切到 Verbose 才能看到。如果你在调试时“明明写了debug却找不到输出”先别查代码检查一下控制台的日志级别下拉菜单很大概率就是被过滤掉了。3.2 格式化输出与 %c 样式魔法console.log()支持类似 C 语言的格式化占位符常用的有%s字符串、%d整数、%o对象可展开的 DOM 对象、%O对象普通 JS 对象。比如console.log(%s %d %s, 订单编号, 1024, 已支付)这个写法的优势在于打印结构复杂的字符串时不需要做一堆变量拼接读起来也干净。不过我想重点说的是%c占位符它后面的字符串会被当成 CSS 样式应用到整条日志上console.log(%c订单创建失败, color: #fff; background: #e74c3c; padding: 4px 8px; border-radius: 4px; font-size: 14px;)这条日志打印出来是白字红底的徽章样式在大量日志输出里极其醒目。我在团队的调试规范里推荐过这种做法错误统一用红底白字%c前缀警告用黄底黑字普通日志不加样式。配合日志级别过滤基本上不用盯着屏幕找半天。3.3 console.table 与 console.dir从对象到表格console.table()是我最常用的输出工具之一。当你有一组结构相似的对象时用table打印出来是一个可排序的表格视觉上比一堆折起来的大对象清晰太多。比如console.table([ { name: 张三, age: 24, city: 上海 }, { name: 李四, age: 30, city: 北京 } ])如果对象里有大量不需要展示的字段还可以传第二个参数指定列console.table(list, [name, age])console.dir()则是把对象以树形结构展开显示。最常见的用法是打印 DOM 元素console.log(document.body)显示的是 HTML 标签结构而console.dir(document.body)显示的是这个元素的属性和方法列表包括clientWidth、scrollHeight、dataset这些你需要的信息。想快速了解一个 DOM 节点有什么属性时dir远比log好用。3.4 计时、分组、计数、断言与堆栈这几个方法比较“小而美”但每个都有明确的使用场景。console.time()和console.timeEnd()是一对计时器括号里的标签是同一个字符串时才会正确结束并打印耗时。比如console.time(数组排序耗时) arr.sort() console.timeEnd(数组排序耗时)要注意time和timeEnd的标签必须完全一致拼写错一点就会得到一条警告信息而无法正常输出。标签重复使用时timeEnd会结束上一次未结束的同名计时器所以嵌套场景下尽量用语义化标签。console.count()用来统计同一段代码的执行次数适合确认事件回调有没有触发、触发了多少次。配合countReset()可以清零重新计数。console.group()和console.groupEnd()可以把一组日志折叠起来。业务逻辑比较复杂时把不同模块的输出归组控制台会清爽很多。groupCollapsed()则是默认折叠的版本想看细节再点展开大型项目里实测很好用。console.assert()会在条件为假时打印一条错误日志相当于“断言”。我调试接口数据时经常用它console.assert(user.age 18, 用户年龄必须大于18当前值, user.age)条件满足时什么都不打印条件不满足时直接红字报错写临时校验逻辑很方便。最后是console.trace()它会在控制台打印当前的调用堆栈。排查“这个函数到底是被谁调进来的”这类问题时在可疑函数里加一行console.trace()就能看到完整的调用链路不用去 Sources 面板手动打断点。4. 命令行 API 里容易被人忽略的老牌工具debug、monitor、getEventListeners4.1 debug 与 monitor给第三方函数“插桩”debug()命令可以在指定函数上自动设置一个断点函数执行时就会停在入口处。用法是传入函数本身或函数名比如debug(fetch)这样页面任何代码调用fetch时都会在fetch入口断住你可以在 Sources 面板里一步步看参数、走逻辑。不想继续就执行undebug(fetch)移除断点。这个功能最妙的地方在于不管目标函数是你自己写的还是第三方库里的只要能在全局作用域访问到就能直接插桩。我曾经用debug(HTMLElement.prototype.click)来排查一个“明明调用了 click 却没有触发点击事件”的问题断住之后直接看到调用方传进来的上下文问题根源立刻就清楚了。monitor()和debug()类似但它是打印函数的每次调用参数和返回值而不是中断执行monitor(document.querySelector)之后每执行一次document.querySelector(...)控制台就会输出一条记录包含参数和返回结果。这种监听方式对确认“某个函数有没有被调用、被调用了几次、每次都传了什么”非常有帮助尤其适合定位“某个接口被无限重复请求”这类性能问题。用unmonitor()取消监听。4.2 getEventListeners查看元素绑了哪些事件getEventListeners()返回指定元素上注册的所有事件监听器对象。比如选中一个按钮后getEventListeners($0)输出结果会按事件类型分组click字段下的数组里就是该事件注册的所有处理函数。每个监听器对象里还能看到useCapture、passive等属性排查事件是冒泡触发还是捕获触发时非常直观。这个命令的典型使用场景是“页面有个按钮点了没反应”。很多新人会直接去代码里搜addEventListener但如果你用的是第三方组件库事件绑定可能发生在库的内部搜半天搜不到。直接在控制台getEventListeners($0).click看一下数组里到底有几个处理函数每个函数的源码是什么一下子就锁定了范围。如果监听器返回空对象说明这个元素根本没有绑定对应事件问题很可能出在事件委托或者别的元素上排查方向立刻就能调整。4.3 keys、values、copy、inspect、clear辅助操作一把梭keys()和values()分别返回对象的所有键名和键值数组。它们看起来和Object.keys()重复但妙处在于可以用在任意控制台表达式上配合$_使用非常流畅const obj { name: test, level: 2 } keys(obj) values(obj)copy()会把内容复制到系统剪贴板这个命令我要单独夸一下。平时想把接口返回的大 JSON 复制到编辑器里手动选中复制不仅费劲还可能漏掉缩进。直接copy(JSON.stringify(data, null, 2))然后去编辑器里粘贴格式完整、绝不丢内容。配合fetch在控制台里先获取接口数据再用copy导出来整个链路不到五秒钟。inspect()会跳转到 Elements 面板并选中传入的 DOM 元素。比如从一堆匹配div中筛选出第一个想看看它在页面上的实际位置inspect($(div))clear()清空控制台输出。需要注意它和console.clear()的区别console.clear()是 Console API可以在代码里调用clear()是命令行 API只能在控制台交互里执行。两者效果一样但页面代码里没有clear()这个函数用错了会报错。5. 几个真实的调试场景从找事件到查性能瓶颈5.1 场景按钮点击没反应先查监听器再决定是否打断点有次接到一个反馈说页面上某个“提交订单”按钮点击没有任何反应。这种问题大多数时候不是按钮没绑定事件而是事件绑定了但执行出错或者被某个外层元素拦截了。我的排查链路是先在 Elements 面板选中按钮切到 Console 执行getEventListeners($0)如果click数组是空的说明按钮自身没有绑定事件可能事件在父级委托或者按钮被一层遮罩盖住、压根点不到。如果click数组里有多个处理函数逐个看函数源码找到可疑的那一个然后debug($0.__clickHandler)这样我就能在点击按钮时停在对应函数入口按步执行看哪一行抛异常。整套流程下来从发现问题到锁定根因通常不会超过三分钟。5.2 场景用一个函数找出页面里的重复事件绑定有次排查一个性能问题发现某个列表项的点击事件执行次数成倍增长怀疑是事件被重复绑定了。直接在控制台里写一个小函数遍历关键节点$$(.list-item).map(item ({ text: item.textContent.slice(0, 10), listeners: getEventListeners(item).click?.length || 0 }))$$()拿到所有列表项getEventListeners()统计每个项绑定的 click 监听器数量。输出的表格一眼就能看出哪些项绑定了多个监听器顺着那段代码去找重复绑定的地方就非常快了。这种组合拳是控制台命令的真正价值单个命令只能帮你做一件事组合起来能帮你快速做结构化排查。5.3 场景监控异步函数的调用频率和参数另一个常见需求是“某个接口好像被频繁调用每次参数都不一样”。直接打断点太烦因为接口每秒可能调用几十次。这时候monitor()就派上用场了monitor(fetch)然后什么都不用做正常操作页面控制台会持续输出每次fetch的调用参数和返回值。打开控制台观察输出频率马上就能判断接口是不是在循环里被调用。如果是某个特定函数导致的可以换成monitor(axios.post)或者直接监听自定义业务函数。这种方法不侵入代码、不打断运行流程非常适合线上环境的即时排查。5.4 场景计算一段渲染逻辑的实际耗时性能优化时console.time()是最直接的计时手段。但要注意在控制台里使用它和在代码里使用有一点区别控制台本身是异步执行的可能无法捕捉到浏览器渲染管线之后的完整时间。如果需要更精确的数据建议配合performance.now()const start performance.now() // 在控制台手动触发一段逻辑 console.log(耗时, performance.now() - start, ms)这种原始的时间戳方式更适合控制台交互场景因为它不依赖console.time()内部的事件循环机制结果更可靠。特别是在对比“两种写法哪种更快”的时候直接在控制台跑几轮、算平均值效果比凭感觉判断好得多。6. 团队项目里关于 console 的几个实践问题6.1 把 console.log 从临时工具变成团队规范很多项目里的console.log是临时调试随手写的上线时要么忘记删要么全删掉下一次排查问题时又到处乱加。我见过比较成熟的团队会定一套简单的打印规范业务入口和出口用console.info异常用console.error性能耗时统一用performance的标记然后配合console.group把同一模块的日志归组。这样开发环境和线上排查都舒服很多。另外我强烈建议在团队调试规范里引入%c样式前缀。比如所有组件库内部调试日志统一用蓝色前缀[Lib]业务日志用绿色前缀[Biz]错误日志用红底白字。这样混合输出的时候扫一眼颜色就能判断日志属于哪个体系不用每条都展开看。成本极低收益非常直接。6.2 生产环境还能不能留着 console 输出这是团队项目里绕不开的问题。我的观点是不是所有console都要删而是要看输出的内容是否涉及敏感数据。像接口请求参数、用户信息、内部错误对象这些在任何环境下都不应该打印。对于无害的结构化日志如果开发环境已经够用线上可以保留但要控制级别和数量避免日志量大到影响性能。如果公司项目里希望构建产物完全去掉console可以借助构建工具的配置。比如在打包压缩阶段把 console 相关语句移除配置思路是按环境区分只有生产环境才开启移除。需要注意移除console不仅仅是删几行代码的问题如果代码里存在依赖console返回值或者把console传给其他函数的写法暴力移除可能导致运行时报错。所以在做全局清理前先检索一下项目里的console用法确认都是独立调用再去配置清理规则。6.3 用控制台给前端函数做“单测”和渲染验证最后分享一个日常工作里的小玩法在控制台里临时粘贴一个函数定义然后立刻调用验证。比如我急着确认某个时间格式化函数在不同边界下是否正常function formatTime(t) { return t 10 ? 0 t : String(t) } [0, 5, 9, 10, 23].map(formatTime)控制台直接输出结果比反复刷新页面、点击操作要快得多。如果验证的是组件渲染逻辑可以先在控制台改一个元素的关键样式或文本再决定是不是要落代码。这种“用小函数快速验证逻辑、再迁移到项目里”的做法能明显减少提交代码后发现低级 bug 的频率。控制台本质上是浏览器自带的一个 JavaScript 运行时用来做临时实验、边界值测试、数据筛选体验完全不输给本地写脚本跑一遍。
返回列表
PREV
查看更多资讯
NEXT
返回资讯列表