直接开始正文。
先打一个实际的场景:很多刚接触前端的人,会觉得 JavaScript 不过是一门“给网页加点交互”的脚本语言,甚至有人把它和 Java 混为一谈。等到真正开始准备校招、社招,打开招聘网站一看,前端岗位的 JD 里几乎都写着“熟练掌握 JavaScript,理解 ES6+ 语法,有项目实战经验”。这时候才发现,JavaScript 不仅是前端的根基,也是决定你能否拿到 Offer 的关键一环。
本文不打算只贴一份“XX天速成”的标题党路线图,而是围绕一套完整的 140 集零基础 JavaScript 教程内容,把从语法入门到项目实战、从学习环境到源码调试、从常见报错到工程规范的路径完整拆解一遍。无论你是刚接触编程的在校生,还是想转行前端、准备社招的开发同学,都可以按这条路线对照检查自己的知识盲区。
1. 为什么 2026 年学 JavaScript 仍然是前端入门的正确选择
1.1 JavaScript 到底是什么
JavaScript 是一门运行在浏览器环境中的动态编程语言,最初的设计目标是让网页具备交互能力,比如点击按钮、提交表单、动态更新页面内容。随着 Node.js 的出现,JavaScript 也跑到了服务端,变成了前后端都可以写的主流语言。
从技术分类上看,JavaScript 属于“解释型 + 动态类型”的语言,不需要像 Java、C++ 那样先编译再运行,而是由 JavaScript 引擎逐行解释执行。浏览器中常见的 V8 引擎就是 Chrome 和 Node.js 的底层执行环境。
很多初学者最容易混淆的两个概念是 Java 和 JavaScript。两者从语法风格到运行环境几乎没有关系,JavaScript 在 1995 年由 Brendan Eich 用 10 天设计出来,名字里的 “Java” 更多是早期市场推广的产物。
1.2 前端校招社招对 JavaScript 的要求
如果去翻一下近两年的前端面试题,会发现 JavaScript 的考察重点非常集中:
- 基础语法:变量声明、数据类型、运算符、流程控制。
- 函数进阶:闭包、作用域、this 指向、箭头函数。
- 异步编程:回调地狱、Promise、async/await、事件循环。
- ES6+ 新特性:解构赋值、模板字符串、模块化、扩展运算符。
- 手写源码:常见的手写 Promise、防抖节流、深拷贝、数组去重。
- 浏览器 API:DOM 操作、BOM、事件机制、本地存储。
- 框架基础:Vue、React 底层都离不开 JavaScript 的响应式原理和组件化思想。
校招更看重基础是否扎实、学习能力是否强;社招则会结合项目经验,考察解决实际问题的能力,比如性能优化、错误处理、代码可维护性。但不管是哪种,JavaScript 这一关都是绕不开的。
1.3 零基础学习容易踩的认知误区
这里先说几个零基础同学常见的学习误区,避免走弯路。
第一个误区是“只看不敲”。JavaScript 语法看起来简单,但真正上手写代码时,变量提升、闭包、this 指向这些概念如果不亲自实验,很难形成深刻记忆。
第二个误区是“跳过基础直接学框架”。很多同学看完变量和函数,就急着去学 Vue 或 React,结果遇到“为什么数据变了页面不更新”“为什么这里的 this 是 undefined”这类问题时,只能靠搜索解决,根因却不清楚。框架可以让你快速做出页面,但 JavaScript 基础才决定你能走多远。
第三个误区是“不重视调试”。遇到报错不是先去读懂报错信息,而是直接把整段代码删掉重写。正确做法应该是打开浏览器控制台,看报错行号、错误类型、调用堆栈,再根据提示逐步定位。
2. 学习前的环境准备与工具链
2.1 浏览器与开发者工具
学 JavaScript 不需要一开始就搭复杂环境,最简单的方式就是打开浏览器,按 F12 打开开发者工具,切到 Console 面板,直接输入:
console.log("Hello, JavaScript");按下回车就能看到输出。这个能力非常适合验证语法片段,比如测试数组方法、查看 API 返回结果。
Chrome 开发者工具是前端开发最重要的调试工具,建议重点掌握这几个面板:
- Elements:查看页面 DOM 结构和样式。
- Console:查看日志、报错、运行 JavaScript。
- Sources:断点调试、查看源码。
- Network:查看网络请求、响应状态、耗时。
- Application:查看 localStorage、Session Storage、Cookie。
2.2 编辑器选择
编辑器方面,目前主流选择是 VS Code。它免费、插件生态丰富、对 JavaScript 和前端开发支持很好。建议安装以下插件:
- Prettier:自动格式化代码。
- ESLint:检查代码规范。
- Live Server:启动本地静态服务器,实现页面热更新。
- JavaScript (ES6) code snippets:提供常用代码片段。
安装好之后,新建一个文件夹,创建一个index.html,再用 Live Server 打开,后面写项目时可以直接预览。
2.3 是否需要 Node.js
如果只是学浏览器端的 JavaScript,暂时不装 Node.js 也没关系。但到了后期学习模块化、构建工具、npm 包管理时,Node.js 几乎是必需品。
Node.js 可以在 官网 下载 LTS 版本。版本号不需要追求最新,LTS(长期支持版)更稳定。安装完成后在终端执行:
node -v npm -v能输出版本号就说明安装成功。
需要注意:Node.js 环境中的 JavaScript 和浏览器中的 JavaScript 在执行环境上存在差异。浏览器里有window、document这些对象,Node.js 环境里则是global、process、module。刚开始学习时,先以浏览器环境为主,后期再区分两者差异。
2.4 一个示例项目结构
为了后续实战方便,建议建立如下目录结构:
js-learning/ ├── 01-basics/ │ └── index.html ├── 02-dom/ │ └── index.html ├── 03-async/ │ └── index.html ├── 04-project/ │ ├── index.html │ ├── style.css │ └── app.js └── README.md不用一步到位,但要养成“每个模块一个独立文件夹”的习惯,后期查找源码和复习会比较方便。
3. JavaScript 核心语法拆解
3.1 变量与数据类型
JavaScript 声明变量有三种方式:var、let、const。现在开发中推荐使用let和const,var了解即可。
区别如下:
var a = 1; // 函数作用域,存在变量提升 let b = 2; // 块级作用域,可以重新赋值 const c = 3; // 块级作用域,声明后不能重新赋值数据类型方面,JavaScript 分为基本类型和引用类型。
基本类型包括:number、string、boolean、null、undefined、symbol、bigint。 引用类型包括:object、array、function。
一个很容易混淆的点是null和undefined的区别。undefined表示变量已声明但未赋值;null表示变量已经赋值为空。用typeof null会返回"object",这是 JavaScript 早期实现遗留的 bug,但被保留了下来。
接下来看一个综合示例:
// 文件路径:01-basics/index.html 内联 script 或单独 01-basics/script.js let name = "张三"; const age = 25; let isStudent = false; let score = null; let grade; console.log(name, age, isStudent, score, grade); console.log(typeof name); // string console.log(typeof age); // number console.log(typeof isStudent); // boolean console.log(typeof score); // object console.log(typeof grade); // undefined3.2 函数与作用域
函数是 JavaScript 的一等公民。这意味着函数可以赋值给变量、作为参数传递、作为返回值返回。
声明函数的常见方式:
// 函数声明 function add(a, b) { return a + b; } // 函数表达式 const subtract = function (a, b) { return a - b; }; // 箭头函数 const multiply = (a, b) => a * b; console.log(add(1, 2)); // 3 console.log(subtract(5, 2)); // 3 console.log(multiply(3, 4)); // 12作用域决定了变量在哪里可以被访问。JavaScript 有全局作用域、函数作用域和块级作用域。
let globalVar = "全局变量"; function demo() { let functionVar = "函数内变量"; if (true) { let blockVar = "块内变量"; console.log(globalVar); // 可以访问 console.log(functionVar); // 可以访问 console.log(blockVar); // 可以访问 } // 这里无法访问 blockVar,因为它是块级作用域 } demo();闭包是函数和作用域结合产生的强大特性。简单理解:内层函数可以访问外层函数的变量,并且这个变量的引用被保留下来。
function createCounter() { let count = 0; return function () { count++; return count; }; } const counter = createCounter(); console.log(counter()); // 1 console.log(counter()); // 2 console.log(counter()); // 3闭包的典型应用场景包括:数据私有化、防抖节流、柯里化。
3.3 对象与数组
对象是 JavaScript 中最常用的数据结构。创建对象有两种常见方式:
// 对象字面量 const user = { name: "李四", age: 30, greet() { console.log(`你好,我是${this.name}`); }, }; // new Object() const user2 = new Object(); user2.name = "王五"; user2.age = 25; user.greet(); console.log(user2.name);数组方面,需要重点掌握map、filter、reduce、forEach这几个方法,它们在前端开发中使用率极高。
const numbers = [1, 2, 3, 4, 5]; // map:对每个元素做变换,返回新数组 const doubled = numbers.map((n) => n * 2); console.log(doubled); // [2, 4, 6, 8, 10] // filter:筛选符合条件的元素,返回新数组 const evens = numbers.filter((n) => n % 2 === 0); console.log(evens); // [2, 4] // reduce:累加器,返回一个结果 const sum = numbers.reduce((acc, curr) => acc + curr, 0); console.log(sum); // 15 // forEach:遍历,不返回新数组 numbers.forEach((n) => console.log(n));实际开发中要注意:map和forEach的区别是什么?map会返回一个新数组,forEach不返回任何值。如果需要基于原数组生成新数组,用map;如果只是遍历执行副作用操作,用forEach。
3.4 ES6+ 常用语法
ES6(ECMAScript 2015)是 JavaScript 发展史上的里程碑,后续版本在此基础上持续扩展。零基础阶段需要掌握下面这些内容。
模板字符串:用反引号拼接字符串,支持多行和表达式插值。
const person = "小明"; const hobby = "编程"; console.log(`${person} 喜欢 ${hobby}`);解构赋值:从对象或数组中提取值,赋值给变量。
const userInfo = { username: "admin", password: "123456" }; const { username, password } = userInfo; console.log(username); // admin const colors = ["red", "green", "blue"]; const [first, second] = colors; console.log(first); // red console.log(second); // green扩展运算符:用于展开数组、对象,或者合并它们。
const arr1 = [1, 2, 3]; const arr2 = [4, 5, 6]; const merged = [...arr1, ...arr2]; console.log(merged); // [1, 2, 3, 4, 5, 6] const obj1 = { a: 1 }; const obj2 = { b: 2 }; const mergedObj = { ...obj1, ...obj2 }; console.log(mergedObj); // { a: 1, b: 2 }模块化:通过export导出,通过import导入。
// 文件路径:math.js export function add(a, b) { return a + b; } export const PI = 3.14159;// 文件路径:main.js import { add, PI } from "./math.js"; console.log(add(2, 3)); // 5 console.log(PI); // 3.14159这里需要解释一下:模块化代码需要在服务器环境下运行,直接用浏览器打开index.html文件会因为 CORS 限制报错。这也就是前面建议安装 Live Server 的原因。
4. JavaScript 异步编程
4.1 异步编程为什么重要
浏览器中的很多操作都是异步的,比如发送网络请求、读取文件、定时器。如果所有操作都同步执行,页面在等待请求返回时会卡住,用户体验会非常差。
异步编程的演进路径是:回调函数 → Promise → async/await。理解这条演进路径,能帮助你读懂网上大量的历史代码。
4.2 回调函数与回调地狱
先看回调函数:
setTimeout(() => { console.log("2秒后执行"); }, 2000);如果多个异步操作层层嵌套,就会出现“回调地狱”:
setTimeout(() => { console.log("任务1"); setTimeout(() => { console.log("任务2"); setTimeout(() => { console.log("任务3"); }, 1000); }, 1000); }, 1000);这种代码阅读困难、错误处理麻烦,于是 Promise 应运而生。
4.3 Promise 与 async/await
Promise 是一种用于处理异步操作的对象。它有三种状态:pending(进行中)、fulfilled(已成功)、rejected(已失败)。
const request = new Promise((resolve, reject) => { setTimeout(() => { // 模拟成功 resolve("数据加载成功"); // 模拟失败 // reject("数据加载失败"); }, 1000); }); request .then((data) => { console.log(data); }) .catch((error) => { console.log(error); });Promise 链式调用解决回调地狱问题:
function task(name, delay) { return new Promise((resolve) => { setTimeout(() => { console.log(name); resolve(); }, delay); }); } task("任务1", 1000) .then(() => task("任务2", 1000)) .then(() => task("任务3", 1000));async/await 是 Promise 的语法糖,让异步代码写起来像同步代码。
async function runTasks() { await task("异步任务1", 1000); await task("异步任务2", 1000); await task("异步任务3", 1000); console.log("全部完成"); } runTasks();这里有一个非常重要的知识点:await只能用在async函数内部。如果直接在普通函数里写await,会抛出SyntaxError。
在实际项目中,推荐优先使用 async/await,因为可读性更好;但在理解原理阶段,必须掌握 Promise 的 then/catch 写法,因为很多第三方库的底层仍然返回 Promise。
5. 完整实战案例:待办事项应用
接下来用一个待办事项(Todo List)项目,把前面的语法、DOM 操作、数组方法、本地存储、事件处理串起来。这个项目麻雀虽小,但覆盖了前端基础的核心知识。
5.1 需求与功能拆分
我们需要实现:
- 输入框输入待办内容,点击添加按钮,列表新增一条。
- 点击待办项,切换完成状态,已完成项添加删除线。
- 点击删除按钮,删除对应待办项。
- 数据保存到 localStorage,刷新页面后仍然保留。
5.2 HTML 结构
<!-- 文件路径:04-project/index.html --> <!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>JavaScript 待办事项实战</title> <link rel="stylesheet" href="style.css" /> </head> <body> <div class="todo-app"> <h1>我的待办</h1> <div class="input-area"> <input type="text" id="todoInput" placeholder="输入待办事项" /> <button id="addBtn">添加</button> </div> <ul id="todoList"></ul> </div> <script src="app.js"></script> </body> </html>5.3 基础样式
/* 文件路径:04-project/style.css */ * { margin: 0; padding: 0; box-sizing: border-box; } body { font-family: "Microsoft YaHei", sans-serif; background: #f0f2f5; display: flex; justify-content: center; padding-top: 80px; } .todo-app { width: 420px; background: #fff; border-radius: 10px; padding: 24px; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); } .todo-app h1 { font-size: 22px; margin-bottom: 16px; text-align: center; } .input-area { display: flex; gap: 8px; margin-bottom: 16px; } .input-area input { flex: 1; padding: 8px 12px; border: 1px solid #ddd; border-radius: 6px; font-size: 14px; } .input-area button { padding: 8px 16px; background: #1677ff; color: #fff; border: none; border-radius: 6px; cursor: pointer; } #todoList { list-style: none; } .todo-item { display: flex; align-items: center; justify-content: space-between; padding: 10px 8px; border-bottom: 1px solid #f0f0f0; } .todo-item.completed .todo-text { text-decoration: line-through; color: #999; } .delete-btn { background: transparent; border: none; color: #ff4d4f; cursor: pointer; font-size: 14px; }5.4 JavaScript 核心代码
// 文件路径:04-project/app.js // 从 localStorage 读取数据,没有则返回空数组 let todos = JSON.parse(localStorage.getItem("todos")) || []; const input = document.getElementById("todoInput"); const addBtn = document.getElementById("addBtn"); const list = document.getElementById("todoList"); // 渲染列表 function render() { list.innerHTML = ""; todos.forEach((todo, index) => { const li = document.createElement("li"); li.className = "todo-item" + (todo.completed ? " completed" : ""); li.innerHTML = ` <span class="todo-text">${todo.text}</span> <div> <button class="toggle-btn">切换</button> <button class="delete-btn">删除</button> </div> `; // 切换完成状态 const toggleBtn = li.querySelector(".toggle-btn"); toggleBtn.addEventListener("click", () => { todos[index].completed = !todos[index].completed; saveAndRender(); }); // 删除待办 const deleteBtn = li.querySelector(".delete-btn"); deleteBtn.addEventListener("click", () => { todos.splice(index, 1); saveAndRender(); }); list.appendChild(li); }); } // 保存数据并重新渲染 function saveAndRender() { localStorage.setItem("todos", JSON.stringify(todos)); render(); } // 添加待办 function addTodo() { const text = input.value.trim(); if (!text) { alert("待办内容不能为空"); return; } todos.push({ text, completed: false }); input.value = ""; saveAndRender(); } // 绑定事件 addBtn.addEventListener("click", addTodo); input.addEventListener("keydown", (event) => { if (event.key === "Enter") { addTodo(); } }); // 初始渲染 render();5.5 运行与验证
在 VS Code 中,右键index.html,选择 “Open with Live Server”。页面打开后:
- 在输入框输入“学习 JavaScript”,点击添加,列表出现一条新记录。
- 点击“切换”,对应文本出现删除线。
- 点击“删除”,对应记录消失。
- 刷新页面,数据仍然存在,说明 localStorage 生效。
打开 F12 的 Application 面板,在 Local Storage 中可以看到当前页面的存储数据。
这个项目的意义在于:它把 JavaScript 的 DOM 操作、数组遍历、事件监听、数据持久化全部串在了一起。很多框架项目看似复杂,归根结底也是在处理这三件事:获取数据、渲染页面、响应用户操作。
6. 从语法到项目实战的学习路线设计
前文提到,这套内容一共 140 集。很多同学看到“140 集”的第一反应是“这么多,要学多久”,其实更关键的是怎么把这些内容合理地拆解成阶段,每一阶段有清晰的产出。
6.1 学习路线分阶段规划
第一阶段:JavaScript 语法基础(约占 40 集左右)
内容覆盖:变量、数据类型、运算符、流程控制、函数、对象、数组、字符串方法、循环、ES6 新语法。学完这个阶段,你应该能做到脱离教程,独立完成 “输入数字求阶乘”“数组排序去重”“对象属性操作”这类小题。
第二阶段:DOM/BOM 与浏览器 API(约占 25 集左右)
内容覆盖:DOM 查询、DOM 修改、事件流、事件委托、定时器、Cookie、localStorage、sessionStorage、正则表达式。学完这个阶段,你应当能实现图片轮播、Tab 切换、表单校验、倒计时等常见需求。
第三阶段:异步编程与进阶核心(约占 25 集左右)
内容覆盖:同步与异步、回调、Promise、async/await、事件循环、闭包进阶、原型链、this 指向、防抖节流、深拷贝、JSON 处理、错误处理。学完这个阶段,你应当能解释“为什么按钮连续点击会造成重复请求”,并用防抖或节流解决。
第四阶段:工程化与模块化(约占 15 集左右)
内容覆盖:npm 使用、模块化、ES6 import/export、Webpack 或 Vite 基础、开发环境与生产环境、代码规范、调试工具、Git 版本控制。学完这个阶段,你应当能自己搭建一个前端项目的开发骨架。
第五阶段:项目实战(约占 35 集左右)
内容覆盖:接口请求、渲染列表、状态管理基础、组件拆分、打包发布。实战项目应当从简单到复杂,先做单页小应用,再做包含多模块的中型项目,最后可以尝试用 Vue 或 React 重构或扩展。每个项目都要整理成自己的简历项目,写清楚技术栈、实现功能和难点。
6.2 每个阶段的对应产出
学习的最大问题不是输入不足,而是输出不成型。建议每学完一个阶段,就整理一份自己的总结笔记,内容包含:
- 该阶段核心知识点清单。
- 自己踩过的坑和解决方案。
- 可运行的示例代码。
- 阶段项目源码。
比如第一阶段结束后,提交一个“学生成绩管理系统”控制台版本;第二阶段结束后,做一个“待办事项应用”(也就是上面的实战案例)。这样到了求职阶段,你至少有 3 到 5 个能拿得出手的项目,而不是只有一段学习经历。
6.3 校招与社招侧重点差异
校招面试官考察的更多是:
- 语言基础是否牢固。
- 对异步、闭包、原型链这些核心概念是否理解到位。
- 项目是不是自己真正写的。
- 遇到 bug 是否具备独立排查能力。
社招面试官则更关注:
- 代码可维护性,能否写出统一规范的代码。
- 性能优化意识,比如长列表渲染、大对象深拷贝、网络请求并发控制。
- 异常处理能力,有没有做错误边界、接口容错。
- 协作能力,能否看懂他人代码并安全修改。
所以不要只追求“跟着教程敲一遍”。真正有效的做法是,学完每节课后,先合上教程写出自己的版本,再对比差异,总结出更深层的原因。
7. JavaScript 学习高频报错与排查思路
下面整理一份初学者最常遇到的报错和排查清单。这些内容在 140 集教程中会反复出现,但单独抽出来做一次归纳,能帮你快速定位。
| 问题现象 | 常见原因 | 解决思路 |
|---|---|---|
Uncaught SyntaxError: Unexpected token | 括号、引号不匹配,或使用了非法字符 | 查看报错行附近代码;检查中英文标点 |
ReferenceError: xxx is not defined | 变量未声明或拼写错误,函数名拼写不一致 | 确认变量是否声明,检查大小写;确认脚本引入顺序 |
TypeError: xxx is not a function | 调用了一个不是函数的变量;undefined/null 上调用方法 | 在调用前用 console.log 打印目标变量,确认它的类型 |
Cannot read properties of undefined | 读取了 undefined 对象的某个属性 | 使用可选链?.,或在访问前判断数据是否存在 |
Cannot set properties of null | DOM 元素未找到,可能脚本在元素渲染前执行 | 把 script 放到 body 末尾,或使用DOMContentLoaded |
NaN | 对一个不是数字的值进行了数学运算 | 使用Number()转换后判断isNaN() |
| 点击事件只有最后一次生效 | 在循环中绑定事件时没有使用 let 或闭包 | 使用let声明循环变量,或使用事件委托 |
| localStorage 存储后变成字符串 | JSON 数据未序列化和反序列化 | 使用JSON.stringify()存入,JSON.parse()取出 |
以“Cannot read properties of undefined”为例,这个报错出现的频率非常高。常见场景是接口返回的数据结构不确定,比如:
// 接口可能返回 { data: { list: [...] } },也可能返回空对象 fetch("/api/list") .then((res) => res.json()) .then((data) => { // 如果 data 为空,data.list 会直接报错 console.log(data.list.length); });更健壮的写法是:
const list = data?.list ?? []; console.log(list.length);可选链?.能在属性不存在时返回 undefined,而不是抛错;空值合并??能在左侧为 null/undefined 时取默认值。这类细节就是实际工程经验和纯语法的区别。
8. 最佳实践与工程化建议
如果你的目标不仅仅是看懂代码,而是要写出能在团队中协作、能维护的前端代码,下面这些习惯越早养成越好。
8.1 命名规范
变量名和函数名要能“望文生义”。推荐使用小驼峰命名,常量用全大写,组件名用大驼峰。
// 不推荐 let a = "张三"; let d = new Date(); // 推荐 let userName = "张三"; let currentDate = new Date();函数名以动词开头,比如getUserList、handleClick、saveData。布尔类型变量用is、has、can开头,例如isLogin、hasPermission、canEdit。
8.2 注释与代码可维护性
注释不需要解释“做了什么”,而要解释“为什么这么做”。浏览器会自动忽略注释,但同事和未来的你会认真阅读。
// 不推荐 // 给 a 加 1 a++; // 推荐 // 使用 count 累加方式,避免浮点运算误差 let count = 0; count += 1;8.3 错误处理与边界检查
真实项目中最怕的不是功能复杂,而是代码没做边界处理。接口请求一定要有错误提示,输入框要校验空值和类型,本地存储要加 try/catch。
async function fetchData() { try { const res = await fetch("/api/data"); if (!res.ok) { throw new Error(`请求失败:${res.status}`); } const data = await res.json(); return data; } catch (error) { console.error("fetchData error:", error); alert("加载失败,请稍后重试"); } }8.4 性能优化意识
零基础阶段不需要过度优化,但两个性能习惯要从小项目开始培养:
第一,减少不必要的 DOM 操作。每一次innerHTML赋值或appendChild都可能触发页面重排,大量操作时可以把元素先拼成字符串,最后一次性插入 DOM。
第二,使用事件委托减少监听器数量。比如上面的待办项目,如果给每个“删除”按钮都绑定监听器,列表项很多时会占用大量内存。事件委托利用事件冒泡,把监听器绑定到父元素上,通过event.target判断点击目标。
list.addEventListener("click", (event) => { if (event.target.classList.contains("delete-btn")) { // 找到对应 li const li = event.target.closest(".todo-item"); const index = Array.from(list.children).indexOf(li); todos.splice(index, 1); saveAndRender(); } });8.5 Git 与版本管理
建议项目一开始就初始化 Git 仓库,每完成一个功能就提交一次。提交信息格式推荐:feat: 添加待办功能、fix: 修复刷新后数据丢失问题。这不仅是团队协作的基本功,也是简历里展示项目细节的一部分。
git init git add . git commit -m "feat: 完成待办事项应用初版"9. 总结与下一步行动
姑且把最后这部分当作一个“行动清单”来用。你不需要等到把所有集数都看完、记完笔记才开始动手,反而是每看完一个阶段,就立刻把该阶段的内容整合成一个小项目,让教程里的语法变成自己亲手敲过并且现实跑通的代码。
140 集的学习量看上去不少,但如果拆成“语法基础 → DOM 操作 → 异步进阶 → 工程化 → 项目实战”五个阶段,每一阶段都有明确的任务和产出,压力会小很多。对校招同学来说,重点是把地基打牢,至少能够手写常见场景的 JavaScript 代码;对社招同学来说,重点则是把经验沉淀成工程化的思考方式,比如为什么这样写、出问题时怎么排查、如何让代码更容易被团队接手。
抛开所有的概念和技巧,前端学习说到底就是一个反复“写代码、看报错、改代码、再验证”的过程。打开编辑器,新建一个index.html,从console.log("Hello, JavaScript")开始,把第一行代码敲出来,这比收藏十篇教程都更重要。