
千锋教育的这套《JavaScript全套视频教程》是一个以 10 天为周期的 JavaScript 入门课程讲师是 kerwin。和很多动辄几百集的零散教程相比这套课程最大的特点是“短周期、强主线、重练习”——它把 JavaScript 基础语法、Web API、面向对象、异步编程和实战案例压缩成了 10 天的学习计划适合刚开始接触前端、想快速建立 JavaScript 知识骨架的人。这篇文章会从学习者的角度拆解这套课程先看它的核心能力覆盖范围再结合前端开发的实际工作流把 10 天的学习路线展开成可执行的知识点清单同时给出环境搭建、代码验证、常见报错排查以及从教程到面试的衔接建议。不管你是准备自学前端还是想系统补一轮 JavaScript 基础都可以按这套流程走一遍。千锋教育 JavaScript 全套视频教程10 天入门前端 JavaScript 的学习路线与实战验证1. 核心能力速览在开始看课程内容之前先明确这套教程的几个关键属性。以下信息基于课程标题和公开资料整理课程细节需要以实际观看为准。能力项说明课程主题JavaScript 基础入门覆盖从语法到 Web API 再到简单实战讲师kerwin千锋教育周期设计10 天按天划分学习任务适合短周期集中学习面向人群JavaScript 零基础、前端入门者、计算机专业学生核心内容变量与数据类型、运算符与流程控制、函数、数组与对象、DOM 与 BOM、事件、异步编程、ES6 常用特性配套形式视频讲解 代码演示需要自己动手敲代码验证硬件要求一台能运行浏览器和代码编辑器的电脑无特殊 GPU 要求操作系统Windows / macOS / Linux 均可推荐工具Chrome 浏览器、VS Code、Node.js用于运行非 DOM 代码是否支持 API不涉及服务端接口但教程中的异步章节通常包含 fetch / Ajax 基础是否支持批量任务不适用于批量任务课程定位是语法学习和前端开发入门适合场景前端入门、校招面试基础复习、非科班转行前端第一课从这张表可以看出这套课程解决的不是“某一个框架怎么用”的问题而是 JavaScript 这门语言本身怎么学、怎么练、怎么在浏览器里看到效果的问题。它的重点不在工具链而在语言基础和浏览器运行机制。2. 适用场景与学习边界2.1 适合谁学这套课程最适合四类人。第一类是完全没有写过代码的纯零基础学员。课程从变量声明、数据类型这些最基础的概念讲起节奏比很多大学公开课慢但比纯文档教程更有现场感。跟着视频一行一行敲代码是零基础入门最稳的方式。第二类是学过 HTML 和 CSS、但一直不会写交互的前端初学者。很多人在学完 HTML 和 CSS 之后会陷入一个瓶颈页面能写出来但点击没反应、数据不会加载、页面跳转做不了。这套课程的 DOM 和事件部分正好解决这个问题。第三类是非科班转行前端的人。转行者最需要的是短时间内建立知识框架然后通过大量练习把框架填满。10 天的课程设计适合拿出一段完整时间集中突击。第四类是准备校招或社招面试、但 JavaScript 基础不牢的求职者。面试题里大量考察闭包、原型链、this 指向、事件循环、数组方法、深浅拷贝、Promise这些内容在课程的基础语法和高级特性部分都有涉及。2.2 不适合谁学如果你已经掌握了 JavaScript 核心语法并且能熟练使用 Vue、React 开发项目这套课程对你来说基础浓度偏高可以直接跳过去学习工程化、组件库、性能优化等进阶内容。如果你想学的是某个具体框架比如 Vue 3 或 React 18这套课程也不是你的第一选择。正确路径是先学原生 JavaScript再进框架。这是一个学习顺序问题不是课程质量问题。如果你希望“10 天学会 JS 后立刻能找到工作”这套课程承担不了这个预期。10 天能解决的问题是“学会 JavaScript 的基础语法和浏览器交互能力”但距离企业级开发还有很长一段路需要继续学习 ES6 进阶、网络请求、模块化、打包工具、框架、组件库、工程化部署等内容。2.3 使用边界与合规提醒这套课程是教学视频不涉及换脸、声音克隆、图像生成等高风险场景。在学习过程中需要注意的只有两点。第一课程中的代码示例如果涉及第三方图片、字体、接口数据仅用于个人学习测试不要直接复制到商业项目中使用。尤其是图片素材和接口地址要考虑版权和授权问题。第二在练习接口请求时建议使用测试环境或公开的免费测试接口如 HTTPBin、JSONPlaceholder不要对没有授权的网站发起高频请求避免对他人服务造成压力。3. 环境准备与前置条件相比 AI 模型部署那套流程JavaScript 入门的环境准备要轻量得多。不需要装 CUDA不需要考虑显存不需要下载模型文件。你需要准备的就是浏览器、编辑器、Node.js 三个东西。3.1 浏览器推荐使用 Chrome 或 Edge原因只有一个开发者工具最好用。JavaScript 的很多调试操作比如查看控制台报错、打断点、查看网络请求、分析页面元素都需要依赖 DevTools。安装完成后按 F12 键打开开发者工具先熟悉以下三个面板Console控制台查看 console.log 输出和报错信息。Elements元素查看和临时修改页面 DOM 结构。Network网络查看页面加载了哪些资源请求状态码是什么。这三个面板会贯穿整个学习周期。每写一段 JavaScript打开控制台看一眼比任何学习笔记都直观。3.2 代码编辑器推荐 VS Code免费、跨平台、插件生态好。刚入门不用装太多插件建议先装以下两个Chinese (Simplified) Language Pack中文界面降低上手成本。Live Server在本地启动一个静态服务器预览 HTML 页面时会用到。VS Code 在你输出代码的过程中还会提供高亮和基础语法提示这对刚接触 JavaScript 的人来说能少犯很多低级拼写错误。3.3 Node.jsNode.js 不是必须安装的但建议装。原因有两个。第一JavaScript 有两种运行环境浏览器和 Node.js。课程中的 DOM、BOM 相关代码只能在浏览器里运行但纯语法练习题比如数组排序、字符串处理、函数定义可以放到 Node.js 里跑启动速度更快不用每次都开浏览器。第二后续学习 npm、Vue、React、工程化工具时Node.js 是基础依赖。早装早省事。安装完成后打开终端执行node -v如果能输出版本号说明安装成功。3.4 通用检查清单检查项要求验证方式操作系统Windows 10 / macOS / Linux能正常打开终端即可浏览器Chrome 或 Edge 最新版打开后按 F12 有响应编辑器VS Code 最新版能正常创建 .html / .js 文件Node.js建议 18 LTS 以上终端执行node -v网络能访问 npm 仓库即可国内可配置镜像执行npm config get registry4. 10 天学习路线拆解从语法到页面交互这套课程的核心是“10 天学会 JavaScript”所以学习路线必须按天拆解。下面的拆解是基于 JavaScript 标准知识体系做的建议具体节奏需要结合课程实际内容调整。4.1 第 1-2 天语言基础与运行原理前两天解决三个问题JavaScript 是什么、代码写在哪里、怎么运行。第一个任务是理解 JavaScript 的三种书写方式行内式写在 HTML 标签的事件属性里不推荐。内嵌式写在script标签里适合学习阶段用。外部式写在独立的 .js 文件里通过script srcxxx.js/script引入这是实际开发中最常用的方式。第二个任务是掌握基本语法变量声明var、let、const 的区别。数据类型number、string、boolean、null、undefined、object、symbol、bigint。运算符算术、赋值、比较、逻辑、三元。流程控制if / else、switch、for、while、break、continue。模板字符串使用反引号拼接字符串替代传统加号拼接。这个阶段最容易犯的错误是“变量提升”和“类型转换”混淆。比如console.log(a); // undefined var a 10;这个输出结果是 undefined而不是报错因为 var 声明的变量会被提升到作用域顶部但赋值不会提升。换成 let 就会直接报错console.log(a); // ReferenceError: Cannot access a before initialization let a 10;建议把 let 和 const 作为默认选择var 只需要了解它的历史背景。4.2 第 3-4 天函数与作用域函数是 JavaScript 的核心也是面试中考察最重的部分。需要掌握的内容函数声明和函数表达式的区别。箭头函数与普通函数的 this 指向差异。参数传递默认参数、剩余参数、arguments。返回值与纯函数概念。作用域全局作用域、函数作用域、块级作用域。闭包理解函数嵌套和变量引用的关系。高阶函数函数作为参数或返回值。一个典型的闭包示例function createCounter() { let count 0; return function () { count 1; return count; }; } const counter createCounter(); console.log(counter()); // 1 console.log(counter()); // 2闭包的核心理解是内部函数即使在外层函数执行完毕后仍然可以访问外层函数的局部变量。4.3 第 5-6 天数组、对象与内置方法数组和对象是前端开发中使用频率最高的数据结构。这一阶段不只是学会定义它们而是熟练使用内置方法。数组方法建议重点掌握push / pop / shift / unshift增删元素。map / filter / reduce遍历与聚合。forEach / some / every普通遍历与条件判断。find / findIndex / indexOf查找元素。includes / join / split转换操作。sort排序注意默认排序是按字符串 Unicode 码点排的。slice / splice截取和删除。一个常用的数组工具链写法const numbers [1, 2, 3, 4, 5, 6]; // 过滤出偶数并翻倍 const result numbers .filter((num) num % 2 0) .map((num) num * 2); console.log(result); // [4, 8, 12]对象部分重点掌握对象的定义和访问方式点语法和方括号语法。对象的遍历for...in、Object.keys、Object.values、Object.entries。解构赋值。深浅拷贝的区别。JSON.stringify 和 JSON.parse 的序列化与反序列化。这里特别提醒一点JSON.stringify在做前端性能优化时经常用到但它在遇到undefined、函数、Symbol 值时会自动忽略这些属性遇到循环引用会直接报错。理解它的限制后面做数据存储和数据上报时会少踩很多坑。4.4 第 7-8 天DOM 操作与浏览器事件这是从“会写 JS”到“会写网页交互”的转折点。DOM 操作的核心是获取元素getElementById、querySelector、querySelectorAll。修改内容和属性innerText、innerHTML、setAttribute。修改样式style 属性、classList 添加和移除。创建和删除元素createElement、appendChild、removeChild。事件监听addEventListener。事件对象target、currentTarget、preventDefault、stopPropagation。事件冒泡与事件委托。一个常见的事件委托示例ul idlist li项目 1/li li项目 2/li li项目 3/li /ulconst list document.querySelector(#list); // 事件委托只监听父元素处理所有子元素点击 list.addEventListener(click, (event) { const target event.target; if (target.tagName LI) { console.log(点击了, target.textContent); } });事件委托的价值在于即使后续往列表里动态添加新的 li不需要重新绑定事件。4.5 第 9 天异步编程与接口请求异步编程是 JavaScript 初学者最容易卡住的地方也是从课堂练习走向真实项目必须跨过的门槛。需要掌握的内容同步与异步的区别。回调函数的基本写法。Promise 的状态与链式调用。async / await 写法。事件循环的基本概念。fetch 发起 HTTP 请求。一个简单的 fetch 请求示例async function getUserInfo(userId) { try { const response await fetch(https://jsonplaceholder.typicode.com/users/${userId}); if (!response.ok) { throw new Error(请求失败${response.status}); } const data await response.json(); console.log(data.name); } catch (error) { console.error(获取用户信息失败, error); } } getUserInfo(1);这里有三点需要特别注意一是要处理非 2xx 状态码二是要处理网络异常三是 await 必须在 async 函数里使用。4.6 第 10 天综合实战与查漏补缺最后一天建议做一个综合小项目把所有知识点串起来。推荐做下面几个方向之一待办事项列表Todo List锻炼 DOM 操作、事件处理、数组方法、本地存储。简易计算器锻炼函数封装、字符串处理、事件绑定。用户信息卡片渲染锻炼 fetch 请求、对象遍历、DOM 渲染。随机颜色生成器锻炼 Math 方法、模板字符串、事件监听。完成项目后可以按照下面这个清单自查能独立说出 var / let / const 的区别吗能手动实现一个数组的 map 方法吗能解释闭包的作用和应用场景吗能说清楚 Promise 的三种状态和链式调用吗能通过 fetch 请求一个接口并把数据渲染到页面上吗能定位控制台里最常见的 TypeError、ReferenceError 报错吗如果这些问题都能回答出来10 天入门的目标基本就达成了。5. 核心知识点实操验证与代码示例这一节把课程中最重要的几个知识点单独拿出来给出一套“输入、执行、预期输出、判断标准”的验证方法。学习过程中每看完一个章节就按下面的方式在本地验证一次。5.1 变量与数据类型验证创建variable-test.js文件let userName Kerwin; const age 18; const isFrontEndDeveloper true; console.log(typeof userName); // string console.log(typeof age); // number console.log(typeof isFrontEndDeveloper); // boolean console.log(typeof null); // object历史遗留问题 console.log(typeof undefined); // undefined let defaultName userName || 游客; let realName userName ?? 游客; console.log(defaultName); // Kerwin预期结果在终端执行node variable-test.js后依次输出 string、number、boolean、object、undefined、Kerwin、Kerwin。判断标准能把||和??的区别说清楚。||会在左侧为 0、、null、undefined 等 falsy 值时取右侧值而??只在左侧为 null 或 undefined 时取右侧值。5.2 数组方法实操验证创建array-test.js文件const students [ { name: 张三, score: 82 }, { name: 李四, score: 95 }, { name: 王五, score: 60 }, { name: 赵六, score: 74 } ]; // 按成绩从高到低排序 const sorted [...students].sort((a, b) b.score - a.score); console.log(sorted.map(s ${s.name}: ${s.score}).join()); // 找出成绩大于等于 80 分的学生 const excellent students.filter(s s.score 80); console.log(excellent.map(s s.name)); // [张三, 李四] // 计算全班平均分 const total students.reduce((sum, s) sum s.score, 0); const avg total / students.length; console.log(avg.toFixed(2)); // 77.75预期结果终端输出排序后的成绩列表、优秀学生姓名列表、平均分 77.75。判断标准能解释 sort 方法为什么不用a.score - b.score加上展开运算符[...students]会改变原数组的问题以及 reduce 的第二个参数初始值为什么必须写。5.3 闭包与立即执行函数验证创建closure-test.js文件function createTodoList() { const tasks []; return { add(task) { tasks.push(task); console.log(已添加${task}); return tasks.length; }, remove(task) { const index tasks.indexOf(task); if (index ! -1) { tasks.splice(index, 1); } console.log(已删除${task}); return tasks.length; }, list() { console.log(tasks); } }; } const todo createTodoList(); todo.add(学习 JavaScript); todo.add(练习 DOM 操作); todo.list(); todo.remove(学习 JavaScript); todo.list();预期结果操作日志依次输出任务数组中第一个任务被删除。判断标准能说出 tasks 数组为什么不会被外部直接修改因为闭包把变量隔离在了 createTodoList 函数内部。5.4 DOM 交互验证创建dom-test.html文件!DOCTYPE html html langzh-CN head meta charsetUTF-8 titleDOM 测试/title /head body div idapp h1JavaScript DOM 测试/h1 button idbtn点击我/button p idinfo还没有点击/p /div script srcdom-test.js/script /body /html创建dom-test.js文件const button document.querySelector(#btn); const info document.querySelector(#info); button.addEventListener(click, () { const now new Date(); const timeString ${now.getHours()}:${String(now.getMinutes()).padStart(2, 0)}; info.textContent 最后一次点击时间${timeString}; info.style.color #d33; });操作步骤用 VS Code 打开dom-test.html右键选择 “Open with Live Server”然后在浏览器里点击按钮。预期结果每次点击按钮页面上的提示文字会更新成当前时间颜色变为红色。判断标准点击没有报错、页面内容发生了变化说明 DOM 绑定成功。如果点击后没有反应打开控制台查看报错信息。5.5 异步接口请求验证创建async-test.html文件!DOCTYPE html html langzh-CN head meta charsetUTF-8 titleAsync 测试/title /head body ul idpost-list/ul script srcasync-test.js/script /body /html创建async-test.js文件const postList document.querySelector(#post-list); async function fetchPosts() { try { const response await fetch(https://jsonplaceholder.typicode.com/posts?_limit5); const posts await response.json(); postList.innerHTML posts .map((post) listrong${post.title}/strong/li) .join(); } catch (error) { postList.innerHTML li加载失败${error.message}/li; } } fetchPosts();预期结果页面加载后列表里展示 5 条文章标题。判断标准Network 面板看到请求状态码 200页面正常渲染数据说明 fetch 请求、Promise 异步处理、DOM 渲染整条链路已经跑通。这一阶段如果遇到跨域问题可以用课程配套的本地接口或者 JSONPlaceholder 的公开接口尽量不要对没有授权的接口发起测试请求。6. 浏览器调试与资源占用观察6.1 Console 面板输出与报错定位写 JavaScript 的初期90% 的时间都在和 Console 面板打交道。要养成三个习惯第一每次写完代码先看 Console 有没有红色报错。红色报错一定要读完整ReferenceError: xxx is not defined通常说明变量名写错了或没有声明TypeError: Cannot read properties of null通常说明你获取的 DOM 元素不存在。第二多使用console.table()查看对象和数组结构比console.log()更直观。第三遇到循环或重复执行的代码在console.log中加入可识别的前缀避免输出刷屏。6.2 Network 面板请求状态与耗时学异步请求时Network 面板是标配。打开面板刷新页面观察所有网络请求Status Code200 是成功404 是资源不存在500 是服务端错误。Name请求的文件名字能帮助你确认请求到底打到哪个接口。Time请求耗时结合课程里的网络概念理解访问延迟。Typexhr / fetch 表示异步请求document 表示页面文档js / css 表示静态资源。如果请求没有任何返回先看控制台报错再判断是跨域、接口地址错误还是网络问题。6.3 Source 面板断点调试断点调试是进阶能力但从入门第一天就应该接触。在 Sources 面板打开 js 文件点击行号设置断点刷新页面后代码会在断点处暂停。此时可以逐行执行查看变量的实时值。这个操作比console.log打点更高效面试中也经常考察调试能力。6.4 性能与资源观察JavaScript 本身没有显存占用的问题但要注意两个性能指标第一页面加载时 JavaScript 的下载和解析时间。文件越大、脚本越多页面阻塞越严重。入门阶段练习时尽量把 JavaScript 放到页面底部或用defer属性加载。第二不必要的 DOM 操作次数。每次修改innerHTML或appendChild都会触发浏览器重绘和重排。批量渲染时不建议在循环里操作 DOM可以先拼接好字符串最后一次写入。// 不推荐循环里每次修改 DOM for (let i 0; i 1000; i) { document.body.innerHTML p${i}/p; } // 推荐先拼接最后一次性写入 let html ; for (let i 0; i 1000; i) { html p${i}/p; } document.body.innerHTML html;这一部分属于性能优化入门知识。掌握这个思路后再去看 React、Vue 的虚拟 DOM 原理会有“原来如此”的感觉。7. 常见报错与排查清单学习 JavaScript 的过程中报错是常态。下面整理一份最常见的报错排查表。报错现象可能原因排查方式解决方案ReferenceError: xxx is not defined变量未声明或拼写错误检查变量名是否一致在正确的作用域声明变量Uncaught SyntaxError: Unexpected token语法错误常见于少写括号、引号、逗号看 IDE 红色波浪线提示按提示修正语法TypeError: Cannot read properties of null获取的 DOM 元素不存在确认元素的 id / class 是否正确把 script 放到 body 末尾或等待 DOM 加载完成Uncaught ReferenceError: Cannot access x before initialization在 let / const 声明前访问了变量调整变量声明顺序使用let/const时先声明后使用JSON.stringify 循环引用报错对象内部互相引用检查数据是否存在父子引用使用自定义序列化方案fetch 请求无响应或 CORS 报错跨域或接口地址错误打开 Network 面板看请求状态使用支持 CORS 的测试接口或配置本地代理点击按钮没反应事件绑定失败或 JS 文件未加载在控制台执行代码片段测试检查选择器和脚本引入顺序console.log 输出多次事件绑定了多次检查代码是否在循环中绑定事件将事件绑定移出循环或先解绑再绑定8. 从教程到实战的进阶路径8.1 学完 JavaScript 基础之后学什么10 天学会 JavaScript 语法只是第一步。接下来需要按一条清晰的主线继续深入HTML 与 CSS 进阶Flex 布局、Grid 布局、CSS 动画、响应式设计。ES6 进阶模板变量、解构赋值、展开运算符、Set / Map、模块化 import / export。网络与异步进阶Axios、请求拦截、防抖节流、WebSocket。框架学习Vue 3 或 React 18二选一即可重点是组件化思维和数据流。构建工具Vite / Webpack理解项目为什么需要打包。工程化基础Git、npm 脚本、代码规范、Docker 部署静态页面。组件库Element Plus、Ant Design 等进入项目开发后的常用依赖。在学习框架的过程中会遇到诸如“为什么 Vue 提示elmessage未定义”、“为什么组件库按需导入不生效”等问题。这些问题本质上是在考验 JavaScript 模块化能力和对工具链的理解底子扎实了排查思路会清晰很多。8.2 前端面试八股文怎么背网上流行的“前端八股文”是 JavaScript 基础面试题的集合常见的包括说说 var、let、const 的区别。什么是闭包闭包有哪些应用场景说一下事件循环Event Loop。Promise 和 async/await 的区别是什么数组的 map、filter、reduce 有什么区别深拷贝和浅拷贝的区别如何实现深拷贝介绍一下防抖和节流。this 的指向有哪些情况什么是原型链怎么理解纯函数八股文不是不能背而是不能在完全不懂原理的情况下去背。建议按照“先看课程理解概念 - 自己写代码验证 - 遇到面试题用自己的话复述 - 对照标准答案补漏”这个顺序准备。这样面试时被追问底层原理不至于哑火。8.3 用 Docker 部署前端静态页面的思路教程学完后很多人想把自己的练习项目发布到线上。这里给出一条完整的前端部署思路。写一个简单的DockerfileFROM nginx:alpine COPY . /usr/share/nginx/html EXPOSE 80在项目根目录构建镜像并启动容器docker build -t my-frontend-app . docker run -d -p 8080:80 my-frontend-app浏览器访问http://localhost:8080就能看到自己的页面运行在 Nginx 容器里。这一条路径把前端开发和工程化部署串起来了也是从纯前端走向全栈开发前最值得掌握的一项技能。9. 最佳实践与学习建议9.1 每天固定输出代码看视频不等于学会。只看不敲10 天后什么都留不下。建议每天至少独立写 30 行代码把课程中的示例遮住自己重现一遍。9.2 建立错误记录文档遇到报错不要只复制到百度或 AI 工具里找答案。先把报错信息、出现场景、解决方式记录到一个 Markdown 文件里。积累到 30 条以上你对 JavaScript 的掌握会明显上台阶。9.3 一个项目反复迭代10 天课程结束后不要着急开始下一个新课程。把第 10 天的综合项目拿出来用第二天新学的知识点重新改造一遍。比如先写一个 Todo List入门时用原生 DOM 实现学过组件化后把它改成 Vue 组件学过自动化部署后用 GitHub Actions 把它发布成静态网站。同一个项目的不断重构比堆砌新项目效率高得多。9.4 善用 AI 工具但要独立纠错现在写代码可以借助 AI 工具辅助但有一个原则AI 给出的代码必须能自己看懂、能自己跑通、能自己解释。如果直接把 AI 生成的代码复制到编辑器里运行报错了也不知道怎么改那学习效果基本为零。建议先独立思考再让 AI 做代码审查或优化建议。9.5 遵守开源和版权协议教程里的示例代码和素材仅供个人学习。如果要包装成自己的项目、放进简历或用于商业用途需要检查素材来源和许可协议。尤其是接口数据、图片、字体和第三方库合规使用是最基本的要求。10. 总结与下一步千锋教育这套 JavaScript 视频教程的价值不在于“10 天”这个数字而在于它提供了一条从零开始学习 JavaScript 的完整路径先语法再函数再数据结构然后是 DOM 交互和异步编程最后落到综合项目。对零基础学习者来说这是一套知识密度合适、节奏紧凑的入门课程。最先要验证的内容是第 1 天的环境搭建和第 8 天的 DOM 交互。前者决定学习效率后者决定能不能从“会写代码”过渡到“会写网页”。最容易踩的坑是看视频时间太长敲代码时间太短。解决办法是调整时间配比视频和代码的比例至少保持在 3:7也就是说看 30 分钟视频至少要敲 70 分钟代码。后续可以继续扩展的方向有四个深入学习 ES6 新特性、学习 Vue / React 框架、掌握 Git 和打包工具、了解前端性能优化和部署流程。每完成一个阶段再回头看最初写的代码都会发现明显的进步。把课程里的示例代码从头到尾敲一遍再按这篇文章的验证方法逐个跑通你的 JavaScript 基础就算真正立住了。建议收藏备用尤其是环境准备和报错排查两张表学习过程中会反复用到。