# 1.let和const

特性 var let const
作用域 函数作用域 块级作用域 块级作用域
变量提升 会提升 提升但存在TDZ 提升但存在TDZ
初始化(声明就赋值) 不用 var a; 不用 let a; 需要const a = 1;
重复声明 允许 不允许 不允许
值可变性 可变 可变 不可变(基本类型)
全局作用域 成为window属性 不成为window属性 不成为window属性

变量提升是 JS 引擎在代码执行前的一个预处理行为,它会将变量和函数的声明移动到当前作用域的顶部。

# 一、let 关键字

# 1. 块级作用域

let 声明的变量只在当前代码块(由 {} 界定)内有效,包括 if、for、while 等语句的代码块。

{
  let x = 10;
  console.log(x); // 10
}
console.log(x); // ReferenceError: x is not defined
1
2
3
4
5
  • 特点​​:
    • 不同于 var 的函数作用域,let 是真正的块级作用域
    • 在循环中每次迭代都会创建新的绑定
for (let i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 100); // 输出 0, 1, 2
}
1
2
3

有关算法题 (opens new window)

# 2. 暂时性死区(TDZ)

定义​​:在代码块内,使用 let 声明变量之前,该变量都不可访问的区域称为暂时性死区。

console.log(a); // ReferenceError: Cannot access 'a' before initialization
let a = 2;

// 对比 var​​:
console.log(b); // undefined
var b = 2;
1
2
3
4
5
6
  • ​原理​​:
    • 变量在作用域内已经存在(编译阶段)
    • 但在声明语句执行前不可访问(运行阶段)
    • 这种设计是为了强制良好的编程习惯

# 3. 不允许重复声明

  • 规则​​:
    • 在同一作用域内,let 不允许重复声明同名变量
    • 包括与 var、const 或函数参数的重复声明
let c = 1;
let c = 2; // SyntaxError: Identifier 'c' has already been declared

function test(arg) {
  let arg = 10; // SyntaxError: Identifier 'arg' has already been declared
}
1
2
3
4
5
6

# 二、const 关键字详解

# 1. 必须初始化

  • 规则​​:
    • const 声明时必须立即赋值
    • 不能先声明后赋值
const PI = 3.14; // 正确
const MAX; // SyntaxError: Missing initializer in const declaration
1
2

# 2. 基本类型不可变

  • 定义​​:
    • 对于基本类型(Number、String、Boolean 等),const 声明的变量值不可改变
    • 对于引用类型(Object、Array 等),变量绑定的内存地址不可变,但内容可修改
const NAME = "Alice";
NAME = "Bob"; // TypeError: Assignment to constant variable

const ARR = [1, 2];
ARR.push(3); // 允许
ARR = [4, 5]; // TypeError
1
2
3
4
5
6

# 3. 冻结对象技巧

方法​​:使用 Object.freeze() 使对象完全不可变

const OBJ = Object.freeze({ prop: "value" });
OBJ.prop = "new"; // 静默失败(严格模式下报错)
1
2

# 三、底层原理

    1. ​创建阶段​​(编译时):
    • 在作用域中创建变量
    • let/const 会进入 TDZ 状态
    1. ​初始化阶段​​(运行时):
    • 执行到声明语句时初始化
    • let/const 结束 TDZ
    1. ​赋值阶段​​:
    • 为变量赋值

# 命名规范

类型 命名风格 示例
常量 全大写+下划线 const MAX_SIZE = 10
普通变量 驼峰式 let itemCount = 0
私有变量 下划线前缀 let _internal = 1

# 2.函数的扩展

# 一、箭头函数

优点:
(1)、函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。
(2)、写法更加简洁

缺点:
(1)、不可以当作构造函数,箭头函数没有 this 值,同时 箭头函数也没有 prototype,也就是说,不可以使用new命令,否则会抛出一个错误。
(2)、不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用 rest 参数代替。
(3)、不可以使用yield命令,因此箭头函数不能用作 Generator 函数。

更多对比参考:JS基础的this指向 (opens new window)

# 二、函数默认值

直接为函数的参数指定默认值

function Point(x = 0, y = 0) {}
1

# 三、rest参数

用于获取函数的多余参数,这样就不需要使用arguments对象了。
rest 参数搭配的变量是一个数组,该变量将多余的参数放入数组中。

