h5打开以查看

1. 闭包 (Closure)

一句话概括: 闭包是一个函数与其被创建时的词法作用域(lexical scope)的组合。

深入理解:

  • 词法作用域(静态作用域):函数的作用域在函数定义的时候就已经决定了,而不是在执行的时候。它取决于代码书写的位置。

  • 机制:当一个内部函数引用了其外部函数作用域中的变量时,即使外部函数已经执行完毕并返回,JavaScript 的垃圾回收机制也不会回收外部函数的作用域链。这个内部函数依然持有对外部作用域的引用,这就是闭包。

例子与作用:

javascript

function createCounter() {
  let count = 0; // 外部作用域的变量

  return function() {
    count++; // 内部函数引用了外部变量
    return count;
  };
}

const myCounter = createCounter();
console.log(myCounter()); // 1
console.log(myCounter()); // 2
// 尽管 createCounter() 已执行完,但 myCounter 仍能访问和修改 count
  • 数据私有化:如上例,count 变量对外是完全隐藏的,只能通过 myCounter 这个函数来操作,实现了类似“私有变量”的功能。

  • 回调函数与异步编程:在事件监听、setTimeout、Ajax 请求等场景中,回调函数常常形成闭包,记住了它被定义时的环境。

  • 函数工厂:像 createCounter 这样的函数可以生成多个独立的计数器,每个都有自己独立的 count 变量。

常见误区:在循环中使用闭包,如果不正确使用 let (块级作用域) 或立即执行函数(IIFE),会导致所有闭包都引用循环变量的最终值。


2. 原型链 (Prototype Chain)

一句话概括: JavaScript 实现继承的主要机制,每个对象都有一个指向其原型的内部链接,形成一条链,用于属性和方法的查找。

深入理解:

  • __proto__ (隐式原型):每个对象都有一个 __proto__ 属性,指向它的构造函数的 prototype 对象。这是一个访问器属性(现代代码中更推荐使用 Object.getPrototypeOf(obj))。

  • prototype (显式原型):只有函数才有 prototype 属性。当你使用 new 关键字调用函数时,新创建对象的 __proto__ 就会指向该函数的 prototype

  • 查找机制:当你访问一个对象的属性时,JS 引擎会先在对象自身查找。如果没找到,就通过 __proto__ 去它的原型上找,再没找到就去原型的原型上找,直到找到 null(原型链的尽头)为止。这就是原型链。

例子与图示:

javascript

function Person(name) {
  this.name = name;
}
Person.prototype.sayHello = function() { // 方法定义在原型上
  console.log(`Hello, I'm ${this.name}`);
};

const alice = new Person('Alice');
alice.sayHello(); // 1. alice 自身没有 sayHello
                 // 2. 查找 alice.__proto__ (即 Person.prototype)
                 // 3. 找到了,调用

console.log(alice.__proto__ === Person.prototype); // true
console.log(Person.prototype.__proto__ === Object.prototype); // true
console.log(Object.prototype.__proto__); // null

作用:

  • 实现继承:子构造函数的 prototype 指向父构造函数的一个实例,从而实现方法和属性的共享与继承。

  • 共享方法:将方法定义在 prototype 上,而不是构造函数内部。这样所有实例共享同一个方法,极大节省了内存。定义在构造函数内部的属性则是每个实例独有的。

现代语法:ES6 的 class 关键字本质上是原型继承的语法糖,它让继承的写法更清晰、更接近于传统面向对象语言,但底层依然是原型链。


3. 事件循环 (Event Loop)

一句话概括: JavaScript 管理异步回调的机制,它决定了代码的执行顺序,是实现“非阻塞”行为的关键。

深入理解:
JavaScript 是单线程的,意味着它一次只能做一件事。事件循环机制让它能够处理高并发操作而不阻塞。

