# 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
2
3
4
5
- 特点:
- 不同于
var
的函数作用域,let
是真正的块级作用域 - 在循环中每次迭代都会创建新的绑定
- 不同于
for (let i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 100); // 输出 0, 1, 2
}
2
3
# 2. 暂时性死区(TDZ)
定义:在代码块内,使用 let
声明变量之前,该变量都不可访问的区域称为暂时性死区。
console.log(a); // ReferenceError: Cannot access 'a' before initialization
let a = 2;
// 对比 var:
console.log(b); // undefined
var b = 2;
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
}
2
3
4
5
6
# 二、const 关键字详解
# 1. 必须初始化
- 规则:
- const 声明时必须立即赋值
- 不能先声明后赋值
const PI = 3.14; // 正确
const MAX; // SyntaxError: Missing initializer in const declaration
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
2
3
4
5
6
# 3. 冻结对象技巧
方法:使用 Object.freeze()
使对象完全不可变
const OBJ = Object.freeze({ prop: "value" });
OBJ.prop = "new"; // 静默失败(严格模式下报错)
2
# 三、底层原理
- 创建阶段(编译时):
- 在作用域中创建变量
- let/const 会进入 TDZ 状态
- 初始化阶段(运行时):
- 执行到声明语句时初始化
- let/const 结束 TDZ
- 赋值阶段:
- 为变量赋值
# 命名规范
类型 | 命名风格 | 示例 |
---|---|---|
常量 | 全大写+下划线 | 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) {}
# 三、rest参数
用于获取函数的多余参数,这样就不需要使用arguments
对象了。
rest
参数搭配的变量是一个数组,该变量将多余的参数放入数组中。
function add(...values) {
let sum = 0;
for (var val of values) {
sum += val;
}
return sum;
}
add(2, 5, 3) // 10
2
3
4
5
6
7
8
- 与 arguments 的区别:
- 剩余参数是真正的数组
- 只包含未对应形参的实参
- 必须放在参数列表最后
# 3.数组的扩展
# 一、扩展运算符(...)的应用
# 1. 复制数组
const arr1 = [1, 2];
const arr2 = [...arr1]; // 浅拷贝
2
# 2. 合并数组
const merged = [...[1, 2], ...[3, 4]]; // [1, 2, 3, 4]
# 3. 替代apply方法
Math.max(...[1, 2, 3]); // 等同于 Math.max(1, 2, 3)
# 4. 数组空位的处理
// 将数组空位转为 undefined 或者指定值:
console.log([1, , 3].map(x => x || 0)); // [1, 0, 3]
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']
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(); // []
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' }
2
3
4
5
# 2. findIndex()
查找第一个符合条件的元素索引
console.log([1, 5, 10].findIndex(x => x > 3)); // 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]
2
3
4
# 4. includes()
判断是否包含某元素
console.log([1, 2, NaN].includes(2)); // true
console.log([1, 2, NaN].includes(NaN)); // true
2
# 5. 遍历方法
entries()
/ keys()
/ values()
- 返回迭代器
for (const [index, value] of ['a', 'b'].entries()) {
console.log(index, value); // 0 'a' → 1 'b'
}
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]]
2
3
4
5
# 4.对象的扩展
# 一. 属性简写
# 1. 属性值简写
const name = 'Alice';
const age = 25;
// ES5
const person = { name: name, age: age };
// ES6
const person = { name, age }; // 自动使用变量名作为属性名
2
3
4
5
6
# 2. 方法简写
// ES5
const obj = {
sayHello: function() { console.log('Hello')}
};
// ES6
const obj = {
sayHello() {console.log('Hello')}
};
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');
}
};
2
3
4
5
6
7
8
9
10
11
# 三、对象扩展运算符(...)
# 1. 对象浅拷贝
const obj1 = { a: 1, b: 2 };
const obj2 = { ...obj1 }; // { a: 1, b: 2 }
2
# 2. 对象合并
const obj1 = { a: 1, b: 2 };
const obj2 = { b: 3, c: 4 };
const merged = { ...obj1, ...obj2 }; // { a: 1, b: 3, c: 4 }
2
3
# 3. 默认值与覆盖
const defaults = { theme: 'light', fontSize: 16 };
const userSettings = { fontSize: 18 };
const finalSettings = { ...defaults, ...userSettings };
// { theme: 'light', fontSize: 18 }
2
3
4
# 四、新增对象方法
# 1. Object.is()
相当于全等符(===),不同之处只有两个:一是+0不等于-0,二是NaN等于自身
Object.is(NaN, NaN); // true
Object.is(+0, -0); // false
2
# 2. Object.assign()
对象合并(浅拷贝)
const target = { a: 1 };
const source = { b: 2 };
Object.assign(target, source); // { a: 1, b: 2 }
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]]
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"
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
2
3
与传统方式的对比
// 传统非标准方式
console.log(child.__proto__ === parent); // true
// 标准方式(推荐)
console.log(Object.getPrototypeOf(child) === parent); // true
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
2
3
4
5
6
7
8
与传统方式的对比
// 传统非标准方式(不推荐)
child.__proto__ = parent;
// 标准方式(推荐)
Object.setPrototypeOf(child, parent);
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 }
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 }
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 }
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 => { /* 失败处理 */ }
);
2
3
4
- 特点:
- 可以链式调用
- 返回新的 Promise 对象
- 第二个参数可选(通常用 catch 代替)
# 2. catch()
promise.catch(
error => { /* 错误处理 */ }
);
2
3
# 3. finally()
promise.finally(
() => { /* 无论成功失败都会执行 */ }
);
2
3
- 典型用途:
- 清理资源
- 隐藏加载状态
# Promise 静态方法
# 1. Promise.resolve()
// 创建一个立即 resolve 的 Promise
Promise.resolve('success').then(value => {
console.log(value); // 'success'
});
2
3
4
等价于:
new Promise(resolve => resolve('success'))
# 2. Promise.reject()
// 创建一个立即 reject 的 Promise
Promise.reject(new Error('fail')).catch(error => {
console.error(error); // Error: fail
});
2
3
4
# 3. Promise.all()
Promise.all([promise1, promise2, promise3])
.then(values => {
console.log(values); // [value1, value2, value3]
})
.catch(error => {
// 任意一个失败就会触发
});
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 先完成)
2
3
4
5
6
7
8
9
10
11
- 典型应用:
- 请求超时处理
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));
2
3
4
5
6
7
8
9
10
11
12
- 竞速场景
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);
});
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);
}
});
});
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 都失败
});
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('所有服务器请求都失败了');
});
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();
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);
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重命名
2
3
4
5
6
7
8
9
10
# 2. 默认导出(Default Export)
// 每个模块只能有一个默认导出
export default class Person {
constructor(name) {
this.name = name;
}
}
2
3
4
5
6
# 3. 混合导出
export const version = '1.0';
export default function() {
console.log('Default function');
}
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);
2
3
4
5
6
# 2. 导入默认导出
import Person from './module.js'; // 名称可自定义
import customName from './module.js';
2
# 3. 混合导入
import defaultExport, { namedExport } from './module.js';
import defaultExport, * as namespace from './module.js';
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');
}
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'; // 所有命名导出
2
3
4
# 3. 导入时执行(无绑定导入)
import './init.js'; // 仅执行模块,不导入任何内容
# 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);
}
}
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');
2
3
4
# 二、Class 的核心特性
# 1. 本质仍是函数
typeof Person; // "function"
Person === Person.prototype.constructor; // true
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`);
}
}
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();
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);
2
3
4
5
# 4. 初始化与转换
数组去重
const unique = [...new Set([1, 2, 2, 3])]; // [1, 2, 3]
# 典型业务场景
- 数据去重(最常用场景)
// 用户选择的标签去重
const selectedTags = ['前端', 'JavaScript', '前端', 'React'];
const uniqueTags = [...new Set(selectedTags)];
// ['前端', 'JavaScript', 'React']
// 商品颜色规格去重
const productColors = ['红', '蓝', '红', '绿', '蓝'];
const uniqueColors = Array.from(new Set(productColors));
2
3
4
5
6
7
8
- 投票/点赞系统
// 用户点赞记录
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;
}
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();
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]]
2
3
4
5
6
7
8
# 典型业务场景
- 键值对数据存储(需要复杂键时)
// 用对象作为键存储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();
});
2
3
4
5
6
7
8
9
10
11
12
13
14
- 更适合使用 Map 的情况:
- 频繁按键查找:比普通对象查找更高效,特别是键为复杂类型时
- 需要有序遍历:Map保持插入顺序,而对象属性顺序不可靠
- 大数据量键值存储:当键值对数量很大时(10万+),Map性能优于对象