JavaScript / ES6
目标:2–3 小时快速具备可实战能力。按“概念 → 示例 → 练习 → 自测”结构组织,聚焦高频必会内容。
1) 概念:核心概念与必须掌握的 API/配置(80/20 精选)
1.1 语言与语法(必知 16 条)
- 
let / const:两者都是块级作用域;默认用const,只有需要重赋值时用let,避免var。 - 
解构赋值:从对象/数组中按结构取值更简洁;支持默认值与重命名(
{a: x=0})。 - 
展开/剩余
...:浅拷贝/合并对象数组或收集剩余参数;记住只是浅复制。 - 
模板字符串:多行 +
${expr}插值;避免繁琐的字符串拼接与转义。 - 
箭头函数:不绑定自己的
this,更适合回调;对象方法或需要this时用常规函数。 - 
this规则:与“调用方式”相关;可用call/apply/bind显式绑定,箭头函数捕获外层this。 - 
模块
import/export:浏览器与 Node 原生支持 ES Modules;Node 侧需package.json设"type":"module"。 - 
类与原型:
class是原型语法糖;优先组合而非深层继承。 - 
迭代与
for...of:遍历可迭代对象(数组、Map、Set);对象键值用Object.entries()。 - 
可选链
?./ 空值合并??:安全访问深层属性并为null/undefined提供默认值。 - 
默认参数 / 剩余参数:用默认参数代替函数内手动判空;用
...args代替arguments。 - 
Promise 基础:异步结果的占位;
.then/.catch/.finally组织回调与错误。 - 
async/await:同步写法处理异步;配合try/catch统一错误处理。 - 
并发组合:
Promise.all并行、allSettled容错、race/any竞速;注意失败传播与回滚策略。 - 
错误处理:抛出
new Error(msg);使用try/catch/finally,记录上下文信息。 - 
事件循环:微任务(Promise 回调)先于宏任务(
setTimeout);理解顺序有助定位异步问题。 
1.2 标准库与 Web API(必会 14 条)
- 
Array:
map/filter/reduce/find/some/every/flat/includes/sort;sort需自定义比较函数确保数值排序正确。 - 
Object:
keys/values/entries/fromEntries/assign/hasOwn;遍历时过滤掉原型链属性。 - 
Map/Set/WeakMap/WeakSet:Map 键可为任意值;Set 去重;Weak* 适合与对象弱关联防止内存泄漏。
 - 
字符串/正则:
includes/startsWith/endsWith/trim/padStart;正则用于匹配/替换。 - 
数值/Math:
Number.isNaN/parseInt/parseFloat/toFixed;注意浮点误差(如 0.1+0.2)。 - 
日期/国际化:
Date基础;用Intl.DateTimeFormat/NumberFormat做本地化。 - 
JSON:
JSON.parse/stringify;可用“替换器/还原器”控制序列化。 - 
structuredClone:原生深拷贝(不克隆函数/DOM 节点);优于JSONHack。 - 
URL/参数:
URL与URLSearchParams解析/构造链接和查询参数。 - 
Fetch:
fetch(url,{method,headers,body});HTTP 非 2xx 不抛错,需手动检查res.ok。 - 
取消/超时:
AbortController+signal取消请求;组件卸载时要 abort。 - 
Storage:
localStorage/sessionStorage仅存字符串;结合JSON存取对象。 - 
DOM 操作:
querySelector、classList、closest、append/remove、textContent/value。 - 
事件:
addEventListener、事件委托(监听父节点)与stopPropagation/preventDefault。 
1.3 配置与工程规范(实战 6 条)
- 
模块化启动:浏览器
<script type="module">;Node 设"type":"module"(仅 ES 模块)。 - 
本地服务器:用 VS Code Live Server 或
npx serve起 HTTP,避免file://导致模块/跨域问题。 - 
ESLint + Prettier:开启
eslint:recommended与常用规则(no-undef/no-unused-vars);统一代码风格。 - 
浏览器兼容:用
browserslist声明目标;老环境再考虑 Babel/打包器。 - 
导出风格:优先具名导出(便于重构与 Tree Shaking);默认导出仅限单一主对象。
 - 
官方文档优先:遇到歧义先查 MDN 与 ECMAScript 规范;网络 API 参考 WHATWG 文档。
 
2) 场景:典型应用与设计取舍(做决策时看这里)
- 
列表渲染:
Array.map+ 模板字符串快速输出;有用户输入时避免innerHTML,优先textContent或createElement以防 XSS。 - 
事件处理:大量动态项使用事件委托(绑在容器上);少量静态项逐个绑定更直观。
 - 