核心组件:

  1. 调用栈 (Call Stack):执行同步代码的地方,函数调用会形成一个栈帧。

  2. Web APIs (在浏览器中) / C++ APIs (在 Node.js 中):提供异步功能(如 setTimeoutfs.readFilefetch)。当调用这些 API 时,会将其回调函数和相应任务交给它们处理,调用栈继续执行后续代码。

  3. 任务队列 (Callback Queue / Task Queue):Web/C++ API 处理完任务后,会将对应的回调函数放入任务队列中等待

  4. 微任务队列 (Microtask Queue):一个优先级更高的队列,用于存放 Promise.then/catch/finally 的回调和 MutationObserver 等。

  5. 事件循环 (Event Loop):它像一个永恒的循环,不断地做一件事:

    • 检查调用栈是否为空

    • 如果调用栈为空,它先检查微任务队列,将其中的所有任务全部执行完毕(清空)。

    • 然后,从任务队列中取出一个任务(如 setTimeout 回调)放入调用栈执行。

    • 重复此过程。

例子与顺序:

javascript

console.log('1. Start');

setTimeout(() => console.log('2. Timeout'), 0);

Promise.resolve()
  .then(() => console.log('3. Promise'));

console.log('4. End');

// 输出顺序:
// 1. Start
// 4. End
// 3. Promise  (微任务优先)
// 2. Timeout  (宏任务后)

为什么重要? 在 Node.js 中,正是这个机制使得单线程能够高效处理成千上万的网络请求、文件操作等 I/O 密集型任务,而不会因为等待响应而卡住。


4. Promise

一句话概括: Promise 是一个对象,它代表了一个异步操作的最终完成(或失败) 及其结果值。它是管理异步回调的现代化解决方案。

深入理解:

  • 三种状态

    • pending(进行中):初始状态。

    • fulfilled(已成功):操作成功完成。

    • rejected(已失败):操作失败。
      状态一旦改变就不可逆(从 pending 变为 fulfilled 或 rejected)。

  • 解决回调地狱:Promise 的 .then() 和 .catch() 方法允许我们进行链式调用(Chaining),将纵向增长的嵌套回调变成了横向增长的扁平代码,大大提高了可读性和可维护性。

  • 错误冒泡:链中的任何一个环节出错,错误都会一直向后传递,直到被最近的 .catch() 捕获。这提供了比回调函数更强大的错误处理能力。

  • 与事件循环的关系:Promise 的回调(.then.catch.finally)是微任务,因此它们比 setTimeout 等宏任务具有更高的优先级。

例子:

javascript

// 模拟一个异步操作
function asyncOperation(data) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (Math.random() > 0.3) {
        resolve(`Success! Data: ${data}`); // 成功,改变状态为 fulfilled
      } else {
        reject(new Error('Failed!')); // 失败,改变状态为 rejected
      }
    }, 1000);
  });
}

// 使用 Promise 链式调用
asyncOperation('Hello')
  .then(result => {
    console.log(result);
    return asyncOperation('World'); // 返回一个新的 Promise,继续链式调用
  })
  .then(anotherResult => {
    console.log(anotherResult);
  })
  .catch(error => { // 捕获链中任何位置的错误
    console.error('An error occurred:', error);
  });

async/await:ES8 的 async/await 是建立在 Promise 之上的语法糖,它让你能用写同步代码的方式去写异步代码,进一步提升了可读性。await 会“暂停” async 函数的执行,等待 Promise 的解决。

总结与联系

  • 闭包 让你能在异步回调中记住并访问定义时的上下文。

  • 事件循环 是 JavaScript 处理异步的核心机制,它管理着调用栈和任务队列。

  • Promise 是管理异步操作的现代化工具,它利用事件循环的微任务队列提供了更优雅的链式调用和错误处理。

  • 原型链 是 JavaScript 对象系统的基础构建模式,与异步特性关系稍远,但决定了语言如何组织代码和实现复用。

h5打开以查看

更多推荐