function add(...values) {
  let sum = 0;
  for (var val of values) {
    sum += val;
  }
  return sum;
}
add(2, 5, 3) // 10
1
2
3
4
5
6
7
8
  • 与 arguments 的区别​​:
    • 剩余参数是真正的数组
    • 只包含未对应形参的实参
    • 必须放在参数列表最后

# 3.数组的扩展

# 一、扩展运算符(...)的应用

# 1. 复制数组

const arr1 = [1, 2];
const arr2 = [...arr1]; // 浅拷贝
1
2

# 2. 合并数组

const merged = [...[1, 2], ...[3, 4]]; // [1, 2, 3, 4]
1

# 3. 替代apply方法

Math.max(...[1, 2, 3]); // 等同于 Math.max(1, 2, 3)
1

# 4. 数组空位的处理

// 将数组空位转为 undefined 或者指定值:
console.log([1, , 3].map(x => x || 0)); // [1, 0, 3]
1
2

# 二、创建数组的新方法

# 1. Array.from()

将类数组对象或可迭代对象转为数组

let arrayLike = {
    '0': 'a', '1': 'b', '2': 'c', length: 3
};

// ES5的写法
var arr1 = [].slice.call(arrayLike); // ['a', 'b', 'c']

// ES6的写法
let arr2 = Array.from(arrayLike); // ['a', 'b', 'c']
1
2
3
4
5
6
7
8
9

# 2. Array.of()

创建包含任意数量元素的数组

Array.of(3);       // [3] 
Array.of(1, 2, 3); // [1, 2, 3]
Array.of();        // []
1
2
3

# 三、数组实例的新方法

# 1. find()

查找第一个符合条件的元素

const users = [
  { id: 1, name: 'Alice' },
  { id: 2, name: 'Bob' }
];
console.log(users.find(u => u.id === 2)); // { id: 2, name: 'Bob' }
1
2
3
4
5

# 2. findIndex()

查找第一个符合条件的元素索引

console.log([1, 5, 10].findIndex(x => x > 3)); // 1
1

# 3. fill()

填充数组元素

// 填充整个数组
console.log([1, 2, 3].fill(0)); // [0, 0, 0]
// 指定范围填充
console.log([1, 2, 3, 4].fill(0, 1, 3)); // [1, 0, 0, 4]
1
2
3
4

# 4. includes()

判断是否包含某元素

console.log([1, 2, NaN].includes(2));    // true
console.log([1, 2, NaN].includes(NaN)); // true
1
2

# 5. 遍历方法

entries() / keys() / values() - 返回迭代器

for (const [index, value] of ['a', 'b'].entries()) {
  console.log(index, value); // 0 'a' → 1 'b'
}
1
2
3

# 6. flat() ES2019 (ES10)

flat() 用于将嵌套数组"拉平"(数组扁平化)

const arr1 = [1, 2, [3, 4]];
console.log(arr1.flat()); // [1, 2, 3, 4]

const arr2 = [1, 2, [3, 4, [5, 6]]];
console.log(arr2.flat()); // [1, 2, 3, 4, [5, 6]]
1
2
3
4
5

# 4.对象的扩展

# 一. 属性简写

# 1. 属性值简写

const name = 'Alice';
const age = 25;
// ES5
const person = { name: name, age: age };
// ES6
const person = { name, age }; // 自动使用变量名作为属性名
1
2
3
4
5
6

# 2. 方法简写

// ES5
const obj = {
  sayHello: function() { console.log('Hello')}
};
// ES6
const obj = {
  sayHello() {console.log('Hello')}
};
1
2
3
4
5
6
7
8

# 二、计算属性名

const propKey = 'name';
const dynamicKey = 'First' + 'Name';

const obj = {
  [propKey]: 'Alice',       // 使用变量作为属性名
  ['age']: 25,             // 使用表达式作为属性名
  [dynamicKey]: 'Bob',     // 计算结果作为属性名
  ['say' + 'Hello']() {    // 计算方法名
    console.log('Hello');
  }
};
1
2
3
4
5
6
7
8
9
10
11

# 三、对象扩展运算符(...)

# 1. 对象浅拷贝

const obj1 = { a: 1, b: 2 };
const obj2 = { ...obj1 }; // { a: 1, b: 2 }
1
2

# 2. 对象合并

const obj1 = { a: 1, b: 2 };
const obj2 = { b: 3, c: 4 };
const merged = { ...obj1, ...obj2 }; // { a: 1, b: 3, c: 4 }
1
2
3

# 3. 默认值与覆盖

