WEB3全栈开发——面试专业技能点P3JavaScript / TypeScript

目录

[一、ES6+ 语法](#一、ES6+ 语法)

[1. let 和 const 变量声明](#1. let 和 const 变量声明)

[2. 箭头函数 () => {}](#2. 箭头函数 () => {})

[3. 模板字符串](#3. 模板字符串)

[4. 解构赋值](#4. 解构赋值)

[5. 默认参数](#5. 默认参数)

[6. 展开运算符 ...](#6. 展开运算符 ...)

[7. Promise 和 async/await 异步处理](#7. Promise 和 async/await 异步处理)

[8. 类(class)和继承](#8. 类(class)和继承)

[9. 模块导入导出 import 和 export](#9. 模块导入导出 import 和 export)

[10. Symbol 类型](#10. Symbol 类型)

[11. Map 和 Set 数据结构](#11. Map 和 Set 数据结构)

[12. 生成器函数 function*](#12. 生成器函数 function*)

[13. 可选链操作符 ?.](#13. 可选链操作符 ?.)

[14. 空值合并运算符 ??](#14. 空值合并运算符 ??)

二、Javascript闭包

解释:

三、原型链

[1.javascript原型链(经典函数构造器 + prototype)](#1.javascript原型链(经典函数构造器 + prototype))

解释:

[2.TypeScript 版本(class 语法)](#2.TypeScript 版本(class 语法))

解释:

3.总结

三、作用域链

作用域链简介

[1. JavaScript 版本示例](#1. JavaScript 版本示例)

解释:

[2. TypeScript 版本示例](#2. TypeScript 版本示例)

解释:

总结

[四、如何使用 TypeScript 构建类型安全的 DApp 项目](#四、如何使用 TypeScript 构建类型安全的 DApp 项目)

[1. 使用 TypeScript 定义智能合约接口类型](#1. 使用 TypeScript 定义智能合约接口类型)

[2. 使用 web3.js 或 ethers.js 连接以太坊并调用合约(示例用 ethers.js)](#2. 使用 web3.js 或 ethers.js 连接以太坊并调用合约(示例用 ethers.js))

[3. 使用 TypeScript 定义事件类型并监听合约事件](#3. 使用 TypeScript 定义事件类型并监听合约事件)

[4. 使用类型安全的 ABI 绑定工具(如 TypeChain)](#4. 使用类型安全的 ABI 绑定工具(如 TypeChain))

[5. 总结](#5. 总结)

五、泛型

概念

[1. 泛型函数示例](#1. 泛型函数示例)

解释:

[2. 泛型接口示例](#2. 泛型接口示例)

解释:

[3. 泛型类示例](#3. 泛型类示例)

解释:

[4. 泛型约束示例](#4. 泛型约束示例)

解释:

六、接口

概念

[1. TypeScript 接口示例](#1. TypeScript 接口示例)

解释:

[2. JavaScript 中模拟接口(无类型检查)](#2. JavaScript 中模拟接口(无类型检查))

解释:

应用场景

七、装饰器

[📌 一、装饰器是什么?](#📌 一、装饰器是什么?)

[🧪 二、类装饰器示例](#🧪 二、类装饰器示例)

[✅ 解释:](#✅ 解释:)

[📦 三、方法装饰器示例](#📦 三、方法装饰器示例)

[✅ 解释:](#✅ 解释:)

[🏷️ 四、属性装饰器示例](#🏷️ 四、属性装饰器示例)

[✅ 解释:](#✅ 解释:)

[📌 五、装饰器的应用场景](#📌 五、装饰器的应用场景)


一、ES6+ 语法

"ES6+ 语法"指的是 ECMAScript 2015(即 ES6)及其之后版本的 JavaScript 语言新特性和语法。简单说,就是现代 JavaScript 的新语法和功能。

常见 ES6+ 语法包括:

  • letconst 变量声明

  • 箭头函数 () => {}

  • 模板字符串 Hello ${name}

  • 解构赋值

  • 默认参数

  • 展开运算符 ...

  • Promise 和 async/await 异步处理

  • 类(class)和继承

  • 模块导入导出 importexport

  • Symbol 类型

  • Map 和 Set 数据结构

  • 生成器函数 function*

  • 可选链操作符 ?.

  • 空值合并运算符 ??

1. letconst 变量声明

复制代码
let a = 10;    // 可变变量
const b = 20;  // 常量,不能重新赋值
// b = 30; // 会报错

解释: let 声明的变量有块级作用域,const 声明常量,值不能变。


2. 箭头函数 () => {}

复制代码
const add = (x, y) => x + y;
console.log(add(2, 3)); // 5

解释: 箭头函数写法简洁,并且不绑定自己的 this


3. 模板字符串

复制代码
const name = 'Alice';
console.log(`Hello, ${name}!`);  // Hello, Alice!

解释: 用反引号 ````` 包裹,可以直接嵌入变量和表达式。


4. 解构赋值

复制代码
const person = {name: 'Bob', age: 25};
const {name, age} = person;
console.log(name, age); // Bob 25

const arr = [1, 2, 3];
const [first, second] = arr;
console.log(first, second); // 1 2

解释: 从对象或数组中快速提取值赋给变量。


5. 默认参数

复制代码
function greet(name = 'Guest') {
  console.log(`Hello, ${name}`);
}
greet();          // Hello, Guest
greet('Alice');   // Hello, Alice

解释: 函数参数可以设置默认值。


6. 展开运算符 ...

复制代码
const arr1 = [1, 2];
const arr2 = [...arr1, 3, 4];
console.log(arr2); // [1, 2, 3, 4]

const obj1 = {a: 1, b: 2};
const obj2 = {...obj1, c: 3};
console.log(obj2); // {a:1, b:2, c:3}

解释: 展开数组或对象,合并或复制。


7. Promise 和 async/await 异步处理

复制代码
// Promise
function fetchData() {
  return new Promise(resolve => {
    setTimeout(() => resolve('data'), 1000);
  });
}

fetchData().then(data => console.log(data));  // data

// async/await
async function asyncFetch() {
  const data = await fetchData();
  console.log(data);
}
asyncFetch();  // data

解释: 用 Promise 处理异步,async/await 语法更简洁。


8. 类(class)和继承

复制代码
class Animal {
  constructor(name) {
    this.name = name;
  }
  speak() {
    console.log(`${this.name} makes a noise.`);
  }
}

class Dog extends Animal {
  speak() {
    console.log(`${this.name} barks.`);
  }
}

const d = new Dog('Rex');
d.speak();  // Rex barks.

解释: ES6 引入类语法,更接近传统面向对象。


9. 模块导入导出 importexport

复制代码
// utils.js
export function sum(x, y) {
  return x + y;
}

// main.js
import {sum} from './utils.js';
console.log(sum(2, 3));  // 5

解释: 支持模块化开发,导入导出代码片段。


10. Symbol 类型

复制代码
const sym = Symbol('desc');
const obj = {};
obj[sym] = 'value';
console.log(obj[sym]);  // value

解释: Symbol 是一种独一无二的标识符,常用作对象属性键,避免命名冲突。


11. Map 和 Set 数据结构

复制代码
const map = new Map();
map.set('a', 1);
console.log(map.get('a')); // 1

const set = new Set([1, 2, 2, 3]);
console.log(set); // Set {1, 2, 3}

解释: Map 是键值对集合,Set 是无重复值的集合。


12. 生成器函数 function*

复制代码
function* gen() {
  yield 1;
  yield 2;
  yield 3;
}

const g = gen();
console.log(g.next().value); // 1
console.log(g.next().value); // 2

解释: 生成器可暂停执行,逐步产出值。


13. 可选链操作符 ?.

复制代码
const obj = {a: {b: 10}};
console.log(obj.a?.b);     // 10
console.log(obj.x?.b);     // undefined 不报错

解释: 访问嵌套属性时安全,不会因中间值为 null 或 undefined 报错。


14. 空值合并运算符 ??

复制代码
const foo = null ?? 'default';
console.log(foo);  // default

const bar = 0 ?? 42;
console.log(bar);  // 0

解释: 当左侧是 null 或 undefined 时,返回右侧值。

二、Javascript闭包

闭包(Closure)示例

复制代码
function outer() {
  let count = 0;
  return function inner() {
    count++;
    console.log(count);
  }
}

const counter = outer();
counter();  // 1
counter();  // 2
counter();  // 3

解释:

  • 函数 outer 返回了一个内部函数 inner

  • inner 函数可以访问 outer 的变量 count,即使 outer 已经执行完毕。

  • 这种函数和其访问的变量环境形成的组合,就叫闭包。

  • 闭包常用来实现私有变量和数据封装。

三、原型链

1.javascript原型链(经典函数构造器 + prototype)

复制代码
function Person(name) {
  this.name = name;
}

Person.prototype.sayHello = function() {
  console.log(`Hello, my name is ${this.name}`);
};

const alice = new Person('Alice');
alice.sayHello();  // Hello, my name is Alice

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

解释:

  • 每个对象都有一个内部属性 [[Prototype]](常用 __proto__ 访问),指向它的原型对象。

  • 当访问对象的属性或方法时,如果自身没有,会沿着 [[Prototype]] 一层层往上找,这个查找链就是原型链

  • 上面例子中,alice 访问 sayHello 方法时没在自身属性里找到,就去它的原型对象 Person.prototype 查找。

  • Person.prototype 的原型是 Object.prototype,这构成了原型链的多层关系。

  • Object.prototype 的原型是 null,链条终点。

2.TypeScript 版本(class 语法)

复制代码
class Person {
  name: string;

  constructor(name: string) {
    this.name = name;
  }

  sayHello() {
    console.log(`Hello, my name is ${this.name}`);
  }
}

const alice = new Person('Alice');
alice.sayHello();  // Hello, my name is Alice

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

解释:

  • TypeScript 使用 class 关键字声明类,语法更现代,代码更清晰。

  • sayHello 是类的方法,实际挂载在 Person.prototype 上,实例通过原型链访问。

  • Object.getPrototypeOf() 用来获取对象的原型,效果和 __proto__ 类似,但更标准安全。

  • 原型链关系和JavaScript版本完全一样:实例原型 → 类的 prototypeObject.prototypenull

3.总结

  • JavaScript版本用构造函数和显式原型,适合传统理解原型链机制。

  • TypeScript版本用类语法,更符合现代代码风格,本质上还是基于JavaScript原型链实现。

三、作用域链

作用域链简介

  • 作用域链是当访问一个变量时,JavaScript 引擎从当前作用域开始,逐级向上查找变量的过程。

  • 这保证了内层函数可以访问外层函数的变量。

  • 作用域链和原型链不同,作用域链是关于变量查找的执行环境机制。


1. JavaScript 版本示例

复制代码
function outer() {
  const a = 10;

  function inner() {
    const b = 20;
    console.log(a + b); // 30
  }

  inner();
}

outer();

解释:

  • inner 函数内部访问变量 a,它不在自身作用域中。

  • 于是它查找外层作用域 outer,找到了变量 a,然后计算并输出结果。

  • 这就是作用域链:innerouter → 全局。


2. TypeScript 版本示例

TypeScript 的作用域链机制和 JavaScript 一样:

复制代码
function outer() {
  const a: number = 10;

  function inner() {
    const b: number = 20;
    console.log(a + b); // 30
  }

  inner();
}

outer();

解释:

  • 语法和JavaScript几乎一致,只是变量类型显式声明了。

  • 作用域链机制完全相同,内层函数能访问外层函数的变量。


总结

  • 作用域链保证了变量从内层到外层逐级查找。

  • 它是运行时上下文环境的一部分,和闭包密切相关。

  • JavaScript 和 TypeScript 的作用域链规则一致,TS 只是加了类型。

四、如何使用 TypeScript 构建类型安全的 DApp 项目

下面给你关于"使用 TypeScript 构建类型安全的 DApp(去中心化应用)项目"的几个核心知识点,附带简短代码示例和解释。


1. 使用 TypeScript 定义智能合约接口类型

复制代码
// 定义智能合约函数的接口
interface MyContract {
  methods: {
    balanceOf(address: string): { call(): Promise<string> };
    transfer(to: string, amount: string): { send(): Promise<void> };
  };
}

解释:

通过接口定义智能合约方法,确保调用时参数类型和返回类型明确,减少错误。


2. 使用 web3.js 或 ethers.js 连接以太坊并调用合约(示例用 ethers.js)

复制代码
import { ethers } from 'ethers';

async function getBalance(contract: MyContract, address: string): Promise<string> {
  const balance = await contract.methods.balanceOf(address).call();
  return balance;
}

解释:

函数参数用类型接口约束 contract,保证传入合约实例符合预期方法,address 是字符串。


3. 使用 TypeScript 定义事件类型并监听合约事件

复制代码
interface TransferEvent {
  from: string;
  to: string;
  value: string;
}

contract.on('Transfer', (from: string, to: string, value: string) => {
  const event: TransferEvent = { from, to, value };
  console.log('Transfer event:', event);
});

解释:

定义事件结构接口,事件监听回调参数用类型标注,方便后续类型检查和自动补全。


4. 使用类型安全的 ABI 绑定工具(如 TypeChain)

复制代码
// 生成的合约类型(伪代码)
import { MyContract } from './types';

const contract: MyContract = getContractInstance();

const result = await contract.balanceOf('0x123...');

解释:

TypeChain 等工具根据合约 ABI 自动生成 TypeScript 类型,调用合约更安全,减少运行时错误。


5. 总结

  • 用 TypeScript 接口和类型定义合约方法和事件,保证调用和监听的类型安全。

  • 使用 ethers.js 或 web3.js 结合类型定义调用智能合约。

  • 通过 TypeChain 等工具生成合约类型代码,减少手写错误。

五、泛型

概念

泛型可以让函数、类或接口在使用时指定类型,而不是在定义时就固定死,提高了代码复用性和类型安全。

JavaScript 本身没有泛型,泛型是 TypeScript(以及其他静态类型语言)提供的类型系统特性,用来增强代码的类型安全和复用性。

简单来说:

  • JavaScript 是动态类型语言,变量和函数的参数类型在运行时确定,没有静态类型检查。

  • TypeScript 在 JavaScript 基础上加了类型系统,其中就包括泛型,可以在编译阶段帮你检查类型,避免运行时错误。

所以,泛型是 TypeScript 的特色,JavaScript 没有对应的语法和概念。

1. 泛型函数示例

复制代码
function identity<T>(arg: T): T {
  return arg;
}

const str = identity<string>('hello'); // str 类型是 string
const num = identity<number>(123);     // num 类型是 number

解释:

  • identity 是一个泛型函数,<T> 是类型参数,代表调用时传入的具体类型。

  • 传入参数和返回值类型都与 T 一致,调用时指定类型,保证类型安全且复用性强。


2. 泛型接口示例

复制代码
interface Box<T> {
  value: T;
}

const box1: Box<string> = { value: 'hello' };
const box2: Box<number> = { value: 100 };

解释:

  • Box 是一个泛型接口,成员 value 的类型由外部指定。

  • 方便用同一个接口定义不同类型的对象。


3. 泛型类示例

复制代码
class Stack<T> {
  private items: T[] = [];

  push(item: T) {
    this.items.push(item);
  }

  pop(): T | undefined {
    return this.items.pop();
  }
}

const stack = new Stack<number>();
stack.push(10);
console.log(stack.pop()); // 10

解释:

  • 泛型类 Stack 支持存放任何类型的元素,且保证类型一致性。

  • 实例化时指定具体类型,保证操作时类型安全。


4. 泛型约束示例

复制代码
interface Lengthwise {
  length: number;
}

function loggingIdentity<T extends Lengthwise>(arg: T): T {
  console.log(arg.length);
  return arg;
}

loggingIdentity('hello'); // 输出 5
loggingIdentity([1, 2, 3]); // 输出 3
// loggingIdentity(123); // 报错,number 没有 length 属性

解释:

  • 泛型约束保证传入的类型必须有 length 属性。

  • 防止传入不符合约束的类型。

六、接口

概念

接口用于定义对象的结构,让代码更规范,可扩展性更好。

1. TypeScript 接口示例

复制代码
interface User {
  id: number;
  name: string;
  email?: string; // 可选属性
}

function greet(user: User) {
  console.log(`Hello, ${user.name}`);
}

const user1 = { id: 1, name: 'Alice' };
greet(user1);

解释:

  • interface User 定义了一个结构类型,规定对象必须有 idnameemail 可选。

  • 函数 greet 形参要求是 User 类型,确保传入对象符合接口。


2. JavaScript 中模拟接口(无类型检查)

JavaScript 没有接口,但你可以用约定或运行时检查实现类似效果:

复制代码
function greet(user) {
  if (typeof user.id !== 'number' || typeof user.name !== 'string') {
    throw new Error('Invalid user object');
  }
  console.log(`Hello, ${user.name}`);
}

const user1 = { id: 1, name: 'Alice' };
greet(user1);

解释:

  • 通过函数内部手动检查对象属性类型,保证参数符合预期结构。

  • 但没有编译时类型检查,容易出错且不够方便。


总结:

  • TypeScript接口:静态类型检查,定义对象结构,是编译时用来提升代码健壮性的工具。

  • JavaScript:无接口概念,只能靠编码习惯和运行时检测保证对象结构。

应用场景

  • API 数据模型定义

  • 前后端数据类型统一

  • 高度协作项目中约定字段结构

七、装饰器

下面是 TypeScript 中的 装饰器(Decorator) 的讲解、使用条件、代码示例和解释。


📌 一、装饰器是什么?

装饰器是对类、方法、属性或参数的增强,是元编程的一种形式。需要在 tsconfig.json 中开启 experimentalDecorators

装饰器是 一种特殊的语法 ,用于 修改类、类方法、属性或参数的行为。它本质上是一个函数。

装饰器是 TypeScript 的高级功能之一,需要在 tsconfig.json 中启用

复制代码
{
  "compilerOptions": {
    "experimentalDecorators": true
  }
}

🧪 二、类装饰器示例

复制代码
function Logger(constructor: Function) {
  console.log('Class decorated:', constructor.name);
}

@Logger
class User {
  constructor(public name: string) {}
}

✅ 解释:

  • @Logger 是一个类装饰器。

  • 它接收构造函数作为参数,在类定义时执行。

  • 装饰器不会改变类本身行为,但可以扩展、增强或者记录日志。


📦 三、方法装饰器示例

复制代码
function LogMethod(
  target: any,
  propertyKey: string,
  descriptor: PropertyDescriptor
) {
  const original = descriptor.value;
  descriptor.value = function (...args: any[]) {
    console.log(`Method ${propertyKey} called with`, args);
    return original.apply(this, args);
  };
}

class MathTool {
  @LogMethod
  add(a: number, b: number) {
    return a + b;
  }
}

const tool = new MathTool();
tool.add(2, 3); // 控制台打印日志

✅ 解释:

  • @LogMethod 修改 add 方法,使其在执行前打印参数。

  • 可以用于日志记录、性能分析、权限验证等。


🏷️ 四、属性装饰器示例

复制代码
function ReadOnly(target: any, propertyKey: string) {
  Object.defineProperty(target, propertyKey, {
    writable: false,
  });
}

class Person {
  @ReadOnly
  name = 'Alice';
}

const p = new Person();
// p.name = 'Bob'; // ❌ 会失败(只读)

✅ 解释:

  • @ReadOnly 将属性设置为只读。

📌 五、装饰器的应用场景

  • 日志打印(如方法调用参数)

  • 权限控制

  • 数据校验

  • 自动绑定(如 Vue、NestJS 中常见用法)

  • AOP(面向切面编程)

  • NestJS 控制器、服务模块增强

  • Web3 签名校验封装

八、Javascript事件循环-eventloop

下面我将全面地给你讲清 JavaScript 的事件循环(Event Loop)机制 ,包括浏览器环境下的执行流程,并结合同步/异步、宏任务/微任务、Promise/定时器等概念,做到既清晰又实用。


🧠 1、JavaScript 是单线程语言

JavaScript 引擎只有一个主线程,不能同时干多件事,所以就需要一种机制来有序执行异步任务,这就是:

事件循环(Event Loop)


🔁 2、事件循环的基本流程(浏览器中)

  1. 所有代码进入主线程执行(同步代码)。

  2. 异步任务(如 setTimeoutfetchPromise、事件监听)被分发到对应的任务队列中。

  3. 主线程空了以后,事件循环会按照先"微任务队列",再"宏任务队列"的顺序执行异步任务。

  4. 每执行完一轮宏任务(一次主线程执行 + 所有微任务清空),就称为一个 tick


📦 3、任务分类:宏任务 vs 微任务

分类 常见 API 示例 执行时机
宏任务 setTimeoutsetIntervalsetImmediaterequestAnimationFrame、I/O 每次 Event Loop 循环
微任务 Promise.thencatchfinallyMutationObserverqueueMicrotask 宏任务后立即执行

🧪 4、经典例子讲清执行顺序

复制代码
console.log('script start');

setTimeout(() => {
  console.log('setTimeout');
}, 0);

Promise.resolve().then(() => {
  console.log('promise1');
}).then(() => {
  console.log('promise2');
});

console.log('script end');

✅ 输出顺序:

复制代码
script start
script end
promise1
promise2
setTimeout

✅ 原因:

  1. console.log('script start') 是同步 → 立即执行

  2. setTimeout 是宏任务 → 放入任务队列,稍后执行

  3. Promise.then 是微任务 → 放入微任务队列

  4. console.log('script end') 是同步 → 立即执行

  5. 主线程空了 → 执行微任务队列(promise1, promise2

  6. 微任务清空 → 执行宏任务(setTimeout


📊 5、流程图(简化版)

复制代码
          ↓ 执行同步代码(主线程)
事件循环 → 执行所有微任务队列
          ↓
        渲染页面(可选)
          ↓
       执行一个宏任务
          ↓
      重复事件循环

🛠 6、延伸技巧

📌 queueMicrotask:手动添加微任务

复制代码
queueMicrotask(() => {
  console.log('microtask');
});

setTimeout(fn, 0) 还要早执行!


📌 await 本质上是微任务

复制代码
async function foo() {
  console.log('start');
  await null;
  console.log('end');
}
foo();
console.log('after');

输出:

复制代码
start
after
end

🤔 7、Node.js 的事件循环与浏览器略有不同

Node.js 的事件循环分了更多阶段(如 timers、I/O callbacks、check 等),如果你需要我可以单独讲。


如果你希望:

  • 通过图形化理解整个流程

  • 或想练习几道面试题

  • 或结合浏览器 DevTools 分析微任务执行

我也可以帮你准备,告诉我你的具体需求!

九、Javascript事件循环-宏任务-微任务

十、Javascript练习时间循环的网站推荐

十一、js面试题里的事件循环和nodejs事件循环是一个东西吗

Node.js 和 浏览器中 JavaScript 的事件循环机制看起来类似,但在底层实现任务处理模型方面存在关键区别。下面我们从结构图 + 概念 + 代码示例来系统讲解。


🧠 1️⃣ 概念区别:Node.js vs 浏览器事件循环

特性 浏览器(JS引擎) Node.js(基于 libuv)
宿主环境 浏览器(Chrome, Firefox, etc) Node.js(V8 + libuv)
宏任务队列 setTimeoutsetInterval、XHR 等 setTimeoutsetImmediateI/O 回调
微任务队列 Promise.thenMutationObserver Promise.thenprocess.nextTick
事件循环阶段数 简单两队列(宏任务 & 微任务) 6个阶段:timers → pending → idle → poll → check → close
特有机制 页面渲染、浏览器API、DOM 操作 文件系统、网络请求、TCP/UDP、子进程、线程池
微任务优先级 Promise 优先 process.nextTick > Promise.then
执行顺序可控性 宏/微任务顺序比较直观 顺序更复杂,受 libuv 控制

🖼️ 2️⃣ 图示对比

📌 浏览器事件循环流程:

复制代码
┌────────────────────┐
│     宏任务队列     │ ← setTimeout / setInterval / I/O
└─────────┬──────────┘
          ↓
     执行栈(call stack)
          ↑
┌─────────┴──────────┐
│     微任务队列     │ ← Promise.then / MutationObserver
└────────────────────┘

👉 微任务会在当前宏任务执行结束后、下一个宏任务开始前全部执行完。


📌 Node.js 事件循环 6 阶段(来自 libuv)

复制代码
┌────────────────────────────┐
│        timers 阶段         │ ← setTimeout、setInterval
├────────────────────────────┤
│      pending callbacks      │ ← I/O错误回调
├────────────────────────────┤
│     idle, prepare 阶段      │
├────────────────────────────┤
│         poll 阶段          │ ← 等待I/O事件、读取文件等
├────────────────────────────┤
│         check 阶段         │ ← setImmediate 回调
├────────────────────────────┤
│       close callbacks       │ ← 如 socket.on('close', ...)
└────────────────────────────┘

💡每个阶段执行后,都会检查并清空微任务队列nextTickPromise.then


🧪 3️⃣ 示例代码对比:Node.js 与 浏览器中事件顺序

Node.js 示例

复制代码
// node_event_loop.js
setTimeout(() => {
  console.log('setTimeout');
});

setImmediate(() => {
  console.log('setImmediate');
});

process.nextTick(() => {
  console.log('process.nextTick');
});

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

🧾 输出顺序通常为:

复制代码
process.nextTick
Promise.then
setTimeout
setImmediate

浏览器中的类似代码

复制代码
// browser_event_loop.html
setTimeout(() => {
  console.log('setTimeout');
});

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

queueMicrotask(() => {
  console.log('queueMicrotask');
});

🧾 浏览器输出顺序:

复制代码
Promise.then
queueMicrotask
setTimeout

📌 没有 process.nextTicksetImmediate,微任务只包括 PromisequeueMicrotask


📚 4️⃣ 总结对比重点

分类 浏览器 JS Node.js
微任务执行顺序 Promise.then process.nextTickPromise
独有机制 DOM、WebAPI setImmediate、libuv 阶段模型
事件循环控制结构 简单宏/微任务 6阶段循环,每阶段后执行微任务
定时器精度 稳定,受浏览器控制 若有 I/O 操作,setTimeout可能延迟
使用场景 前端交互 服务端、I/O密集、非阻塞操作

如你需要结合 Node.js 的 libuv 源码分析浏览器的 requestAnimationFrame 渲染机制对比,我也可以继续展开。是否要配合图形化可视化执行流程模拟工具?

十二、节流和防抖

相关推荐
前端大卫3 小时前
Vue3 + Element-Plus 自定义虚拟表格滚动实现方案【附源码】
前端
却尘4 小时前
Next.js 请求最佳实践 - vercel 2026一月发布指南
前端·react.js·next.js
ccnocare4 小时前
浅浅看一下设计模式
前端
Lee川4 小时前
🎬 从标签到屏幕:揭秘现代网页构建与适配之道
前端·面试
Ticnix4 小时前
ECharts初始化、销毁、resize 适配组件封装(含完整封装代码)
前端·echarts
纯爱掌门人4 小时前
终焉轮回里,藏着 AI 与人类的答案
前端·人工智能·aigc
twl4 小时前
OpenClaw 深度技术解析
前端
崔庆才丨静觅4 小时前
比官方便宜一半以上!Grok API 申请及使用
前端
星光不问赶路人5 小时前
vue3使用jsx语法详解
前端·vue.js
天蓝色的鱼鱼5 小时前
shadcn/ui,给你一个真正可控的UI组件库
前端