别再只会 console.log 了!这 15 个 Console 调试技巧,让你的 Debug 效率翻倍
"console.log('到这了')" "console.log('到这了 2')" "console.log('到这了 3')" "console.log('为什么不执行???')" —— 每个程序员的日常
前言:你真的会用 console 吗?
让我猜猜你的调试方式:
console.log(data)
console.log("data:", data)
console.log("========== 分割线 ==========")
console.log(data)
console.log("到这了")
console.log("到这了2")
console.log("为什么不进来???")
如果你中枪了,别担心,你不是一个人。
但是,console 对象其实有超过 20 个方法,而大多数人只会用 console.log。
今天,我要带你解锁 console 的全部潜力。
学完这篇文章,你的调试效率至少提升 50%。
第一章:基础进阶——让 console.log 更好用
1.1 技巧一:用对象包裹变量名
这是最简单但最有用的技巧。
❌ 不好的写法:
const userName = "张三"
const userAge = 25
const userEmail = "zhangsan@example.com"
console.log(userName)
console.log(userAge)
console.log(userEmail)
// 输出:
// 张三
// 25
// zhangsan@example.com
// 问题:你根本不知道哪个是哪个!
✅ 好的写法:
const userName = "张三"
const userAge = 25
const userEmail = "zhangsan@example.com"
console.log({ userName, userAge, userEmail })
// 输出:
// { userName: '张三', userAge: 25, userEmail: 'zhangsan@example.com' }
// 清清楚楚,一目了然!
这个技巧利用了 ES6 的对象简写语法,变量名自动成为属性名。
1.2 技巧二:给输出加上 emoji 标签
当你的控制台输出很多的时候,找到你要的那条信息就像大海捞针。
解决方案:用 emoji 做视觉标记!
// 🔍 调试信息
console.log("🔍 正在查找用户:", userId)
// ✅ 成功信息
console.log("✅ 用户登录成功:", user.name)
// ❌ 错误信息
console.log("❌ 登录失败:", error.message)
// ⚠️ 警告信息
console.log("⚠️ 用户权限不足")
// 🚀 性能相关
console.log("🚀 API 响应时间:", responseTime, "ms")
// 📦 数据相关
console.log("📦 接收到的数据:", data)
// 🔄 状态变化
console.log("🔄 状态更新:", oldState, "->", newState)
在一堆黑白文字中,emoji 会非常显眼,让你一眼就能找到关键信息。
1.3 技巧三:使用模板字符串格式化
const user = { name: "张三", age: 25 }
const action = "登录"
const timestamp = new Date().toLocaleTimeString()
// ❌ 拼接字符串(丑陋且难读)
console.log("用户 " + user.name + " 在 " + timestamp + " 执行了 " + action)
// ✅ 模板字符串(清晰优雅)
console.log(`[${timestamp}] 用户 ${user.name} 执行了 ${action}`)
// 输出:[14:30:25] 用户 张三 执行了 登录
1.4 技巧四:console.log 的 CSS 样式
是的,你可以给 console.log 加 CSS 样式!
// 基础用法
console.log("%c这是红色文字", "color: red")
console.log("%c这是大号蓝色文字", "color: blue; font-size: 20px")
// 高级用法:多种样式组合
console.log(
"%c 成功 %c 操作已完成",
"background: #4CAF50; color: white; padding: 2px 6px; border-radius: 3px",
"color: #4CAF50"
)
// 实用示例:创建一个漂亮的日志函数
const prettyLog = {
success: (msg) =>
console.log(
`%c ✓ SUCCESS %c ${msg}`,
"background: #4CAF50; color: white; padding: 2px 6px; border-radius: 3px; font-weight: bold",
"color: #4CAF50"
),
error: (msg) =>
console.log(
`%c ✗ ERROR %c ${msg}`,
"background: #f44336; color: white; padding: 2px 6px; border-radius: 3px; font-weight: bold",
"color: #f44336"
),
warning: (msg) =>
console.log(
`%c ⚠ WARNING %c ${msg}`,
"background: #ff9800; color: white; padding: 2px 6px; border-radius: 3px; font-weight: bold",
"color: #ff9800"
),
info: (msg) =>
console.log(
`%c ℹ INFO %c ${msg}`,
"background: #2196F3; color: white; padding: 2px 6px; border-radius: 3px; font-weight: bold",
"color: #2196F3"
),
}
// 使用
prettyLog.success("用户登录成功")
prettyLog.error("网络请求失败")
prettyLog.warning("API 即将废弃")
prettyLog.info("当前版本: 2.0.0")
第二章:数据展示——让复杂数据一目了然
2.1 技巧五:console.table() —— 表格展示数据
这可能是最被低估的 console 方法。
当你有一个对象数组时,console.log 的输出是这样的:
const users = [
{ id: 1, name: "张三", age: 25, city: "北京" },
{ id: 2, name: "李四", age: 30, city: "上海" },
{ id: 3, name: "王五", age: 28, city: "广州" },
]
console.log(users)
// 输出一堆难以阅读的嵌套对象...
但如果你用 console.table():
console.table(users)
// 输出一个漂亮的表格:
// ┌─────────┬────┬────────┬─────┬────────┐
// │ (index) │ id │ name │ age │ city │
// ├─────────┼────┼────────┼─────┼────────┤
// │ 0 │ 1 │ '张三' │ 25 │ '北京' │
// │ 1 │ 2 │ '李四' │ 30 │ '上海' │
// │ 2 │ 3 │ '王五' │ 28 │ '广州' │
// └─────────┴────┴────────┴─────┴────────┘
还可以只显示特定列:
console.table(users, ["name", "city"])
// 只显示 name 和 city 列
// ┌─────────┬────────┬────────┐
// │ (index) │ name │ city │
// ├─────────┼────────┼────────┤
// │ 0 │ '张三' │ '北京' │
// │ 1 │ '李四' │ '上海' │
// │ 2 │ '王五' │ '广州' │
// └─────────┴────────┴────────┘
适用场景:
- API 返回的数据列表
- 数据库查询结果
- 配置项对比
- 任何数组或对象的可视化
2.2 技巧六:console.dir() —— 查看对象的完整结构
console.log 打印 DOM 元素时,显示的是 HTML 结构。
console.dir 打印 DOM 元素时,显示的是对象属性。
const element = document.querySelector("#app")
console.log(element)
// 输出:<div id="app">...</div>(HTML 结构)
console.dir(element)
// 输出:div#app 的所有属性和方法(对象结构)
// 包括:className, id, innerHTML, style, onclick...
当你需要查看一个对象有哪些属性和方法时,用 console.dir。
2.3 技巧七:console.dirxml() —— 查看 XML/HTML 结构
const element = document.querySelector("#app")
console.dirxml(element)
// 以 XML/HTML 树形结构展示元素及其子元素
第三章:分组与层级——让输出更有组织
3.1 技巧八:console.group() —— 分组输出
当你有一堆相关的日志时,可以把它们分组:
console.group("用户信息")
console.log("姓名: 张三")
console.log("年龄: 25")
console.log("城市: 北京")
console.groupEnd()
console.group("订单信息")
console.log("订单号: 12345")
console.log("金额: ¥99.00")
console.log("状态: 已支付")
console.groupEnd()
// 输出:
// ▼ 用户信息
// 姓名: 张三
// 年龄: 25
// 城市: 北京
// ▼ 订单信息
// 订单号: 12345
// 金额: ¥99.00
// 状态: 已支付
3.2 技巧九:console.groupCollapsed() —— 默认折叠的分组
console.groupCollapsed("详细调试信息(点击展开)")
console.log("这是一些详细的调试信息...")
console.log("通常不需要看,但需要时可以展开")
console.log("比如:完整的请求参数、响应数据等")
console.groupEnd()
// 输出:
// ▶ 详细调试信息(点击展开) ← 默认是折叠的
适用场景:
- 详细的调试信息(平时不看,出问题时展开)
- 大量的数据输出
- 嵌套的对象结构
3.3 技巧十:嵌套分组
console.group("🛒 购物车")
console.group("商品列表")
console.log("iPhone 15 Pro - ¥8999")
console.log("AirPods Pro - ¥1899")
console.groupEnd()
console.group("优惠信息")
console.log("满减: -¥500")
console.log("优惠券: -¥100")
console.groupEnd()
console.log("💰 总计: ¥10298")
console.groupEnd()
// 输出:
// ▼ 🛒 购物车
// ▼ 商品列表
// iPhone 15 Pro - ¥8999
// AirPods Pro - ¥1899
// ▼ 优惠信息
// 满减: -¥500
// 优惠券: -¥100
// 💰 总计: ¥10298
第四章:性能分析——找出代码瓶颈
4.1 技巧十一:console.time() —— 测量代码执行时间
这是性能调试的神器!
console.time("数据处理")
// 模拟一些耗时操作
const data = []
for (let i = 0; i < 100000; i++) {
data.push({ id: i, value: Math.random() })
}
console.timeEnd("数据处理")
// 输出:数据处理: 45.123ms
可以同时计时多个操作:
console.time("总耗时")
console.time("获取数据")
const response = await fetch("/api/users")
const users = await response.json()
console.timeEnd("获取数据")
// 输出:获取数据: 234.56ms
console.time("处理数据")
const processedUsers = users.map((user) => ({
...user,
fullName: `${user.firstName} ${user.lastName}`,
}))
console.timeEnd("处理数据")
// 输出:处理数据: 12.34ms
console.time("渲染")
renderUsers(processedUsers)
console.timeEnd("渲染")
// 输出:渲染: 89.01ms
console.timeEnd("总耗时")
// 输出:总耗时: 335.91ms
4.2 技巧十二:console.timeLog() —— 中间计时
console.time("多步骤操作")
await step1()
console.timeLog("多步骤操作", "步骤1完成")
// 输出:多步骤操作: 100.00ms 步骤1完成
await step2()
console.timeLog("多步骤操作", "步骤2完成")
// 输出:多步骤操作: 250.00ms 步骤2完成
await step3()
console.timeEnd("多步骤操作")
// 输出:多步骤操作: 400.00ms
4.3 技巧十三:console.count() —— 计数器
想知道某段代码执行了多少次?
function handleClick() {
console.count("按钮点击")
// 其他逻辑...
}
// 点击3次后:
// 按钮点击: 1
// 按钮点击: 2
// 按钮点击: 3
// 重置计数器
console.countReset("按钮点击")
实用场景:
function render(component) {
console.count(`${component} 渲染次数`)
// 渲染逻辑...
}
// 检查组件是否有不必要的重复渲染
render("Header") // Header 渲染次数: 1
render("Header") // Header 渲染次数: 2 ← 为什么渲染了两次?
render("Header") // Header 渲染次数: 3 ← 可能有性能问题!
第五章:错误追踪——快速定位问题
5.1 技巧十四:console.trace() —— 打印调用栈
当你想知道"这个函数是从哪里被调用的"时:
function functionA() {
functionB()
}
function functionB() {
functionC()
}
function functionC() {
console.trace("调用栈追踪")
}
functionA()
// 输出:
// 调用栈追踪
// at functionC (script.js:10)
// at functionB (script.js:6)
// at functionA (script.js:2)
// at script.js:13
这在调试复杂的回调链或事件处理时特别有用。
5.2 技巧十五:console.assert() —— 条件断言
只有当条件为 false 时才输出:
const user = { name: "张三", age: 25 }
// 如果条件为 true,什么都不输出
console.assert(user.name, "用户名不能为空")
// 如果条件为 false,输出错误信息
console.assert(user.age >= 18, "用户必须成年")
console.assert(user.email, "用户邮箱不能为空")
// 输出:Assertion failed: 用户邮箱不能为空
适用场景:
function processOrder(order) {
console.assert(order, "订单不能为空")
console.assert(order.items?.length > 0, "订单必须包含商品")
console.assert(order.totalAmount > 0, "订单金额必须大于0")
// 如果所有断言都通过,继续处理...
}
第六章:实战技巧——日常开发中的最佳实践
6.1 创建一个增强版 Logger
// logger.js - 一个实用的日志工具
const isDev = process.env.NODE_ENV === "development"
const logger = {
// 基础日志(只在开发环境输出)
log: (...args) => {
if (isDev) console.log("📝", ...args)
},
// 信息日志
info: (...args) => {
if (isDev)
console.log(
"%c ℹ️ INFO ",
"background: #2196F3; color: white; border-radius: 3px",
...args
)
},
// 成功日志
success: (...args) => {
if (isDev)
console.log(
"%c ✅ SUCCESS ",
"background: #4CAF50; color: white; border-radius: 3px",
...args
)
},
// 警告日志
warn: (...args) => {
console.warn("⚠️", ...args) // 警告在生产环境也输出
},
// 错误日志
error: (...args) => {
console.error("❌", ...args) // 错误在生产环境也输出
},
// 分组日志
group: (label, fn) => {
if (!isDev) return fn()
console.group(`📦 ${label}`)
const result = fn()
console.groupEnd()
return result
},
// 计时日志
time: async (label, fn) => {
if (!isDev) return fn()
console.time(`⏱️ ${label}`)
const result = await fn()
console.timeEnd(`⏱️ ${label}`)
return result
},
// 表格日志
table: (data, columns) => {
if (isDev) console.table(data, columns)
},
}
export default logger
// 使用示例
import logger from "./logger"
logger.info("应用启动")
logger.success("用户登录成功", { userId: 123 })
logger.warn("API 即将废弃")
logger.error("网络请求失败", error)
logger.group("用户数据", () => {
logger.log("姓名:", user.name)
logger.log("年龄:", user.age)
})
await logger.time("数据加载", async () => {
return await fetchData()
})
6.2 调试 API 请求
// 创建一个 API 调试拦截器
const debugFetch = async (url, options = {}) => {
const requestId = Math.random().toString(36).substr(2, 9)
console.groupCollapsed(
`🌐 API 请求 [${requestId}] ${options.method || "GET"} ${url}`
)
console.log("📤 请求参数:", {
url,
method: options.method || "GET",
headers: options.headers,
body: options.body ? JSON.parse(options.body) : undefined,
})
console.time(`⏱️ 响应时间 [${requestId}]`)
try {
const response = await fetch(url, options)
const data = await response.clone().json()
console.timeEnd(`⏱️ 响应时间 [${requestId}]`)
console.log("📥 响应状态:", response.status, response.statusText)
console.log("📦 响应数据:", data)
if (!response.ok) {
console.error("❌ 请求失败")
} else {
console.log("✅ 请求成功")
}
console.groupEnd()
return response
} catch (error) {
console.timeEnd(`⏱️ 响应时间 [${requestId}]`)
console.error("❌ 请求异常:", error.message)
console.groupEnd()
throw error
}
}
// 使用
const response = await debugFetch("/api/users", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ name: "张三" }),
})
6.3 调试 React 组件渲染
// 在 React 组件中使用
function UserProfile({ userId }) {
console.count(`UserProfile 渲染 (userId: ${userId})`)
useEffect(() => {
console.log("🔄 UserProfile useEffect 触发", { userId })
return () => {
console.log("🧹 UserProfile 清理", { userId })
}
}, [userId])
console.group("📊 UserProfile 渲染详情")
console.log("Props:", { userId })
console.log("渲染时间:", new Date().toLocaleTimeString())
console.groupEnd()
return <div>...</div>
}
6.4 调试状态变化
// 创建一个状态变化追踪器
function createStateTracker(initialState, name = "State") {
let state = initialState
return {
get: () => state,
set: (newState) => {
console.group(`🔄 ${name} 变化`)
console.log("旧值:", state)
console.log("新值:", newState)
console.trace("调用来源")
console.groupEnd()
state = newState
return state
},
}
}
// 使用
const userState = createStateTracker({ name: "", loggedIn: false }, "UserState")
userState.set({ name: "张三", loggedIn: true })
// 输出:
// ▼ 🔄 UserState 变化
// 旧值: { name: '', loggedIn: false }
// 新值: { name: '张三', loggedIn: true }
// 调用来源: (调用栈)
第七章:Console 方法速查表
┌─────────────────────────────────────────────────────────────────────────┐
│ Console 方法速查表 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ 📝 基础输出 │
│ ├─ console.log() 普通日志输出 │
│ ├─ console.info() 信息日志(某些浏览器有特殊图标) │
│ ├─ console.warn() 警告日志(黄色背景) │
│ └─ console.error() 错误日志(红色背景) │
│ │
│ 📊 数据展示 │
│ ├─ console.table() 以表格形式展示数组/对象 │
│ ├─ console.dir() 以对象形式展示(查看属性和方法) │
│ └─ console.dirxml() 以 XML/HTML 形式展示 DOM 元素 │
│ │
│ 📁 分组管理 │
│ ├─ console.group() 创建展开的分组 │
│ ├─ console.groupCollapsed() 创建折叠的分组 │
│ └─ console.groupEnd() 结束当前分组 │
│ │
│ ⏱️ 性能分析 │
│ ├─ console.time() 开始计时 │
│ ├─ console.timeLog() 输出中间时间 │
│ ├─ console.timeEnd() 结束计时并输出 │
│ └─ console.count() 计数器(统计调用次数) │
│ │
│ 🔍 调试追踪 │
│ ├─ console.trace() 打印调用栈 │
│ ├─ console.assert() 条件断言(条件为 false 时输出) │
│ └─ console.clear() 清空控制台 │
│ │
│ 🎨 样式输出 │
│ └─ console.log('%c文字', 'CSS样式') 带样式的输出 │
│ │
└─────────────────────────────────────────────────────────────────────────┘
第八章:常见问题与注意事项
8.1 生产环境要移除 console
问题: console 语句会影响性能,也可能泄露敏感信息。
解决方案:
// 方案1:使用环境变量控制
if (process.env.NODE_ENV === 'development') {
console.log('调试信息')
}
// 方案2:使用构建工具移除
// webpack 配置(使用 terser-webpack-plugin)
optimization: {
minimizer: [
new TerserPlugin({
terserOptions: {
compress: {
drop_console: true, // 移除所有 console
},
},
}),
],
}
// 方案3:使用 babel 插件
// babel.config.js
plugins: [
['transform-remove-console', { exclude: ['error', 'warn'] }]
]
// 方案4:使用 ESLint 规则
// .eslintrc.js
rules: {
'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'warn'
}
8.2 console.log 的异步陷阱
问题: console.log 打印对象时,显示的是引用,不是快照。
const obj = { count: 0 }
console.log(obj) // 可能显示 { count: 1 } 而不是 { count: 0 }
obj.count = 1
解决方案:
// 方案1:使用 JSON.stringify
console.log(JSON.stringify(obj))
// 方案2:使用展开运算符创建浅拷贝
console.log({ ...obj })
// 方案3:使用 JSON.parse + JSON.stringify 创建深拷贝
console.log(JSON.parse(JSON.stringify(obj)))
// 方案4:使用 structuredClone(现代浏览器)
console.log(structuredClone(obj))
8.3 大对象的性能问题
问题: 打印大对象会导致浏览器卡顿。
// ❌ 不好:打印整个大数组
console.log(hugeArray) // 可能有 10000 个元素
// ✅ 好:只打印需要的部分
console.log("数组长度:", hugeArray.length)
console.log("前10个元素:", hugeArray.slice(0, 10))
console.log("第一个元素:", hugeArray[0])
结语:从 console.log 到 console 大师
今天我们学习了 15 个 console 调试技巧:
- 用对象包裹变量名 - 自动显示变量名
- 用 emoji 标签 - 视觉标记,快速定位
- 模板字符串格式化 - 清晰优雅的输出
- CSS 样式 - 让日志更醒目
- console.table() - 表格展示数据
- console.dir() - 查看对象结构
- console.dirxml() - 查看 XML/HTML 结构
- console.group() - 分组输出
- console.groupCollapsed() - 折叠分组
- 嵌套分组 - 层级结构
- console.time() - 测量执行时间
- console.timeLog() - 中间计时
- console.count() - 计数器
- console.trace() - 调用栈追踪
- console.assert() - 条件断言
记住:好的调试习惯能让你的开发效率翻倍。
下次当你想写 console.log('到这了') 的时候,想想有没有更好的方式。
附录:Console 快捷键
浏览器控制台快捷键:
打开控制台:
├─ Windows/Linux: F12 或 Ctrl + Shift + J
└─ Mac: Cmd + Option + J
控制台内操作:
├─ 清空控制台: Ctrl + L (Windows) / Cmd + K (Mac)
├─ 多行输入: Shift + Enter
├─ 执行代码: Enter
├─ 上一条命令: ↑
├─ 下一条命令: ↓
└─ 自动补全: Tab
如果你觉得这篇文章有用,请分享给你那个还在写 console.log('111') 的同事。
也许他需要知道:console 的世界,远比你想象的精彩。 🎨
最后,送给所有程序员一句话:
"调试的艺术,在于问对问题,而不是打更多的 log。" 🔍
愿你的 bug 越来越少,调试越来越快。