const defaults = { theme: 'light', fontSize: 16 };
const userSettings = { fontSize: 18 };
const finalSettings = { ...defaults, ...userSettings };
// { theme: 'light', fontSize: 18 }
1
2
3
4

# 四、新增对象方法

# 1. Object.is()

相当于全等符(===),不同之处只有两个:一是+0不等于-0,二是NaN等于自身

Object.is(NaN, NaN);   // true
Object.is(+0, -0);     // false
1
2

# 2. Object.assign()

对象合并(浅拷贝)

const target = { a: 1 };
const source = { b: 2 };
Object.assign(target, source); // { a: 1, b: 2 }
1
2
3

# 3. Object.keys() / Object.values() / Object.entries()

const obj = { a: 1, b: 2 };

Object.keys(obj);    // ['a', 'b']
Object.values(obj);  // [1, 2]
Object.entries(obj); // [['a', 1], ['b', 2]]
1
2
3
4
5

# 4. super 关键字

指向当前对象的原型对象

const proto = {
  greet() { return 'Hello'}
};

const obj = {
  greet() { return super.greet() + ' World' }
};

Object.setPrototypeOf(obj, proto);
obj.greet(); // "Hello World"
1
2
3
4
5
6
7
8
9
10

# 5. Object.getPrototypeOf(obj)

返回指定对象的原型(即内部 [[Prototype]] 属性的值)

const parent = { name: 'Parent' };
const child = Object.create(parent);
console.log(Object.getPrototypeOf(child) === parent); // true
1
2
3

与传统方式的对比

// 传统非标准方式
console.log(child.__proto__ === parent); // true
// 标准方式(推荐)
console.log(Object.getPrototypeOf(child) === parent); // true
1
2
3
4

# 6. Object.setPrototypeOf(obj, prototype)

设置一个指定对象的原型到另一个对象或 null

const parent = { name: 'Parent' };
const child = { name: 'Child' };

Object.setPrototypeOf(child, parent);

console.log(child.name); // "Child"
console.log(child.toString()); // 继承自 Object.prototype
console.log(Object.getPrototypeOf(child) === parent); // true
1
2
3
4
5
6
7
8

与传统方式的对比

// 传统非标准方式(不推荐)
child.__proto__ = parent;
// 标准方式(推荐)
Object.setPrototypeOf(child, parent);
1
2
3
4

# 7. Object.fromEntries() ES2019 (ES10)

用于将键值对列表(如 Map、数组等)转换为一个对象。它是 Object.entries() 的逆操作。

数组转换为对象

const entries = [
  ['name', 'Alice'],
  ['age', 25],
  ['isAdmin', true]
];

const obj = Object.fromEntries(entries);
console.log(obj);
// { name: "Alice", age: 25, isAdmin: true }
1
2
3
4
5
6
7
8
9

Map 转换为对象

const map = new Map([
  ['name', 'Bob'],
  ['age', 30]
]);

const obj = Object.fromEntries(map);
console.log(obj);
// { name: "Bob", age: 30 }
1
2
3
4
5
6
7
8

与 Object.entries() 的关系

Object.fromEntries()Object.entries() 的逆操作:

const originalObj = { a: 1, b: 2 };
const entries = Object.entries(originalObj);
// [ ['a', 1], ['b', 2] ]
const newObj = Object.fromEntries(entries);
// { a: 1, b: 2 }
1
2
3
4
5

# 5.Promise介绍

Promise 对象是异步编程的一种解决方案,j解决传统的回调地狱函数的问题
Promises/A+ 规范是 JavaScript Promise 的标准,规定了一个 Promise 所必须具有的特性。
Promise对象有以下两个特点。

一、对象的状态不受外界影响。

Promise对象代表一个异步操作,

有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。

只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。

二、一旦状态改变,就不会再变,任何时候都可以得到这个结果。

Promise对象的状态改变,只有两种可能:
从pending变为fulfilled和从pending变为rejected。
只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果,这时就称为 resolved(已定型)。

# Promise 实例方法

# 1. then()

promise.then(
  value => { /* 成功处理 */ },
  error => { /* 失败处理 */ }
);
1
2
3
4
  • ​​特点​​:
    • 可以链式调用
    • 返回新的 Promise 对象
    • 第二个参数可选(通常用 catch 代替)

# 2. catch()

promise.catch(
  error => { /* 错误处理 */ }
);
1
2
3

# 3. finally()

promise.finally(
  () => { /* 无论成功失败都会执行 */ }
);
1
2
3
  • ​​典型用途​​:
    • 清理资源
    • 隐藏加载状态

