1. 事件循环(Event Loop)
事件循环是 JavaScript 的核心执行机制之一,管理着任务的调度,尤其是处理异步任务。
- 宏任务(Macrotask) :包括
setTimeout、setInterval、I/O操作等,它们被放入宏任务队列,等待主线程空闲时执行。 - 微任务(Microtask) :包括
Promise.then、MutationObserver、process.nextTick(Node.js)等,它们的优先级高于宏任务,会在当前事件循环结束前立即执行。
示例:
console.log('Start');
setTimeout(() => {
console.log('setTimeout');
}, 0);
Promise.resolve().then(() => {
console.log('Promise');
});
console.log('End');
执行顺序:
console.log('Start')和console.log('End')是同步任务,立即执行。Promise的回调属于微任务,会在事件循环的本轮结束前执行。setTimeout是宏任务,在下一个事件循环中执行。
2. 单线程机制
JavaScript 的单线程机制决定了它一次只能执行一个任务,所有代码共享一个执行上下文。这种设计虽然简化了开发,避免了多线程中的资源争抢、竞态条件等问题,但在处理复杂任务时,可能会出现阻塞,降低性能。
为了应对单线程的缺点,JavaScript 使用异步机制和事件循环来处理任务,从而提高应用的性能。
3. 任务队列(Task Queue)
JavaScript 中的任务队列用于管理等待执行的异步任务。任务分为宏任务队列和微任务队列:
- 宏任务队列:存放
setTimeout、setInterval、I/O 操作等任务。 - 微任务队列:存放
Promise.then、MutationObserver、process.nextTick(Node.js)等任务。
在每个事件循环的执行中,JavaScript 引擎会优先执行微任务队列中的任务,确保这些任务在宏任务之前执行。
4. 闭包(Closure)
闭包是 JavaScript 中的一种执行机制,它允许内部函数访问外部函数的变量。即使外部函数已经执行完毕,它的作用域仍然被保留在内存中,供内部函数访问。
示例:
function outer() {
let counter = 0;
return function inner() {
counter++;
console.log(counter);
};
}
const increment = outer();
increment(); // 输出 1
increment(); // 输出 2
闭包的作用包括:
- 数据私有化:通过闭包,变量可以在函数内部“私有化”,外部无法直接访问。
- 记忆功能:闭包可以保持函数执行上下文,记住外部函数中的状态。
5. 原型链机制(Prototype Chain)
JavaScript 中的原型链是实现继承的一种机制。每个对象都有一个内部属性 [[Prototype]],它指向另一个对象(即原型对象)。当访问一个对象的属性时,JavaScript 引擎会先查找该对象本身的属性,如果找不到,则沿着原型链向上查找,直到找到该属性或到达 null(表示原型链的终点)。
示例:
function Person(name) {
this.name = name;
}
Person.prototype.greet = function() {
console.log(`Hello, I'm ${this.name}`);
};
const john = new Person('John');
john.greet(); // 输出:Hello, I'm John
在这个例子中,john 对象继承了 Person.prototype 上的 greet 方法,这是通过原型链实现的。
6. 作用域链(Scope Chain)
JavaScript 的作用域链是指当前执行上下文中变量的查找机制。JavaScript 中的作用域分为全局作用域和局部作用域,作用域链是通过函数的嵌套关系形成的。当查找变量时,JavaScript 会按照作用域链的顺序查找变量,先从当前作用域开始,逐级向上查找,直到找到变量或到达全局作用域。
示例:
let a = 10;
function outer() {
let b = 20;
function inner() {
let c = 30;
console.log(a, b, c); // 输出 10, 20, 30
}
inner();
}
outer();
在这个例子中,inner 函数可以访问到外部作用域的 a 和 b 变量,这是通过作用域链实现的。
7. 执行上下文(Execution Context)
执行上下文是 JavaScript 代码在执行时的环境。每当 JavaScript 代码被执行时,它会创建一个执行上下文。执行上下文包含了变量、函数、对象等信息。JavaScript 在代码执行过程中,依次创建全局执行上下文、函数执行上下文等。
- 全局执行上下文:在代码加载时被创建,包含全局变量和函数。
- 函数执行上下文:在函数被调用时被创建,包含函数内部的变量和参数。
- 块级作用域执行上下文:在
let和const声明的块作用域内创建。
8. 垃圾回收机制(Garbage Collection)
JavaScript 的垃圾回收机制(Garbage Collection, GC) 是一种自动内存管理机制。JavaScript 引擎通过标记-清除算法来追踪哪些对象和变量仍然在使用,哪些已经不再使用,并释放那些不再使用的内存空间。
- 标记-清除:在运行过程中,JavaScript 会标记所有当前在使用的对象,当某些对象不再被引用时,它们会被标记为可回收,最后垃圾回收器会清除这些对象。
相关机制:
- 引用计数:一个对象的引用次数达到 0 时,它会被回收。
- 标记阶段:从根对象(如全局对象)出发,遍历所有引用,标记可到达的对象。
- 清除阶段:释放未被标记的对象所占用的内存。
9. 非阻塞 I/O 机制
在 Node.js 环境中,非阻塞 I/O 是一个重要的执行机制。Node.js 使用了事件驱动的非阻塞 I/O 模型,使得它在处理文件读写、网络请求等操作时,可以继续执行其他任务,而不会因为 I/O 操作的等待时间而阻塞线程。
示例:
const fs = require('fs');
fs.readFile('file.txt', 'utf8', (err, data) => {
if (err) throw err;
console.log(data); // 文件读取完成后执行
});
console.log('This line executes immediately');
在这个例子中,文件读取是异步的,Node.js 会继续执行其他任务,而不会等待文件读取完成。
总结
- 事件循环(Event Loop) :管理异步任务的执行顺序,协调宏任务与微任务的执行。
- 单线程机制:确保 JavaScript 一次只能执行一个任务,避免多线程并发带来的问题。
- 任务队列(Task Queue) :管理异步任务,分为宏任务和微任务队列。
- 闭包(Closure) :通过内部函数访问外部函数的变量,形成数据私有化机制。
- 原型链机制(Prototype Chain) :实现对象继承的机制。
- 作用域链(Scope Chain) :处理变量查找的机制,从当前作用域向外部作用域逐级查找。
- 执行上下文(Execution Context) :代码执行时的环境,包括全局执行上下文、函数执行上下文等。
- 垃圾回收机制(Garbage Collection) :自动内存管理,回收不再使用的对象。
- 非阻塞 I/O 机制:Node.js 中的事件驱动机制,使得 I/O 操作不会阻塞主线程。
这些机制共同构成了 JavaScript 的核心执行模型,帮助开发者处理不同类型的任务。