状态更新:小型页面直接改数组/对象;多人协作或需要撤销/时间旅行时倾向不可变更新(
.../map),成本是拷贝开销。 - 
网络请求:现代浏览器优先
fetch;若需拦截器/旧浏览器兼容可选 axios。 - 
并发请求:能并行就用
Promise.all;部分失败仍要继续用allSettled。 - 
取消/切换页:组件卸载时用
AbortController取消请求;否则可能更新已卸载节点。 - 
数据结构:键不是字符串或需保持插入顺序用 Map,需快速去重/查重用 Set;简单键值仍可用对象/数组。
 - 
模块组织:入口(副作用) + 工具(纯函数) + 业务模块;模块拆分提可读性,但过度拆分增加维护成本。
 - 
深拷贝/合并:首选
structuredClone与展开运算;需要自定义规则时考虑库(如 lodash)权衡体积。 - 
安全:DOM 写入优先
textContent;必须插入 HTML 时用可信模板或 DOMPurify 一类的净化器。 - 
- *
 
 
3) 示例:从零开始的最小可运行项目(含注释与运行步骤)
功能:极简 Todo(新增/完成/删除/过滤),数据持久化到 localStorage,首屏尝试拉取 3 条远端样例(失败则本地回退)。
亮点:使用 ES 模块、async/await、事件委托、AbortController、Map/Set 可选。
3.1 目录结构
es6-mini/
├─ index.html
├─ main.js
├─ storage.js
└─ utils.js
3.2 运行步骤(任选其一)
- 
推荐:在该目录执行
npx serve -p 5173或python3 -m http.server 5173,浏览器打开http://localhost:5173/。 - 
VS Code:安装 Live Server 插件,右键
index.html选择 “Open with Live Server”。 
直接双击
index.html在部分浏览器下会受file://限制导致模块导入失败,故建议走本地服务器。
3.3 代码
index.html
<!doctype html>
<html lang="zh">
<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width,initial-scale=1" />
  <title>ES6 最小可用 Todo</title>
  <style>
    body { font-family: system-ui, -apple-system, Segoe UI, Roboto, Arial; margin: 2rem; }
    .row { display:flex; gap:.5rem; margin-bottom: 1rem; }
    input[type="text"] { flex:1; padding:.5rem; }
    button { padding:.5rem .8rem; cursor:pointer; }
    ul { list-style: none; padding: 0; margin: 0; }
    li { display:flex; align-items:center; gap:.5rem; padding:.4rem 0; border-bottom: 1px solid #eee; }
    li.done span { text-decoration: line-through; color: #888; }
    .muted { color:#666; font-size:.9rem; }
  </style>
</head>
<body>
  <h1>Todo(ES6 模块)</h1>
  <form id="add-form" class="row">
    <input id="todo-input" type="text" placeholder="要做什么..." required />
    <button type="submit">添加</button>
    <select id="filter">
      <option value="all">全部</option>
      <option value="active">未完成</option>
      <option value="done">已完成</option>
    </select>
  </form>
  <ul id="list"></ul>
  <p class="muted"><span id="count">0</span> 项 · 双击可编辑,勾选切换完成</p>
  <script type="module" src="./main.js"></script>
</body>
</html>
utils.js
// 小工具:选择器、事件委托、ID 生成
export const qs = (sel, el = document) => el.querySelector(sel);
export const on = (el, type, selectorOrHandler, handlerMaybe) => {
  // 支持直接绑定或事件委托
  if (typeof selectorOrHandler === "function") {
    el.addEventListener(type, selectorOrHandler);
    return;
  }
  const selector = selectorOrHandler;
  const handler = handlerMaybe;
  el.addEventListener(type, (e) => {
    const target = e.target.closest(selector);
    if (target && el.contains(target)) handler(e, target);
  });
};
// 稳健的 ID:优先原生随机 UUID,回退时间戳+随机数
export const uid = () =>
  (crypto?.randomUUID?.() ?? `id_${Date.now()}_${Math.random().toString(36).slice(2)}`);
storage.js
const KEY = "todos-v1";
export const loadTodos = () => {
  try {
    return JSON.parse(localStorage.getItem(KEY) || "[]");
  } catch {
    return [];
  }
};
export const saveTodos = (todos) => {
  localStorage.setItem(KEY, JSON.stringify(todos));
};
main.js
import { qs, on, uid } from "./utils.js";
import { loadTodos, saveTodos } from "./storage.js";
// ---- 状态 ----
let todos = loadTodos(); // [{id,title,done}]
let filter = "all";
// ---- DOM ----
const $list = qs("#list");
const $form = qs("#add-form");
const $input = qs("#todo-input");
const $filter = qs("#filter");
const $count = qs("#count");
// ---- 初始化:首屏尝试获取 3 条远端样例(失败则本地回退)----
(async function bootstrap() {
  if (todos.length === 0) {
    try {
      const ctrl = new AbortController();
      const timer = setTimeout(() => ctrl.abort(), 3500); // 超时取消
      const res = await fetch("https://jsonplaceholder.typicode.com/todos?_limit=3", { signal: ctrl.signal });
      clearTimeout(timer);
      if (res.ok) {
        const data = await res.json();
        todos = data.map(d => ({ id: uid(), title: d.title, done: d.completed }));
        saveTodos(todos);
      } else {
        fallback();
      }
    } catch {
      fallback();
    }
  }
  render();
})();
function fallback() {
  todos = [
    { id: uid(), title: "学习 ES6 基础", done: false },
    { id: uid(), title: "用 fetch 拉数据", done: true },
    { id: uid(), title: "把数据存到 localStorage", done: false },
  ];
  saveTodos(todos);
}
// ---- 渲染 ----
function render() {
  const frag = document.createDocumentFragment();
  const filtered = todos.filter(t =>
    filter === "active" ? !t.done : filter === "done" ? t.done : true
  );
  for (const t of filtered) {
    const li = document.createElement("li");
    li.dataset.id = t.id;
    if (t.done) li.classList.add("done");
    const cb = document.createElement("input");
    cb.type = "checkbox";
    cb.checked = t.done;
    cb.setAttribute("aria-label", "切换完成");
    const span = document.createElement("span");
    span.textContent = t.title;
    span.contentEditable = "true"; // 双击后可直接编辑(简化演示)
    const del = document.createElement("button");
    del.type = "button";
    del.textContent = "删除";
    del.className = "del";
    li.append(cb, span, del);
    frag.append(li);
  }
  $list.replaceChildren(frag);
  $count.textContent = String(todos.length);
}
// ---- 事件:添加 ----
on($form, "submit", (e) => {
  e.preventDefault();
  const title = $input.value.trim();
  if (!title) return;
  todos.unshift({ id: uid(), title, done: false });
  saveTodos(todos);
  $input.value = "";
  render();
});
// ---- 事件:过滤 ----
on($filter, "change", () => {
  filter = $filter.value;
  render();
});
// ---- 事件:列表(委托) 勾选/删除/编辑 ----
on($list, "change", "li input[type=checkbox]", (e, target) => {
  const id = target.closest("li").dataset.id;
  const item = todos.find(t => t.id === id);
  if (item) {
    item.done = target.checked;
    saveTodos(todos);
    render();
  }
});
on($list, "click", "li .del", (e, btn) => {
  const id = btn.closest("li").dataset.id;
  todos = todos.filter(t => t.id !== id);
  saveTodos(todos);
  render();
});
on($list, "blur", "li span", (e, span) => {
  // 简易编辑:失焦时保存;防 XSS 使用 textContent,而非 innerHTML
  const id = span.closest("li").dataset.id;
  const item = todos.find(t => t.id === id);
  if (item) {
    item.title = span.textContent.trim();
    saveTodos(todos);
    render();
  }
});
为什么这套示例足够“实战”?
覆盖了模块化、异步请求、取消/超时、DOM 事件委托、状态管理、持久化、输入安全(textContent)和基本可访问性(aria/label)。
4) 常见坑与调试技巧(踩坑少一半)
4.1 常见坑(如何避免)
- 
var提升与闭包循环:for (var i...)里的异步回调共享同一i;用let或创建块级作用域。 - 
this迷惑:回调里的this往往不是你以为的对象;需要this的方法用常规函数并bind,回调多用箭头函数。 - 
浮点误差:
0.1 + 0.2 !== 0.3;使用整数放大再缩小或toFixed/Math.round控制显示。 - 
fetch不会为 404/500 抛错:先检查res.ok再res.json();用try/catch包裹。 - 
未取消的请求:组件卸载后仍回调更新 DOM;用
AbortController或在回调中检查“已卸载”标记。 - 
for...in枚举原型链:产生意外键;仅在真正需要时使用,或配合Object.hasOwn() / hasOwnProperty。 - 
sort默认字典序:[2, 10].sort()得到[10, 2];数值排序用(a,b) => a - b。 - 
深浅拷贝混淆:展开
...是浅拷贝;深层对象用structuredClone。 - 
||与??混用:0/''/false是“有效值”但被||视作假;用??仅针对null/undefined。 - 
CORS:跨域被浏览器拦截;需服务端正确设置
Access-Control-Allow-Origin,前端无法单方面解决。 - 
时间与时区:
Date默认为本地时区;序列化用toISOString(),显示用Intl.DateTimeFormat。 - 
内存泄漏:忘记解绑事件监听/定时器;使用事件委托或记录并统一清理。
 
4.2 调试技巧(提效 3 倍)
- 
DevTools 断点:右键行号设条件断点;在代码中用
debugger精确停住。 - 
Console 家族:
console.table/dir/groupCollapsed/time/assert让日志更有结构。 - 
网络面板:看请求头/响应体/时序;用复制
fetch作为重放模板。 - 
DOM 断点:在元素上设“属性修改/节点移除”断点,定位谁在改你的 DOM。
 - 
性能与内存:
performance.now()粗测耗时;快照检查泄漏(观察监听器与闭包)。 - 
Node 调试:
node --inspect,用 Chrome DevTools 附加调试。 - 
- *
 
 
5) 练习清单(分层 3×3)
入门(掌握基本语法与 API)
- 
用
map/filter/reduce:给定一组订单,算总金额与未付款列表,输出为字符串模板。 - 
写一个
request(url, {timeout}):使用fetch + AbortController,超时自动取消并抛错。 - 
DOM 小练习:输入框+按钮,把输入项追加到列表;用事件委托完成删除。
 
巩固(综合使用异步、模块与存储)
- 
分页列表:拉取分页数据(如 GitHub 用户仓库),实现“上一页/下一页”,并缓存到
sessionStorage。 - 
设置面板:用
URLSearchParams读写筛选条件到地址栏,实现“可分享的筛选”。 - 
模块拆分:把通用工具与业务逻辑拆成两个模块,使用具名导出并写 3 条单元“自测”断言。
 
进阶(性能与工程化思维)
- 
并发控制:实现
limit(promiseFactories, max),限制并发下载数量。 - 
防抖/节流:实现通用
debounce与throttle,用在搜索输入与滚动事件。 - 
数据结构选择:用 Set 去重大列表,再用 Map 按
id快速查找、更新与合并。 
6) 自测(Checklist + 闭卷题)
6.1 自测清单(打 √)
- 
我能解释
let/const与var的差别,并始终默认使用const。 - 
我能在代码中正确使用解构、展开、模板字符串与默认参数。
 - 