# Promise 静态方法

# 1. Promise.resolve()

// 创建一个立即 resolve 的 Promise
Promise.resolve('success').then(value => {
  console.log(value); // 'success'
});
1
2
3
4

等价于​​:

new Promise(resolve => resolve('success'))
1

# 2. Promise.reject()

// 创建一个立即 reject 的 Promise
Promise.reject(new Error('fail')).catch(error => {
  console.error(error); // Error: fail
});
1
2
3
4

# 3. Promise.all()

Promise.all([promise1, promise2, promise3])
  .then(values => {
    console.log(values); // [value1, value2, value3]
  })
  .catch(error => {
    // 任意一个失败就会触发
  });
1
2
3
4
5
6
7
  • 特点​​:
    • 所有 Promise 都成功才算成功
    • 一个失败立即 reject
    • 结果数组顺序与输入一致

# 4. Promise.race()

该 Promise 会"跟随"第一个完成的 Promise(无论成功或失败)。

  • 核心特点
    • 竞速机制​​:只关心第一个完成的 Promise
    • 短路特性​​:一旦有一个 Promise 完成(无论成功或失败),立即返回
    • 结果继承​​:返回的 Promise 状态和值与第一个完成的 Promise 一致
const promise1 = new Promise((resolve) => 
  setTimeout(() => resolve('Promise 1'), 1000)
);
const promise2 = new Promise((_, reject) => 
  setTimeout(() => reject('Promise 2'), 500)
);

Promise.race([promise1, promise2])
  .then(value => console.log('Success:', value))
  .catch(error => console.error('Error:', error)); 
// 输出:Error: Promise 2 (因为 promise2 先完成)
1
2
3
4
5
6
7
8
9
10
11
  • 典型应用​​:
  1. 请求超时处理
function fetchWithTimeout(url, timeout = 3000) {
  const fetchPromise = fetch(url);
  const timeoutPromise = new Promise((_, reject) => 
    setTimeout(() => reject(new Error('Request timeout')), timeout)
  );

  return Promise.race([fetchPromise, timeoutPromise]);
}

fetchWithTimeout('https://api.example.com/data')
  .then(response => response.json())
  .catch(error => console.error(error));
1
2
3
4
5
6
7
8
9
10
11
12
  1. 竞速场景
const server1 = fetch('https://server1.example.com/data');
const server2 = fetch('https://server2.example.com/data');

Promise.race([server1, server2])
  .then(response => {
    console.log('更快响应的服务器:', response.url);
  });
1
2
3
4
5
6
7

# 5. Promise.allSettled() (ES2020)

Promise.allSettled([promise1, promise2])
.then(results => {
  results.forEach(result => {
    if (result.status === 'fulfilled') {
      console.log(result.value);
    } else {
      console.error(result.reason);
    }
  });
});
1
2
3
4
5
6
7
8
9
10
  • 与 Promise.all() 区别​​:
    • 不会因为某个 Promise 失败而中断
    • 总是等到所有 Promise 完成

# 6. Promise.any() (ES2021)

  • 核心行为
    • 接收一个 Promise 可迭代对象(通常是数组)
    • 返回一个新的 Promise
    • ​当输入的任何一个 Promise 成功(fulfilled)时​​,立即 resolve 该 Promise 的结果
    • ​当所有 Promise 都失败(rejected)时​​,reject 一个 AggregateError(包含所有错误)
Promise.any([promise1, promise2])
  .then(value => {
    // 第一个成功的 Promise 的结果
  })
  .catch(errors => {
    // 所有 Promise 都失败
  });
1
2
3
4
5
6
7
  • 实际应用场景1:多服务器请求,取最快成功响应
const servers = [
  fetch('https://server1.example.com/data'),
  fetch('https://server2.example.com/data'),
  fetch('https://server3.example.com/data')
];

Promise.any(servers)
  .then(response => response.json())
  .then(data => {
    console.log('从最快响应的服务器获取数据:', data);
  })
  .catch(() => {
    console.error('所有服务器请求都失败了');
  });
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 可取消(终止)的 Promise

# 1. 使用 AbortController (推荐),适用于 fetch 和 axios 等 API:

const controller = new AbortController();
const signal = controller.signal;

// 创建一个可取消的 Promise
const fetchPromise = new Promise((resolve, reject) => {
  fetch('https://api.example.com/data', { signal })
    .then(response => response.json())
    .then(resolve)
    .catch(reject);

  // 监听 abort 事件
  signal.addEventListener('abort', () => {
    reject(new DOMException('Aborted', 'AbortError'));
  });
});

