2025最全JavaScript深度指南:从闭包到Promise的实战笔记

2025最全JavaScript深度指南:从闭包到Promise的实战笔记

【免费下载链接】namaste-javascript-notes It maintains my version of namaste js notes which I learnt from Namaste JS YouTube Playlist by Akshay Saini (@akshaymarch7). 【免费下载链接】namaste-javascript-notes 项目地址: https://gitcode.com/gh_mirrors/na/namaste-javascript-notes

你是否还在为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(真正的私有变量)
闭包的五大实战应用
  1. 模块设计模式:实现真正的私有变量,避免全局污染

    const shoppingCart = (function() {
      const items = []; // 私有数组
    
      return {
        addItem: (product) => items.push(product),
        getTotal: () => items.reduce((sum, item) => sum + item.price, 0),
        getItems: () => [...items] // 返回副本,防止外部修改
      };
    })();
    
  2. 函数柯里化:将多参数函数转换为单参数函数序列

    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
    
  3. 记忆化(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);
    });
    
  4. 防抖与节流:控制高频事件触发频率

    // 防抖:触发后延迟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;
        }
      };
    };
    
  5. 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)。

执行模型图解

mermaid

任务优先级排序

JavaScript中任务执行顺序遵循以下优先级规则(从高到低):

  1. 同步代码:调用栈中直接执行的代码
  2. 微任务(Microtasks)
    • Promise.then/catch/finally
    • MutationObserver
    • queueMicrotask()
  3. 宏任务(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.jsnextTick队列(优先级更高)+ 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

最佳学习路径

根据项目内容,建议按以下顺序学习:

mermaid

常见问题解决

  1. PDF生成失败:确保已安装Node.js(v14+)和必要依赖,运行npm install安装puppeteer等依赖包。

  2. 代码示例无法运行:部分示例依赖浏览器环境,建议在Chrome开发者工具的Console面板中执行。

  3. 章节内容不理解:每个章节末尾提供了YouTube视频链接,可结合视频讲解加深理解。

总结与展望

Namaste JavaScript笔记项目为开发者提供了一个系统、深入学习JavaScript的优质资源。通过本文介绍的闭包、事件循环和Promise三大核心概念,你已经掌握了现代JavaScript开发的关键知识。项目持续更新中,未来可能会增加TypeScript整合、框架应用等高级主题。

下一步学习建议

  1. 实践项目:使用所学知识实现一个小型应用,如待办事项管理器或数据可视化工具。

  2. 源码阅读:阅读知名JavaScript库(如lodash、axios)的源码,学习实际应用中的设计模式。

  3. 性能优化:深入学习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+高频面试题的手写实现与详细解析。

【免费下载链接】namaste-javascript-notes It maintains my version of namaste js notes which I learnt from Namaste JS YouTube Playlist by Akshay Saini (@akshaymarch7). 【免费下载链接】namaste-javascript-notes 项目地址: https://gitcode.com/gh_mirrors/na/namaste-javascript-notes

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值