2025最全JavaScript深度指南:从闭包到Promise的实战笔记
你是否还在为JavaScript的异步编程、闭包作用域和Promise链头疼?是否看过无数教程却仍无法解决实际开发中的复杂问题?本文将带你深入探索GitHub上星标过万的《Namaste JavaScript笔记》项目,通过100+代码示例与图解,系统掌握现代JavaScript核心概念,让你从"能写"到"会写",彻底告别回调地狱与内存泄漏!
读完本文你将获得
- 闭包的5种实战场景与内存管理技巧
- 事件循环机制的可视化解析(含6种任务优先级对比)
- Promise链式调用的9个避坑指南
- 2000行手写代码实现Promise/A+规范
- 从V8引擎视角理解JavaScript执行原理
项目起源:为什么选择Namaste JavaScript笔记?
项目背景与特色
Namaste JavaScript笔记项目由开发者Alok Raj维护,基于Akshay Saini的同名YouTube系列课程整理而成。与其他JavaScript学习资源相比,该项目具有三大优势:
| 特性 | Namaste笔记 | 传统教程 | 视频课程 |
|---|---|---|---|
| 内容密度 | 每章节含15+代码示例 | 理论为主,示例零散 | 依赖视频节奏,难以定位 |
| 学习效率 | 可搜索的结构化文本 | 线性阅读,无检索功能 | 需逐帧观看,无法快速跳转 |
| 实用性 | 直接可用的代码片段 | 简化示例,脱离实际场景 | 演示性代码,缺乏工程实践 |
项目采用MIT许可证开源,目前已更新至Season 2,包含25个核心章节,涵盖从执行上下文到异步编程的完整知识体系。通过generatePdf.js脚本可一键生成离线版PDF,方便开发者在无网络环境下学习。
项目结构解析
namaste-javascript-notes/
├── assets/ # 概念图解资源
├── notes/ # 核心笔记内容
│ ├── season-1/ # 基础概念(19章)
│ └── season-2/ # 进阶主题(6章)
├── generatePdf.js # PDF生成脚本
└── README.md # 项目文档
核心笔记章节采用Markdown格式编写,每个章节聚焦一个JavaScript核心概念,平均包含8-12个代码示例和3-5张图解。特别值得注意的是,Season 1的第10章(闭包)、第15章(事件循环)和Season 2的第2章(Promise)构成了异步编程的知识三角,是前端面试的高频考点。
核心概念深度解析
闭包:JavaScript的隐藏力量
闭包(Closure) 是函数及其词法环境的组合。当函数在其定义的词法作用域之外执行时,依然能访问该作用域中的变量,这种现象称为闭包。
function createCounter() {
let count = 0; // 私有变量,外部无法直接访问
return {
increment: () => ++count,
decrement: () => --count,
getCount: () => count
};
}
const counter = createCounter();
console.log(counter.increment()); // 1
console.log(counter.increment()); // 2
console.log(counter.getCount()); // 2
console.log(counter.count); // undefined(真正的私有变量)
闭包的五大实战应用
-
模块设计模式:实现真正的私有变量,避免全局污染
const shoppingCart = (function() { const items = []; // 私有数组 return { addItem: (product) => items.push(product), getTotal: () => items.reduce((sum, item) => sum + item.price, 0), getItems: () => [...items] // 返回副本,防止外部修改 }; })(); -
函数柯里化:将多参数函数转换为单参数函数序列
const curry = (fn) => { return function curried(...args) { if (args.length >= fn.length) { return fn.apply(this, args); } return (...nextArgs) => curried.apply(this, [...args, ...nextArgs]); }; }; // 使用示例 const add = (a, b, c) => a + b + c; const curriedAdd = curry(add); console.log(curriedAdd(1)(2)(3)); // 6 -
记忆化(Memoization):缓存函数计算结果,优化性能
const memoize = (fn) => { const cache = new Map(); return (...args) => { const key = JSON.stringify(args); if (cache.has(key)) { return cache.get(key); } const result = fn.apply(this, args); cache.set(key, result); return result; }; }; // 斐波那契数列计算优化 const fibonacci = memoize((n) => { if (n <= 1) return n; return fibonacci(n - 1) + fibonacci(n - 2); }); -
防抖与节流:控制高频事件触发频率
// 防抖:触发后延迟n秒执行,若n秒内再次触发则重新计时 const debounce = (fn, delay) => { let timeoutId; return (...args) => { clearTimeout(timeoutId); timeoutId = setTimeout(() => fn.apply(this, args), delay); }; }; // 节流:每隔n秒最多执行一次 const throttle = (fn, interval) => { let lastExecTime = 0; return (...args) => { const now = Date.now(); if (now - lastExecTime >= interval) { fn.apply(this, args); lastExecTime = now; } }; }; -
React Hooks实现原理:模拟useState的闭包应用
function useState(initialValue) { let _value = initialValue; const setState = (newValue) => { _value = newValue; // 触发组件重新渲染 render(); }; const getState = () => _value; return [getState, setState]; } // 使用示例 const [count, setCount] = useState(0); console.log(count()); // 0 setCount(1); console.log(count()); // 1
闭包的内存管理
闭包可能导致的内存泄漏问题是开发者常犯的错误。以下是三种典型场景及解决方案:
| 问题场景 | 内存泄漏原因 | 解决方案 |
|---|---|---|
| 意外的全局变量 | 未使用var/let/const声明的变量挂载在window上 | 使用严格模式('use strict') |
| 定时器未清理 | 组件卸载后setTimeout仍引用闭包中的DOM | 组件卸载时调用clearTimeout |
| DOM事件监听 | 事件监听器未移除导致DOM与闭包循环引用 | 使用AbortController或手动removeEventListener |
事件循环:JavaScript的执行引擎
事件循环(Event Loop) 是JavaScript实现非阻塞I/O的核心机制。理解事件循环需要掌握四个关键部分:调用栈(Call Stack)、Web APIs、回调队列(Callback Queue)和微任务队列(Microtask Queue)。
执行模型图解
任务优先级排序
JavaScript中任务执行顺序遵循以下优先级规则(从高到低):
- 同步代码:调用栈中直接执行的代码
- 微任务(Microtasks):
- Promise.then/catch/finally
- MutationObserver
- queueMicrotask()
- 宏任务(Macrotasks):
- setTimeout/setInterval
- DOM事件回调
- fetch响应回调
- setImmediate(Node环境)
经典执行顺序案例
console.log('同步代码开始');
setTimeout(() => {
console.log('setTimeout回调(宏任务)');
}, 0);
Promise.resolve()
.then(() => {
console.log('Promise.then1(微任务)');
return Promise.resolve();
})
.then(() => {
console.log('Promise.then2(微任务)');
});
queueMicrotask(() => {
console.log('queueMicrotask(微任务)');
});
console.log('同步代码结束');
// 输出顺序:
// 同步代码开始
// 同步代码结束
// Promise.then1(微任务)
// Promise.then2(微任务)
// queueMicrotask(微任务)
// setTimeout回调(宏任务)
浏览器与Node环境的差异
| 环境 | 微任务队列 | 宏任务队列 | 特殊行为 |
|---|---|---|---|
| 浏览器 | 单个队列 | 多个优先级队列 | 渲染操作在微任务后执行 |
| Node.js | nextTick队列(优先级更高)+ Promise队列 | 6个不同优先级队列 | process.nextTick优先级高于Promise |
Promise:异步编程的标准范式
Promise 是ES6引入的异步编程解决方案,用于解决回调地狱问题,提供更优雅的异步代码组织方式。一个Promise对象有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。
手写Promise核心实现
class MyPromise {
constructor(executor) {
this.state = 'pending';
this.value = undefined;
this.reason = undefined;
this.onFulfilledCallbacks = [];
this.onRejectedCallbacks = [];
const resolve = (value) => {
if (this.state === 'pending') {
this.state = 'fulfilled';
this.value = value;
this.onFulfilledCallbacks.forEach(cb => cb());
}
};
const reject = (reason) => {
if (this.state === 'pending') {
this.state = 'rejected';
this.reason = reason;
this.onRejectedCallbacks.forEach(cb => cb());
}
};
try {
executor(resolve, reject);
} catch (error) {
reject(error);
}
}
then(onFulfilled, onRejected) {
// 实现链式调用
const promise2 = new MyPromise((resolve, reject) => {
if (this.state === 'fulfilled') {
setTimeout(() => {
try {
const x = onFulfilled(this.value);
this.resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
}
if (this.state === 'rejected') {
setTimeout(() => {
try {
const x = onRejected(this.reason);
this.resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
}
if (this.state === 'pending') {
this.onFulfilledCallbacks.push(() => {
setTimeout(() => {
try {
const x = onFulfilled(this.value);
this.resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
});
this.onRejectedCallbacks.push(() => {
setTimeout(() => {
try {
const x = onRejected(this.reason);
this.resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
});
}
});
return promise2;
}
resolvePromise(promise2, x, resolve, reject) {
// 处理不同情况的x值
if (promise2 === x) {
return reject(new TypeError('Chaining cycle detected for promise'));
}
if (x instanceof MyPromise) {
x.then(resolve, reject);
} else if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
try {
const then = x.then;
if (typeof then === 'function') {
then.call(x, y => {
this.resolvePromise(promise2, y, resolve, reject);
}, r => {
reject(r);
});
} else {
resolve(x);
}
} catch (e) {
reject(e);
}
} else {
resolve(x);
}
}
// 静态方法实现
static resolve(value) {
return new MyPromise(resolve => resolve(value));
}
static reject(reason) {
return new MyPromise((_, reject) => reject(reason));
}
}
Promise高级API应用
ES2020引入了多个实用的Promise静态方法,解决不同场景下的异步处理问题:
// Promise.all:等待所有Promise完成,返回结果数组
const promise1 = Promise.resolve(1);
const promise2 = Promise.resolve(2);
const promise3 = Promise.resolve(3);
Promise.all([promise1, promise2, promise3])
.then(values => console.log(values)) // [1, 2, 3]
.catch(error => console.log(error));
// Promise.allSettled:等待所有Promise完成(无论成败),返回状态对象
const promise4 = Promise.reject('出错了');
Promise.allSettled([promise1, promise4])
.then(results => console.log(results))
/* [
{ status: 'fulfilled', value: 1 },
{ status: 'rejected', reason: '出错了' }
] */
// Promise.race:返回第一个完成的Promise结果(无论成败)
const promise5 = new Promise(resolve => setTimeout(resolve, 100, '快的'));
const promise6 = new Promise(resolve => setTimeout(resolve, 200, '慢的'));
Promise.race([promise5, promise6])
.then(value => console.log(value)) // '快的'
// Promise.any:返回第一个成功的Promise结果,全部失败才 reject
const promise7 = Promise.reject('失败1');
const promise8 = Promise.reject('失败2');
const promise9 = Promise.resolve('成功');
Promise.any([promise7, promise8, promise9])
.then(value => console.log(value)) // '成功'
.catch(err => console.log(err.errors)); // ['失败1', '失败2']
async/await语法糖
ES2017引入的async/await语法使异步代码更接近同步代码的可读性:
// 基本用法
const fetchData = async () => {
try {
const response = await fetch('https://api.example.com/data');
const data = await response.json();
return data;
} catch (error) {
console.error('数据获取失败:', error);
throw error; // 允许上层处理错误
}
};
// 并发执行多个异步操作
const fetchMultipleData = async () => {
const promise1 = fetch('https://api.example.com/data1');
const promise2 = fetch('https://api.example.com/data2');
// 同时等待两个Promise
const [response1, response2] = await Promise.all([promise1, promise2]);
const data1 = await response1.json();
const data2 = await response2.json();
return { data1, data2 };
};
// 带超时控制的异步操作
const fetchWithTimeout = async (url, timeout = 5000) => {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), timeout);
try {
const response = await fetch(url, { signal: controller.signal });
clearTimeout(timeoutId);
return await response.json();
} catch (error) {
if (error.name === 'AbortError') {
throw new Error(`请求超时(${timeout}ms)`);
}
throw error;
}
};
项目使用指南
快速开始
# 克隆仓库
git clone https://gitcode.com/gh_mirrors/na/namaste-javascript-notes.git
cd namaste-javascript-notes
# 生成PDF版本笔记
node generatePdf.js
# PDF文件将生成在dist/namaste-javascript-notes.pdf
最佳学习路径
根据项目内容,建议按以下顺序学习:
常见问题解决
-
PDF生成失败:确保已安装Node.js(v14+)和必要依赖,运行
npm install安装puppeteer等依赖包。 -
代码示例无法运行:部分示例依赖浏览器环境,建议在Chrome开发者工具的Console面板中执行。
-
章节内容不理解:每个章节末尾提供了YouTube视频链接,可结合视频讲解加深理解。
总结与展望
Namaste JavaScript笔记项目为开发者提供了一个系统、深入学习JavaScript的优质资源。通过本文介绍的闭包、事件循环和Promise三大核心概念,你已经掌握了现代JavaScript开发的关键知识。项目持续更新中,未来可能会增加TypeScript整合、框架应用等高级主题。
下一步学习建议
-
实践项目:使用所学知识实现一个小型应用,如待办事项管理器或数据可视化工具。
-
源码阅读:阅读知名JavaScript库(如lodash、axios)的源码,学习实际应用中的设计模式。
-
性能优化:深入学习JavaScript性能优化技巧,如代码分割、懒加载和内存管理。
资源获取
- 项目仓库:https://gitcode.com/gh_mirrors/na/namaste-javascript-notes
- 在线阅读:https://alok722.github.io/namaste-javascript-notes/dist/lectures.html
- 视频课程:YouTube搜索"Namaste JavaScript by Akshay Saini"
如果你觉得本项目对你有帮助,请给仓库点一个星标⭐,关注作者Alok Raj获取更新。有任何问题或建议,欢迎通过GitHub Issues参与讨论。
下一篇预告:《2025前端面试必备:JavaScript手写代码大全》—— 包含50+高频面试题的手写实现与详细解析。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