// 取消 Promise
controller.abort();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# 2. 使用 Promise.race()

通过与其他 Promise 竞争来实现取消:

function makeCancellable(promise) {
  let rejectFn;
  const wrappedPromise = new Promise((resolve, reject) => {
    rejectFn = reject;
    promise.then(resolve).catch(reject);
  });
  wrappedPromise.cancel = () => {
    rejectFn(new Error('Promise was cancelled'));
  };
  return wrappedPromise;
}

// 使用示例
const myPromise = new Promise(resolve => {
  setTimeout(() => resolve('Done'), 3000);
});
const cancellable = makeCancellable(myPromise);
// 取消
setTimeout(() => cancellable.cancel(), 1000);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# 6.async/await

async/await 是 ES2017 (ES8) 引入的异步编程语法糖,基于 Promise 实现,可以让异步代码看起来像同步代码一样直观。

async 可以说是在generator的基础上进行改进

generator函数的执行要通过next()方法,除此之外还可以依靠co模块,而async函数像普通函数一样执行
async函数的返回值是 Promise 对象,这比 Generator 函数的返回值是 Iterator 对象方便多了
async的语法比promise和generator都简单

# 7.import 和 export

ES6 模块系统是 JavaScript 官方标准化的模块解决方案,提供了 import 和 export 语法来实现模块的导入和导出。

# 一、基本导出(export)

# 1. 命名导出(Named Exports)

// 方式1:声明时直接导出
export const name = 'Alice';
export function sayHello() {
  console.log('Hello');
}

// 方式2:先声明后统一导出
const age = 25;
const job = 'Developer';
export { age, job as occupation }; // 使用as重命名
1
2
3
4
5
6
7
8
9
10

# 2. 默认导出(Default Export)

// 每个模块只能有一个默认导出
export default class Person {
  constructor(name) {
    this.name = name;
  }
}
1
2
3
4
5
6

# 3. 混合导出

export const version = '1.0';
export default function() {
  console.log('Default function');
}
1
2
3
4

# 二、基本导入(import)

# 1. 导入命名导出

import { name, sayHello } from './module.js';
import { age, occupation as job } from './module.js'; // 使用as重命名

// 导入所有命名导出为命名空间对象
import * as module from './module.js';
console.log(module.name);
1
2
3
4
5
6

# 2. 导入默认导出

import Person from './module.js'; // 名称可自定义
import customName from './module.js';
1
2

# 3. 混合导入

import defaultExport, { namedExport } from './module.js';
import defaultExport, * as namespace from './module.js';
1
2

# 三、高级用法

# 1. 动态导入(Dynamic Import)

// 返回Promise
import('./module.js')
  .then(module => {
    console.log(module.default);
  })
  .catch(err => {
    console.error('加载失败', err);
  });

// 在async函数中
async function loadModule() {
  const module = await import('./module.js');
}
1
2
3
4
5
6
7
8
9
10
11
12
13

# 2. 重新导出(Re-export)

// 从其他模块导入并立即导出
export { default } from './module.js'; // 默认导出
export { name } from './module.js';   // 命名导出
export * from './module.js';         // 所有命名导出
1
2
3
4

# 3. 导入时执行(无绑定导入)

import './init.js'; // 仅执行模块,不导入任何内容
1

# 8.Class 以及 extends

# 一、Class 基本语法

# 1. 类定义

