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 渲染机制对比,我也可以继续展开。是否要配合图形化可视化执行流程模拟工具?

十二、节流和防抖

相关推荐
[email protected]1 分钟前
Asp.Net Core SignalR导入数据
前端·后端·asp.net·.netcore
小满zs5 小时前
Zustand 第五章(订阅)
前端·react.js
涵信6 小时前
第一节 基础核心概念-TypeScript与JavaScript的核心区别
前端·javascript·typescript
谢尔登6 小时前
【React】常用的状态管理库比对
前端·spring·react.js
编程乐学(Arfan开发工程师)6 小时前
56、原生组件注入-原生注解与Spring方式注入
java·前端·后端·spring·tensorflow·bug·lua
小公主6 小时前
JavaScript 柯里化完全指南:闭包 + 手写 curry,一步步拆解原理
前端·javascript
姑苏洛言8 小时前
如何解决答题小程序大小超过2M的问题
前端
TGB-Earnest9 小时前
【leetcode-合并两个有序链表】
javascript·leetcode·链表
GISer_Jing9 小时前
JWT授权token前端存储策略
前端·javascript·面试
开开心心就好9 小时前
电脑扩展屏幕工具
java·开发语言·前端·电脑·php·excel·batch