我能用
async/await + try/catch写出一次网络请求并处理非 2xx。 - 
我知道何时用
Promise.all / allSettled / race,以及失败传播的影响。 - 
我能在 DOM 中用事件委托管理列表项的点击/勾选/删除。
 - 
我能安全地把用户输入插入页面(
textContent而非innerHTML)。 - 
我能解释微任务/宏任务的执行顺序,并推断一段代码的打印顺序。
 - 
我知道在 Node 中如何启用 ES 模块(
"type":"module")。 - 
我会用
console.table、条件断点与debugger快速定位问题。 
6.2 闭卷题(含标准答案)
题 1:事件循环顺序
console.log(1);
setTimeout(() => console.log(2));
Promise.resolve().then(() => console.log(3));
console.log(4);
答案:1, 4, 3, 2。
解释:同步先执行(1、4),微任务(Promise 回调 3)先于宏任务(setTimeout 2)。
题 2:?? 与默认参数、解构
const cfg = { retry: 0, timeout: undefined };
const { retry = 3, timeout = 5000 } = cfg;
const maxRetry = cfg.retry ?? 5;
console.log(retry, timeout, maxRetry);
答案:输出 0 5000 0。
解释:解构默认值在属性为 undefined 时生效(timeout→5000);retry 已有值 0 不用默认;?? 只在 null/undefined 时用默认,因此 0 ?? 5 得 0。
题 3:箭头函数的 this
const counter = {
  n: 0,
  incLater() {
    setTimeout(() => { console.log(this.n++); }, 0);
  }
};
counter.incLater();
答案:打印 0(随后 n 变为 1)。
解释:箭头函数不绑定自己的 this,捕获外层 incLater 的 this(即 counter)。
附:推荐官方文档(查阅优先)
- 
MDN:JavaScript 语言基础、内置对象与 Web API(关键词直达:Promise、fetch、Map/Set、URL、Storage、Intl、structuredClone、事件)。
 - 
ECMAScript 规范:语言行为的最终解释(遇到边界/兼容性问题时参考)。
 - 
WHATWG/Fetch:网络请求与流式处理的详细说明。
 - 
ESLint:
eslint:recommended规则与最佳实践;Prettier 风格化说明。 
建议随用随查:用 MDN 作为 API 真值表,工程争议用 ESLint 规则与团队规范统一口径。祝你上手顺滑,直接用本示例改造即可投入小功能开发!