class Person {
  // 构造函数
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }
  
  // 实例方法
  sayHello() {
    console.log(`Hello, my name is ${this.name}`);
  }
  
  // 静态方法
  static create(name) {
    return new Person(name, 0);
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# 2. 使用类

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

const baby = Person.create('Baby');
1
2
3
4

# 二、Class 的核心特性

# 1. 本质仍是函数

typeof Person; // "function"
Person === Person.prototype.constructor; // true
1
2

# 2. 类的方法

  • 实例方法:定义在 prototype
  • 静态方法:定义在类本身(添加 static 关键字)

# 3. 类与普通构造函数的区别

  • 必须使用 new 调用
  • 不存在提升(暂时性死区)
  • 默认严格模式
  • 方法不可枚举

# 三、extends 实现继承

# 1. 基本继承

class Student extends Person {
  constructor(name, age, grade) {
    super(name, age); // 必须首先调用super()
    this.grade = grade;
  }
  
  study() {
    console.log(`${this.name} is studying`);
  }
}
1
2
3
4
5
6
7
8
9
10

# 2. super 关键字

  • super():调用父类构造函数(只能在子类构造函数中使用)
  • super.method():调用父类方法

# 9.Set 和 Map

特性 Set Map
​​存储内容​ 唯一值 键值对
​​键/值类型​ 任意类型值 任意类型键
​​​相等判断​​ SameValueZero SameValueZero
​​顺序​ 插入顺序 插入顺序
主要方法​ add/has/delete set/get/has/delete
应用场景​ 值唯一性检查 键值映射存储

# 一、Set(集合)

# 1. 基本特性

  • 类似数组,但​​成员值唯一​​(无重复值)
  • 使用 SameValueZero 算法判断值是否相等(类似 ===,但认为 NaN 等于自身)
  • 插入顺序即为遍历顺序

# 2. 基本用法

const set = new Set();

// 添加值
set.add(1).add(2).add(2).add(NaN).add(NaN);
console.log(set.size); // 3 (1, 2, NaN)
// 判断存在
console.log(set.has(1)); // true
// 删除值
set.delete(2);
console.log(set.size); // 2
// 清空集合
set.clear();
1
2
3
4
5
6
7
8
9
10
11
12

# 3. 初始化与转换

// 通过数组初始化
const set = new Set([1, 2, 3, 3]);
console.log([...set]); // [1, 2, 3]
// 转为数组
const arr = Array.from(set);
1
2
3
4
5

# 4. 初始化与转换

数组去重

const unique = [...new Set([1, 2, 2, 3])]; // [1, 2, 3]
1

# 典型业务场景

    1. 数据去重(最常用场景)
// 用户选择的标签去重
const selectedTags = ['前端', 'JavaScript', '前端', 'React'];
const uniqueTags = [...new Set(selectedTags)];
// ['前端', 'JavaScript', 'React']

// 商品颜色规格去重
const productColors = ['红', '蓝', '红', '绿', '蓝'];
const uniqueColors = Array.from(new Set(productColors));
1
2
3
4
5
6
7
8
    1. 投票/点赞系统
// 用户点赞记录
const postLikes = new Set();
function likePost(userId, postId) {
  const key = `${userId}_${postId}`;
  if (postLikes.has(key)) {
    console.log('已经点过赞了');
    return false;
  }
  postLikes.add(key);
  // 执行点赞逻辑...
  return true;
}
1
2
3
4
5
6
7
8
9
10
11
12
  • 更适合使用 Set 的情况:
    • ​大数据量去重​​:当需要处理10万+数据去重时,Set比数组过滤性能高得多
    • ​​频繁存在性检查​​:has()操作时间复杂度为O(1),远优于数组的includes()
    • 集合运算​​:并集、交集、差集等运算

# 二、Map(映射)

# 1. 基本特性

  • 类似对象,但​​键可以是任意类型​​(对象只能用字符串/Symbol作为键)
  • 按插入顺序迭代
  • 相比普通对象有更好的性能(频繁增删键值对的场景)
const map = new Map();
// 添加键值对
map.set('name', 'Alice')
   .set({}, 'object key')
   .set(NaN, 'not a number');
// 获取值
console.log(map.get(NaN)); // "not a number"
// 判断键存在
console.log(map.has('name')); // true
// 删除键值对
map.delete('name');
// 清空映射
map.clear();
1
2
3
4
5
6
7
8
9
10
11
12
13

# 2. 初始化与转换

// 通过二维数组初始化
const map = new Map([
  ['name', 'Alice'],
  ['age', 25]
]);

// 转为数组
console.log([...map]); // [['name', 'Alice'], ['age', 25]]
1
2
3
4
5
6
7
8

# 典型业务场景

    1. 键值对数据存储(需要复杂键时)
// 用对象作为键存储DOM元素关联数据
const domData = new Map();
const button = document.getElementById('myButton');

domData.set(button, {
  clickCount: 0,
  lastClickTime: null
});

button.addEventListener('click', () => {
  const data = domData.get(button);
  data.clickCount++;
  data.lastClickTime = new Date();
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
  • 更适合使用 Map 的情况:
    • ​频繁按键查找​​:比普通对象查找更高效,特别是键为复杂类型时
    • ​需要有序遍历​​:Map保持插入顺序,而对象属性顺序不可靠
    • 大数据量键值存储​​:当键值对数量很大时(10万+),Map性能优于对象
lastUpdate: 5/23/2025, 5:45:51 PM