ES6 新增了哪个基本数据类型?它有什么特点?
What new primitive data type was added in ES6? What are its characteristics?
- *考察点:基础知识的掌握。*
共 52 道题目
What new primitive data type was added in ES6? What are its characteristics?
What new primitive data type was added in ES6? What are its characteristics?
考察点:基础知识的掌握。
答案:
ES6 新增了 Symbol 这个基本数据类型。Symbol 是一种原始数据类型,表示独一无二的值。每个通过 Symbol() 创建的值都是唯一的,即使传入相同的描述参数。
主要特点:
唯一性:每个 Symbol 值都是独一无二的
const sym1 = Symbol('desc');
const sym2 = Symbol('desc');
console.log(sym1 === sym2); // false
不可枚举性:Symbol 属性不会出现在 for…in、Object.keys() 等遍历中
const obj = {
name: 'test',
[Symbol('hidden')]: 'secret'
};
console.log(Object.keys(obj)); // ['name']
类型转换限制:Symbol 不能与其他类型进行隐式转换
const sym = Symbol('test');
// String(sym) 或 sym.toString() 可以显式转换
// sym + '' 会报错
适用场景:
What are the differences between let, const and var?
What are the differences between let, const and var?
考察点:变量声明方式和作用域。
答案:
let、const 和 var 是 JavaScript 中三种变量声明方式,它们在作用域、变量提升、重复声明和赋值方面有显著区别。
主要区别:
作用域范围:
// var: 函数作用域或全局作用域
function test1() {
if (true) {
var a = 1;
}
console.log(a); // 1,可以访问
}
// let/const: 块级作用域
function test2() {
if (true) {
let b = 1;
const c = 2;
}
// console.log(b); // ReferenceError
// console.log(c); // ReferenceError
}
变量提升表现:
console.log(a); // undefined (var声明被提升)
// console.log(b); // ReferenceError (let存在暂时性死区)
var a = 1;
let b = 2;
重复声明:
var a = 1;
var a = 2; // 允许重复声明
let b = 1;
// let b = 2; // SyntaxError: 不允许重复声明
重新赋值:
let a = 1;
a = 2; // 允许重新赋值
const b = 1;
// b = 2; // TypeError: 不允许重新赋值
使用建议:
What is block scope? How does ES6 implement it?
What is block scope? How does ES6 implement it?
考察点:作用域理解与变量生命周期。
答案:
块级作用域是指变量只在一对大括号 {} 内有效的作用域规则。ES6 通过 let 和 const 关键字引入了块级作用域,解决了 var 声明变量时的作用域问题。
ES6 实现方式:
let 声明的块级作用域:
{
let a = 1;
console.log(a); // 1
}
// console.log(a); // ReferenceError: a is not defined
const 声明的块级作用域:
{
const PI = 3.14159;
console.log(PI); // 3.14159
}
// console.log(PI); // ReferenceError: PI is not defined
for 循环中的块级作用域:
// ES5 问题
for (var i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 100); // 3 3 3
}
// ES6 解决方案
for (let i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 100); // 0 1 2
}
主要特征:
实际应用:
What are the differences between arrow functions and regular functions?
What are the differences between arrow functions and regular functions?
考察点:语法特性与 this 指向。
答案:
箭头函数是 ES6 引入的函数简写语法,与普通函数在语法、this 指向、arguments 对象等方面存在显著区别。
主要区别:
语法差异:
// 普通函数
function add(a, b) {
return a + b;
}
// 箭头函数
const add = (a, b) => a + b;
// 单参数可省略括号
const double = x => x * 2;
// 无参数必须保留括号
const getRandom = () => Math.random();
this 指向:
const obj = {
name: 'Alice',
// 普通函数:this 指向调用对象
getName: function() {
return this.name; // 'Alice'
},
// 箭头函数:this 继承外层作用域
getNameArrow: () => {
return this.name; // undefined (全局this)
}
};
arguments 对象:
// 普通函数有 arguments 对象
function normalFunc() {
console.log(arguments); // [1, 2, 3]
}
normalFunc(1, 2, 3);
// 箭头函数没有 arguments 对象
const arrowFunc = () => {
// console.log(arguments); // ReferenceError
};
构造函数使用:
// 普通函数可作为构造函数
function Person(name) {
this.name = name;
}
const person = new Person('Bob');
// 箭头函数不能作为构造函数
const PersonArrow = (name) => {
this.name = name;
};
// const person2 = new PersonArrow('Bob'); // TypeError
适用场景:
What are template strings? What are their uses?
What are template strings? What are their uses?
考察点:字符串拼接与表达式插值。
答案:
模板字符串(Template Literals)是 ES6 引入的新型字符串字面量,使用反引号(`)包围,支持嵌入表达式、多行字符串和标签模板等高级功能。
主要用法:
变量插值:
const name = 'Alice';
const age = 25;
// ES5 方式
const msg1 = 'Hello, ' + name + '. You are ' + age + ' years old.';
// ES6 模板字符串
const msg2 = `Hello, ${name}. You are ${age} years old.`;
表达式计算:
const a = 10;
const b = 20;
const result = `${a} + ${b} = ${a + b}`; // "10 + 20 = 30"
// 函数调用
const getName = () => 'Bob';
const greeting = `Hello, ${getName()}!`; // "Hello, Bob!"
多行字符串:
// ES5 多行字符串
const html1 = '<div>\n' +
' <h1>Title</h1>\n' +
' <p>Content</p>\n' +
'</div>';
// ES6 模板字符串
const html2 = `
<div>
<h1>Title</h1>
<p>Content</p>
</div>`;
标签模板:
function highlight(strings, ...values) {
return strings.reduce((result, string, i) => {
return result + string + (values[i] ? `<mark>${values[i]}</mark>` : '');
}, '');
}
const name = 'Alice';
const score = 95;
const message = highlight`Student ${name} scored ${score} points!`;
// "Student <mark>Alice</mark> scored <mark>95</mark> points!"
实际应用:
What is destructuring assignment? What data types can it be used with?
What is destructuring assignment? What data types can it be used with?
考察点:数组、对象解构语法。
答案:
解构赋值是 ES6 引入的语法,允许从数组或对象中提取值并赋给变量,使代码更简洁易读。它是一种模式匹配的赋值方式。
适用数据类型:
数组解构:
// 基本用法
const [a, b, c] = [1, 2, 3];
console.log(a, b, c); // 1 2 3
// 跳过元素
const [first, , third] = [1, 2, 3];
console.log(first, third); // 1 3
// 剩余参数
const [head, ...tail] = [1, 2, 3, 4];
console.log(head, tail); // 1 [2, 3, 4]
对象解构:
// 基本用法
const {name, age} = {name: 'Alice', age: 25, city: 'Beijing'};
console.log(name, age); // 'Alice' 25
// 重命名
const {name: userName, age: userAge} = {name: 'Bob', age: 30};
console.log(userName, userAge); // 'Bob' 30
// 嵌套解构
const {user: {name, profile: {email}}} = {
user: {
name: 'Charlie',
profile: {email: '[email protected]'}
}
};
字符串解构:
const [a, b, c, d, e] = 'hello';
console.log(a, b, c, d, e); // 'h' 'e' 'l' 'l' 'o'
// 获取字符串长度属性
const {length} = 'hello';
console.log(length); // 5
函数参数解构:
// 对象参数解构
function greet({name, age = 18}) {
return `Hello ${name}, you are ${age} years old`;
}
greet({name: 'Alice', age: 25}); // "Hello Alice, you are 25 years old"
// 数组参数解构
function sum([a, b]) {
return a + b;
}
sum([10, 20]); // 30
默认值设置:
// 数组默认值
const [x = 1, y = 2] = [undefined, null];
console.log(x, y); // 1 null
// 对象默认值
const {name = 'Anonymous', age = 0} = {age: 25};
console.log(name, age); // 'Anonymous' 25
实际应用:
How does ES6 declare default parameters? How is it different from ES5?
How does ES6 declare default parameters? How is it different from ES5?
考察点:函数参数默认值。
答案:
ES6 提供了直接在函数参数中声明默认值的语法,相比 ES5 的实现方式更加简洁和安全。
ES6 默认参数语法:
// 基本用法
function greet(name = 'Guest', age = 18) {
return `Hello ${name}, you are ${age} years old`;
}
greet(); // "Hello Guest, you are 18 years old"
greet('Alice'); // "Hello Alice, you are 18 years old"
greet('Bob', 25); // "Hello Bob, you are 25 years old"
与 ES5 的区别:
语法简洁性:
// ES5 方式
function multiply(a, b) {
a = a || 1; // 问题:a为0时也会使用默认值
b = typeof b !== 'undefined' ? b : 1; // 正确但冗长
return a * b;
}
// ES6 方式
function multiply(a = 1, b = 1) {
return a * b;
}
undefined 处理:
// ES6 只有 undefined 才触发默认值
function test(x = 10) {
return x;
}
test(0); // 0 (不会使用默认值)
test(null); // null (不会使用默认值)
test(undefined); // 10 (使用默认值)
test(); // 10 (使用默认值)
表达式和函数调用:
// 默认值可以是表达式或函数调用
function createId(prefix = 'id', suffix = Date.now()) {
return `${prefix}-${suffix}`;
}
// 可以引用前面的参数
function greet(name = 'Guest', message = `Hello ${name}`) {
return message;
}
与解构赋值结合:
// ES6 支持复杂的默认值设定
function config({
host = 'localhost',
port = 3000,
protocol = 'http'
} = {}) {
return `${protocol}://${host}:${port}`;
}
config(); // "http://localhost:3000"
config({host: 'example.com'}); // "http://example.com:3000"
优势对比:
What is the spread operator (...)? What are its common use cases?
What is the spread operator (…)? What are its common use cases?
考察点:数组、对象、函数参数等。
答案:
扩展运算符(Spread Operator)用三个点(…)表示,可以将可迭代对象展开为独立的元素。它在数组、对象、函数调用等场景中有广泛应用。
常见应用场景:
数组操作:
// 数组展开
const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
const combined = [...arr1, ...arr2]; // [1, 2, 3, 4, 5, 6]
// 数组复制(浅拷贝)
const original = [1, 2, 3];
const copy = [...original]; // [1, 2, 3]
// 数组转换
const str = 'hello';
const chars = [...str]; // ['h', 'e', 'l', 'l', 'o']
函数参数传递:
// 将数组元素作为参数传递
const numbers = [1, 2, 3, 4, 5];
const max = Math.max(...numbers); // 等同于 Math.max(1, 2, 3, 4, 5)
// 替代 apply 方法
function sum(a, b, c) {
return a + b + c;
}
const args = [1, 2, 3];
const result = sum(...args); // 6
对象操作:
// 对象展开(ES2018+)
const obj1 = {a: 1, b: 2};
const obj2 = {c: 3, d: 4};
const merged = {...obj1, ...obj2}; // {a: 1, b: 2, c: 3, d: 4}
// 对象复制和属性覆盖
const user = {name: 'Alice', age: 25};
const updatedUser = {...user, age: 26}; // {name: 'Alice', age: 26}
剩余参数(Rest Parameters):
// 函数剩余参数
function sum(first, ...rest) {
return first + rest.reduce((a, b) => a + b, 0);
}
sum(1, 2, 3, 4); // 10
// 数组解构中的剩余元素
const [head, ...tail] = [1, 2, 3, 4, 5];
console.log(head); // 1
console.log(tail); // [2, 3, 4, 5]
DOM 操作:
// NodeList 转数组
const nodeList = document.querySelectorAll('.item');
const elementsArray = [...nodeList];
// 数组方法应用
[...nodeList].forEach(el => el.classList.add('active'));
实际应用:
How does ES6 declare multi-line strings?
How does ES6 declare multi-line strings?
考察点:模板字符串语法。
答案:
ES6 通过模板字符串(Template Literals)提供了声明多行字符串的简洁方式,使用反引号(`)包围字符串,可以直接在字符串中换行。
语法格式:
// ES6 模板字符串多行声明
const multilineString = `
这是第一行
这是第二行
这是第三行
`;
与 ES5 对比:
ES5 实现方式:
// 方式一:字符串拼接
const html = '<div>' +
' <h1>标题</h1>' +
' <p>内容</p>' +
'</div>';
// 方式二:转义字符
const text = '第一行\n第二行\n第三行';
// 方式三:数组 join
const lines = [
'第一行',
'第二行',
'第三行'
].join('\n');
ES6 模板字符串:
const html = `
<div>
<h1>标题</h1>
<p>内容</p>
</div>`;
const text = `
第一行
第二行
第三行`;
实际应用示例:
HTML 模板:
const createCard = (title, content) => `
<div class="card">
<h2 class="card-title">${title}</h2>
<div class="card-content">
${content}
</div>
</div>`;
SQL 查询:
const query = `
SELECT users.name, orders.total
FROM users
JOIN orders ON users.id = orders.user_id
WHERE orders.status = 'completed'
ORDER BY orders.created_at DESC`;
配置文件内容:
const configContent = `
{
"name": "${packageName}",
"version": "${version}",
"description": "${description}"
}`;
注意事项:
trim() 方法${} 使用优势:
What are the differences between for...of and for...in?
What are the differences between for…of and for…in?
考察点:遍历对象与数组的方式。
答案:
for…of 和 for…in 是两种不同的循环语句,主要区别在于遍历的内容和适用的数据类型。
主要区别:
遍历内容不同:
const array = ['a', 'b', 'c'];
// for...in 遍历索引/键名
for (let index in array) {
console.log(index); // "0", "1", "2" (字符串类型)
}
// for...of 遍历值
for (let value of array) {
console.log(value); // "a", "b", "c"
}
适用数据类型:
// for...in 适用于对象
const obj = {name: 'Alice', age: 25, city: 'Beijing'};
for (let key in obj) {
console.log(key, obj[key]); // name Alice, age 25, city Beijing
}
// for...of 适用于可迭代对象
const string = 'hello';
for (let char of string) {
console.log(char); // h, e, l, l, o
}
原型链属性:
Array.prototype.customMethod = function() {};
const arr = [1, 2, 3];
// for...in 会遍历原型链上的可枚举属性
for (let key in arr) {
console.log(key); // "0", "1", "2", "customMethod"
}
// for...of 只遍历自身的值
for (let value of arr) {
console.log(value); // 1, 2, 3
}
支持的数据结构对比:
for…in 支持:
for…of 支持:
使用建议:
// 推荐用法
const users = [{id: 1, name: 'Alice'}, {id: 2, name: 'Bob'}];
// 遍历对象属性 - 使用 for...in
for (let key in users[0]) {
console.log(key, users[0][key]);
}
// 遍历数组元素 - 使用 for...of
for (let user of users) {
console.log(user.name);
}
// 需要索引时 - 使用 entries()
for (let [index, user] of users.entries()) {
console.log(index, user.name);
}
注意事项:
What are Map and Set? How do they differ from Object and Array?
What are Map and Set? How do they differ from Object and Array?
考察点:新数据结构的理解。
答案:
Map 和 Set 是 ES6 引入的两种新数据结构。Map 是键值对集合,Set 是值的集合,它们在功能和性能上相比传统的 Object 和 Array 有显著优势。
Map 与 Object 的区别:
键的类型:
// Object 的键只能是字符串或 Symbol
const obj = {
'name': 'Alice',
1: 'number key' // 1 会被转为字符串 "1"
};
// Map 的键可以是任何类型
const map = new Map();
map.set('string', '字符串键');
map.set(1, '数字键');
map.set(true, '布尔键');
map.set({id: 1}, '对象键');
大小获取:
// Object 需要手动计算
const obj = {a: 1, b: 2, c: 3};
const objSize = Object.keys(obj).length; // 3
// Map 直接获取
const map = new Map([['a', 1], ['b', 2], ['c', 3]]);
const mapSize = map.size; // 3
遍历方式:
// Object 遍历
for (let key in obj) {
console.log(key, obj[key]);
}
// Map 遍历(保证插入顺序)
for (let [key, value] of map) {
console.log(key, value);
}
Set 与 Array 的区别:
唯一性:
// Array 允许重复元素
const arr = [1, 2, 2, 3, 3, 3];
console.log(arr); // [1, 2, 2, 3, 3, 3]
// Set 自动去重
const set = new Set([1, 2, 2, 3, 3, 3]);
console.log(set); // Set(3) {1, 2, 3}
操作方法:
// Array 基于索引操作
const arr = [1, 2, 3];
arr.push(4);
arr[1] = 'modified';
// Set 基于值操作
const set = new Set([1, 2, 3]);
set.add(4);
set.delete(2);
console.log(set.has(3)); // true
API 对比:
// Map 常用方法
const map = new Map();
map.set(key, value); // 设置键值对
map.get(key); // 获取值
map.has(key); // 检查键是否存在
map.delete(key); // 删除键值对
map.clear(); // 清空所有键值对
// Set 常用方法
const set = new Set();
set.add(value); // 添加值
set.has(value); // 检查值是否存在
set.delete(value); // 删除值
set.clear(); // 清空所有值
适用场景:
性能优势:
How does ES6 simplify object property and method declarations?
How does ES6 simplify object property and method declarations?
考察点:对象字面量增强写法。
答案:
ES6 通过对象字面量增强(Enhanced Object Literals)提供了多种简化对象属性和方法声明的语法,使代码更加简洁易读。
主要简化方式:
属性简写:
// ES5 写法
const name = 'Alice';
const age = 25;
const user1 = {
name: name,
age: age
};
// ES6 简写(属性名与变量名相同时)
const user2 = {
name,
age
};
方法简写:
// ES5 写法
const obj1 = {
sayHello: function() {
return 'Hello!';
},
calculate: function(a, b) {
return a + b;
}
};
// ES6 简写
const obj2 = {
sayHello() {
return 'Hello!';
},
calculate(a, b) {
return a + b;
}
};
计算属性名:
// ES5 需要分步骤
const prefix = 'user';
const obj1 = {};
obj1[prefix + 'Name'] = 'Alice';
obj1[prefix + 'Age'] = 25;
// ES6 动态属性名
const obj2 = {
[`${prefix}Name`]: 'Alice',
[`${prefix}Age`]: 25,
[Symbol.iterator]: function() { /* ... */ }
};
生成器方法:
const obj = {
* generator() {
yield 1;
yield 2;
yield 3;
}
};
const gen = obj.generator();
console.log([...gen]); // [1, 2, 3]
async/await 方法:
const api = {
async fetchUser(id) {
const response = await fetch(`/api/users/${id}`);
return response.json();
},
async* asyncGenerator() {
yield await Promise.resolve(1);
yield await Promise.resolve(2);
}
};
综合应用示例:
const createUser = (name, age, role) => {
const prefix = 'get';
const createdAt = Date.now();
return {
// 属性简写
name,
age,
role,
createdAt,
// 方法简写
greet() {
return `Hello, I'm ${this.name}`;
},
// 计算属性名
[`${prefix}Info`]() {
return {name: this.name, age: this.age, role: this.role};
},
// async 方法
async save() {
const response = await fetch('/api/users', {
method: 'POST',
body: JSON.stringify(this.getInfo())
});
return response.json();
},
// 生成器方法
* getFields() {
yield ['name', this.name];
yield ['age', this.age];
yield ['role', this.role];
}
};
};
使用优势:
注意事项:
What is Symbol? What are its application scenarios?
What is Symbol? What are its application scenarios?
考察点:唯一性、隐藏属性等。
答案:
Symbol 是 ES6 引入的第七种基本数据类型,表示独一无二的值。主要用于创建唯一标识符,避免属性名冲突,实现对象的私有属性等。
应用场景:
避免属性名冲突:
// 多个库或模块可能使用同名属性
const PRIVATE_KEY = Symbol('private');
const myObject = {
publicProperty: 'visible',
[PRIVATE_KEY]: 'hidden from normal enumeration'
};
console.log(Object.keys(myObject)); // ['publicProperty']
实现私有属性/方法:
const _counter = Symbol('counter');
const _increment = Symbol('increment');
class Counter {
constructor() {
this[_counter] = 0;
}
[_increment]() {
this[_counter]++;
}
get value() {
return this[_counter];
}
next() {
this[_increment]();
return this.value;
}
}
定义对象的元编程接口:
// 自定义迭代器
const myIterable = {
data: [1, 2, 3],
[Symbol.iterator]() {
let index = 0;
return {
next: () => {
if (index < this.data.length) {
return { value: this.data[index++], done: false };
}
return { done: true };
}
};
}
};
for (let value of myIterable) {
console.log(value); // 1, 2, 3
}
创建常量值:
// 替代魔法字符串常量
const STATUS = {
LOADING: Symbol('loading'),
SUCCESS: Symbol('success'),
ERROR: Symbol('error')
};
function handleStatus(status) {
switch (status) {
case STATUS.LOADING:
return 'Loading...';
case STATUS.SUCCESS:
return 'Success!';
case STATUS.ERROR:
return 'Error occurred';
}
}
实现单例模式:
const singleton = Symbol('singleton');
class Database {
constructor() {
if (Database[singleton]) {
return Database[singleton];
}
Database[singleton] = this;
}
}
扩展内置对象行为:
// 自定义 toPrimitive 行为
const obj = {
[Symbol.toPrimitive](hint) {
if (hint === 'number') {
return 42;
}
if (hint === 'string') {
return 'custom string';
}
return 'default';
}
};
console.log(+obj); // 42
console.log(`${obj}`); // 'custom string'
Well-known Symbols:
ES6 预定义了多个内置 Symbol:
Symbol.iterator:定义对象默认迭代器Symbol.toStringTag:定义对象的字符串描述Symbol.toPrimitive:定义对象转换为原始值的方法Symbol.hasInstance:定义 instanceof 运算符行为Symbol.species:定义创建衍生对象时使用的构造函数实际开发优势:
How does ES6 implement classes? How is it different from ES5 constructor functions?
How does ES6 implement classes? How is it different from ES5 constructor functions?
考察点:面向对象语法。
答案:
ES6 引入了 class 语法糖,提供了更接近传统面向对象语言的类定义方式,但底层仍基于原型链机制。
ES6 类实现:
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
// 实例方法
greet() {
return `Hello, I'm ${this.name}`;
}
// 静态方法
static species() {
return 'Homo sapiens';
}
// getter/setter
get fullInfo() {
return `${this.name}, ${this.age} years old`;
}
set updateAge(newAge) {
this.age = newAge;
}
}
// 继承
class Student extends Person {
constructor(name, age, grade) {
super(name, age); // 调用父类构造函数
this.grade = grade;
}
greet() {
return `${super.greet()}, I'm a student`;
}
}
与 ES5 构造函数的区别:
语法简洁性:
// ES5 方式
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.greet = function() {
return 'Hello, I\'m ' + this.name;
};
Person.species = function() {
return 'Homo sapiens';
};
// ES6 方式更简洁清晰
class Person {
constructor(name, age) { /* ... */ }
greet() { /* ... */ }
static species() { /* ... */ }
}
严格模式:
// ES6 类自动运行在严格模式下
class MyClass {
constructor() {
// 自动严格模式,this 不会指向全局对象
}
}
不可提升:
// ES5 构造函数可以提升
const p1 = new Person(); // 可以工作
function Person() {}
// ES6 类不会提升
// const p2 = new MyClass(); // ReferenceError
class MyClass {}
继承机制:
// ES5 继承较复杂
function Student(name, age, grade) {
Person.call(this, name, age);
this.grade = grade;
}
Student.prototype = Object.create(Person.prototype);
Student.prototype.constructor = Student;
// ES6 继承简单明了
class Student extends Person {
constructor(name, age, grade) {
super(name, age);
this.grade = grade;
}
}
主要优势:
注意事项:
What is modularization? How does ES6 import and export modules?
What is modularization? How does ES6 import and export modules?
考察点:import/export 语法。
答案:
模块化是将程序拆分成独立的、可重用的代码单元的编程方式。ES6 引入了原生模块系统,提供了 import 和 export 语法来实现模块的导入和导出。
导出语法:
命名导出(Named Exports):
// math.js
export const PI = 3.14159;
export function add(a, b) {
return a + b;
}
// 或者统一导出
const subtract = (a, b) => a - b;
const multiply = (a, b) => a * b;
export { subtract, multiply };
// 重命名导出
export { multiply as times };
默认导出(Default Export):
// calculator.js
class Calculator {
add(a, b) { return a + b; }
}
export default Calculator;
// 或者直接导出
export default function(a, b) {
return a * b;
}
导入语法:
命名导入:
// 基本导入
import { PI, add } from './math.js';
// 重命名导入
import { add as sum, subtract as minus } from './math.js';
// 导入所有命名导出
import * as MathUtils from './math.js';
console.log(MathUtils.PI, MathUtils.add(1, 2));
默认导入:
import Calculator from './calculator.js';
// 可以任意命名
import Calc from './calculator.js';
混合导入:
// 同时导入默认和命名导出
import Calculator, { PI, add } from './math-calculator.js';
动态导入:
// 条件导入
if (condition) {
import('./feature.js').then(module => {
module.default(); // 使用默认导出
});
}
// async/await 方式
async function loadModule() {
const module = await import('./heavy-feature.js');
return module.heavyFunction();
}
模块化优势:
与其他模块系统对比:
What is a Promise? What are its three states?
What is a Promise? What are its three states?
考察点:异步编程基础概念。
答案:
Promise 是 ES6 引入的异步编程解决方案,用于处理异步操作的结果。它代表一个异步操作的最终完成(或失败)及其结果值。
三种状态:
// Promise 状态转换示例
const promise = new Promise((resolve, reject) => {
// 初始状态:pending
console.log('Promise created'); // 同步执行
setTimeout(() => {
if (Math.random() > 0.5) {
resolve('Success!'); // 状态变为 fulfilled
} else {
reject('Failed!'); // 状态变为 rejected
}
}, 1000);
});
// 处理 Promise 结果
promise
.then(result => {
console.log('Fulfilled:', result);
})
.catch(error => {
console.log('Rejected:', error);
});
状态特点:
What new common methods did ES6 add to Array?
What new common methods did ES6 add to Array?
考察点:数组操作API。
答案:
ES6 为 Array 新增了多个实用方法,主要用于查找、填充、转换等操作:
主要新增方法:
Array.from() - 将类数组对象或可迭代对象转换为数组:
Array.from('hello'); // ['h','e','l','l','o']
Array.from(new Set([1,2,3])); // [1,2,3]
Array.from({0:'a', 1:'b', length:2}); // ['a','b']
Array.of() - 创建包含所有参数的新数组:
Array.of(7); // [7]
Array.of(1,2,3); // [1,2,3]
Array(7); // [empty × 7] - 对比Array构造函数
find() - 返回第一个满足条件的元素:
[1,2,3,4].find(x => x > 2); // 3
findIndex() - 返回第一个满足条件元素的索引:
[1,2,3,4].findIndex(x => x > 2); // 2
fill() - 用指定值填充数组:
new Array(3).fill(0); // [0,0,0]
[1,2,3].fill('a', 1, 2); // [1,'a',3]
copyWithin() - 浅复制数组的一部分到同一数组中的另一个位置:
[1,2,3,4,5].copyWithin(0, 3); // [4,5,3,4,5]
What new common methods did ES6 add to String?
What new common methods did ES6 add to String?
考察点:字符串处理API。
答案:
ES6 为 String 新增了多个便于字符串处理的方法:
主要新增方法:
startsWith() - 判断字符串是否以指定子字符串开头:
'hello world'.startsWith('hello'); // true
'hello world'.startsWith('world', 6); // true
endsWith() - 判断字符串是否以指定子字符串结尾:
'hello world'.endsWith('world'); // true
'hello world'.endsWith('hello', 5); // true
includes() - 判断字符串是否包含指定子字符串:
'hello world'.includes('lo w'); // true
'hello world'.includes('hi'); // false
repeat() - 重复字符串指定次数:
'abc'.repeat(3); // 'abcabcabc'
'hello'.repeat(0); // ''
padStart() 和 padEnd() - 字符串填充:
'5'.padStart(3, '0'); // '005'
'hello'.padEnd(10, '!'); // 'hello!!!!!'
Are variables declared with const really immutable? Please give examples.
考察点:const 的特性和限制。
答案:
const 声明的变量并不是完全不可变的。const 只保证变量标识符不能被重新赋值,但对于引用类型(对象、数组)的内部内容是可以修改的。
基本类型 - 不可变:
const num = 42;
// num = 43; // TypeError: Assignment to constant variable
const str = 'hello';
// str = 'world'; // TypeError: Assignment to constant variable
引用类型 - 内部可变:
对象属性可以修改:
const obj = { name: 'Alice', age: 25 };
// 可以修改属性
obj.name = 'Bob';
obj.city = 'Beijing';
console.log(obj); // { name: 'Bob', age: 25, city: 'Beijing' }
// 但不能重新赋值整个对象
// obj = {}; // TypeError
数组元素可以修改:
const arr = [1, 2, 3];
// 可以修改数组内容
arr.push(4);
arr[0] = 10;
console.log(arr); // [10, 2, 3, 4]
// 但不能重新赋值整个数组
// arr = []; // TypeError
嵌套对象也可以修改:
const user = {
profile: {
name: 'Alice',
settings: {
theme: 'dark'
}
}
};
user.profile.name = 'Bob';
user.profile.settings.theme = 'light';
// 完全有效
实现真正不可变的方法:
Object.freeze() - 浅冻结:
const obj = Object.freeze({ name: 'Alice', age: 25 });
// obj.name = 'Bob'; // 严格模式下报错,非严格模式静默失败
深冻结实现:
function deepFreeze(obj) {
Object.getOwnPropertyNames(obj).forEach(name => {
const value = obj[name];
if (value && typeof value === 'object') {
deepFreeze(value);
}
});
return Object.freeze(obj);
}
const frozenObj = deepFreeze({
user: { name: 'Alice' }
});
使用 Immutable.js 等库:
const { Map } = require('immutable');
const map1 = Map({ a: 1, b: 2, c: 3 });
const map2 = map1.set('b', 50); // 返回新对象,不修改原对象
const 的实际作用:
Please explain the ‘this’ binding rules of arrow functions.
考察点:词法作用域与上下文绑定。
答案:
箭头函数的 this 指向规则与普通函数完全不同。箭头函数没有自己的 this,而是捕获其定义时所在上下文的 this 值,这称为词法绑定或静态绑定。
核心规则:
继承外层作用域的 this:
const obj = {
name: 'Alice',
regularFunction: function() {
console.log('Regular function this:', this.name); // 'Alice'
const arrowFunction = () => {
console.log('Arrow function this:', this.name); // 'Alice'
};
arrowFunction();
}
};
obj.regularFunction();
不受调用方式影响:
const obj = {
name: 'Alice',
arrowMethod: () => {
console.log(this.name); // undefined (全局this)
}
};
// 无论如何调用,this 都不会改变
obj.arrowMethod(); // undefined
obj.arrowMethod.call({name: 'Bob'}); // undefined
obj.arrowMethod.bind({name: 'Charlie'})(); // undefined
与普通函数对比:
function Person(name) {
this.name = name;
// 普通函数:this 取决于调用方式
this.sayHello = function() {
console.log(`Hello, I'm ${this.name}`);
};
// 箭头函数:this 继承构造函数的 this
this.sayHi = () => {
console.log(`Hi, I'm ${this.name}`);
};
}
const person = new Person('Alice');
const sayHello = person.sayHello;
const sayHi = person.sayHi;
// 普通函数丢失 this 绑定
sayHello(); // "Hello, I'm undefined"
// 箭头函数保持 this 绑定
sayHi(); // "Hi, I'm Alice"
实际应用场景:
事件处理器:
class Counter {
constructor() {
this.count = 0;
// 使用箭头函数避免 this 丢失
document.getElementById('btn').addEventListener('click', () => {
this.increment(); // this 正确指向 Counter 实例
});
}
increment() {
this.count++;
console.log(this.count);
}
}
异步操作:
class DataService {
constructor() {
this.data = [];
}
async fetchData() {
try {
const response = await fetch('/api/data');
const result = await response.json();
// 箭头函数确保 this 指向正确
result.forEach(item => {
this.data.push(item); // this 指向 DataService 实例
});
} catch (error) {
console.error(error);
}
}
}
注意事项:
What is Promise? What problems does it solve?
What is Promise? What problems does it solve?
考察点:异步编程与回调地狱。
答案:
Promise 是 ES6 引入的异步编程解决方案,代表一个异步操作的最终完成或失败。它主要解决了回调函数的嵌套问题,提供了更好的异步流程控制。
Promise 解决的核心问题:
回调地狱(Callback Hell):
// ES5 回调地狱
getData(function(a) {
getMoreData(a, function(b) {
getMoreData(b, function(c) {
getMoreData(c, function(d) {
// 嵌套层级过深,难以维护
});
});
});
});
// Promise 链式调用
getData()
.then(a => getMoreData(a))
.then(b => getMoreData(b))
.then(c => getMoreData(c))
.then(d => {
// 扁平化的异步流程
});
错误处理困难:
// 回调方式:每层都需要错误处理
getData(function(err, a) {
if (err) handleError(err);
getMoreData(a, function(err, b) {
if (err) handleError(err);
// 重复的错误处理逻辑
});
});
// Promise 方式:统一错误处理
getData()
.then(a => getMoreData(a))
.then(b => getMoreData(b))
.catch(err => handleError(err)); // 统一捕获错误
Promise 的核心特性:
状态管理:
const promise = new Promise((resolve, reject) => {
// pending 状态
setTimeout(() => {
if (Math.random() > 0.5) {
resolve('成功'); // 变为 fulfilled
} else {
reject('失败'); // 变为 rejected
}
}, 1000);
});
链式调用:
promise
.then(result => {
console.log(result);
return result + ' 处理完成';
})
.then(newResult => {
console.log(newResult);
})
.catch(error => {
console.error(error);
});
实际应用优势:
并发控制:
// 并行执行多个异步操作
Promise.all([
fetch('/api/user'),
fetch('/api/posts'),
fetch('/api/comments')
]).then(responses => {
// 所有请求完成后处理
});
// 竞速执行,取最快的结果
Promise.race([
fetch('/api/server1'),
fetch('/api/server2')
]).then(fastestResponse => {
// 处理最快返回的结果
});
更好的代码组织:
// 可组合的异步操作
function authenticateUser(credentials) {
return validateCredentials(credentials)
.then(user => generateToken(user))
.then(token => saveSession(token))
.then(session => ({ user: session.user, token: session.token }));
}
解决的问题总结:
How to implement a simple Promise?
How to implement a simple Promise?
考察点:状态机与异步控制。
答案:
实现一个简单的 Promise 需要理解其状态机制、回调队列管理和链式调用原理。以下是一个基础的 Promise 实现:
基础 Promise 实现:
class SimplePromise {
constructor(executor) {
// 三种状态
this.PENDING = 'pending';
this.FULFILLED = 'fulfilled';
this.REJECTED = 'rejected';
// 初始状态
this.state = this.PENDING;
this.value = undefined;
this.reason = undefined;
// 回调队列
this.onFulfilledCallbacks = [];
this.onRejectedCallbacks = [];
// resolve 函数
const resolve = (value) => {
if (this.state === this.PENDING) {
this.state = this.FULFILLED;
this.value = value;
// 执行所有成功回调
this.onFulfilledCallbacks.forEach(callback => callback());
}
};
// reject 函数
const reject = (reason) => {
if (this.state === this.PENDING) {
this.state = this.REJECTED;
this.reason = reason;
// 执行所有失败回调
this.onRejectedCallbacks.forEach(callback => callback());
}
};
// 执行传入的函数
try {
executor(resolve, reject);
} catch (error) {
reject(error);
}
}
// then 方法实现
then(onFulfilled, onRejected) {
// 返回新的 Promise 支持链式调用
return new SimplePromise((resolve, reject) => {
// 处理 fulfilled 状态
const handleFulfilled = () => {
try {
if (typeof onFulfilled === 'function') {
const result = onFulfilled(this.value);
resolve(result);
} else {
resolve(this.value);
}
} catch (error) {
reject(error);
}
};
// 处理 rejected 状态
const handleRejected = () => {
try {
if (typeof onRejected === 'function') {
const result = onRejected(this.reason);
resolve(result);
} else {
reject(this.reason);
}
} catch (error) {
reject(error);
}
};
// 根据当前状态执行相应逻辑
if (this.state === this.FULFILLED) {
// 异步执行保证回调顺序
setTimeout(handleFulfilled, 0);
} else if (this.state === this.REJECTED) {
setTimeout(handleRejected, 0);
} else if (this.state === this.PENDING) {
// 状态未确定,加入回调队列
this.onFulfilledCallbacks.push(handleFulfilled);
this.onRejectedCallbacks.push(handleRejected);
}
});
}
// catch 方法实现
catch(onRejected) {
return this.then(null, onRejected);
}
}
使用示例:
// 基本使用
const promise = new SimplePromise((resolve, reject) => {
setTimeout(() => {
if (Math.random() > 0.5) {
resolve('成功!');
} else {
reject('失败!');
}
}, 1000);
});
promise
.then(result => {
console.log('第一个then:', result);
return result + ' -> 处理完成';
})
.then(result => {
console.log('第二个then:', result);
})
.catch(error => {
console.error('捕获错误:', error);
});
核心实现要点:
状态管理:
回调队列:
链式调用:
异步执行:
静态方法扩展:
// Promise.resolve 静态方法
SimplePromise.resolve = function(value) {
return new SimplePromise(resolve => resolve(value));
};
// Promise.reject 静态方法
SimplePromise.reject = function(reason) {
return new SimplePromise((resolve, reject) => reject(reason));
};
// Promise.all 静态方法
SimplePromise.all = function(promises) {
return new SimplePromise((resolve, reject) => {
const results = [];
let completed = 0;
promises.forEach((promise, index) => {
promise.then(value => {
results[index] = value;
completed++;
if (completed === promises.length) {
resolve(results);
}
}, reject);
});
});
};
这个简单的 Promise 实现展示了 Promise 的核心机制:状态管理、回调队列和链式调用,为理解更复杂的异步流程控制奠定了基础。
What is a Generator? How to use it?
What is a Generator? How to use it?
考察点:函数暂停与迭代器协议。
答案:
Generator(生成器)是 ES6 引入的特殊函数,能够暂停执行并在需要时恢复。它通过 function* 语法定义,使用 yield 关键字来暂停执行并产生值。
基本语法和使用:
Generator 函数定义:
function* simpleGenerator() {
console.log('开始执行');
yield 1;
console.log('第一次恢复');
yield 2;
console.log('第二次恢复');
return 3;
}
// 调用 Generator 函数返回迭代器对象
const gen = simpleGenerator();
console.log(gen.next()); // { value: 1, done: false }
console.log(gen.next()); // { value: 2, done: false }
console.log(gen.next()); // { value: 3, done: true }
yield 的双向通信:
function* communicateGenerator() {
const a = yield '请输入第一个数';
const b = yield '请输入第二个数';
return a + b;
}
const gen = communicateGenerator();
console.log(gen.next()); // { value: '请输入第一个数', done: false }
console.log(gen.next(10)); // { value: '请输入第二个数', done: false }
console.log(gen.next(20)); // { value: 30, done: true }
实际应用场景:
实现迭代器:
function* range(start, end, step = 1) {
for (let i = start; i < end; i += step) {
yield i;
}
}
// 使用 for...of 遍历
for (const num of range(0, 10, 2)) {
console.log(num); // 0, 2, 4, 6, 8
}
// 转换为数组
const numbers = [...range(1, 6)]; // [1, 2, 3, 4, 5]
异步流程控制:
function* fetchUserData() {
try {
const user = yield fetch('/api/user');
const posts = yield fetch(`/api/users/${user.id}/posts`);
const comments = yield fetch(`/api/posts/${posts[0].id}/comments`);
return { user, posts, comments };
} catch (error) {
console.error('获取数据失败:', error);
}
}
// 执行器函数
function runGenerator(genFunc) {
const gen = genFunc();
function step(value) {
const result = gen.next(value);
if (!result.done) {
return result.value.then(step);
}
return result.value;
}
return step();
}
无限序列生成:
function* fibonacci() {
let a = 0, b = 1;
while (true) {
yield a;
[a, b] = [b, a + b];
}
}
const fib = fibonacci();
const first10 = [];
for (let i = 0; i < 10; i++) {
first10.push(fib.next().value);
}
console.log(first10); // [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
状态机实现:
function* trafficLight() {
while (true) {
yield 'red';
yield 'yellow';
yield 'green';
}
}
const light = trafficLight();
console.log(light.next().value); // 'red'
console.log(light.next().value); // 'yellow'
console.log(light.next().value); // 'green'
console.log(light.next().value); // 'red' (循环)
Generator 对象方法:
function* errorHandlingGen() {
try {
const a = yield 'step 1';
const b = yield 'step 2';
yield a + b;
} catch (error) {
yield 'error: ' + error.message;
}
}
const gen = errorHandlingGen();
console.log(gen.next()); // { value: 'step 1', done: false }
console.log(gen.throw(new Error('出错了'))); // { value: 'error: 出错了', done: false }
与 async/await 的关系:
Generator 是 async/await 的基础实现原理:
// Generator 版本
function* asyncGenerator() {
const user = yield fetch('/api/user');
const posts = yield fetch('/api/posts');
return { user, posts };
}
// 等价的 async/await 版本
async function asyncFunction() {
const user = await fetch('/api/user');
const posts = await fetch('/api/posts');
return { user, posts };
}
优势和特点:
How is class inheritance implemented in ES6? What is the role of the super keyword?
How is class inheritance implemented in ES6? What is the role of the super keyword?
考察点:原型链与构造函数继承。
答案:
ES6 的类继承通过 extends 关键字实现,底层仍基于原型链机制。super 关键字用于访问和调用父类的构造函数和方法,是实现继承的核心。
类继承基本语法:
// 父类定义
class Animal {
constructor(name, species) {
this.name = name;
this.species = species;
}
speak() {
return `${this.name} makes a sound`;
}
static getKingdom() {
return 'Animalia';
}
}
// 子类继承
class Dog extends Animal {
constructor(name, breed) {
super(name, 'Canine'); // 调用父类构造函数
this.breed = breed;
}
speak() {
return `${super.speak()} - Woof!`; // 调用父类方法
}
wagTail() {
return `${this.name} is wagging its tail`;
}
}
super 关键字的作用:
在构造函数中调用父类构造函数:
class Vehicle {
constructor(brand, model) {
this.brand = brand;
this.model = model;
}
}
class Car extends Vehicle {
constructor(brand, model, doors) {
super(brand, model); // 必须在使用 this 之前调用
this.doors = doors;
}
}
const myCar = new Car('Toyota', 'Camry', 4);
在方法中调用父类方法:
class Shape {
getArea() {
return 0;
}
describe() {
return `This shape has area: ${this.getArea()}`;
}
}
class Rectangle extends Shape {
constructor(width, height) {
super();
this.width = width;
this.height = height;
}
getArea() {
return this.width * this.height;
}
describe() {
return `Rectangle: ${super.describe()}`;
}
}
在静态方法中调用父类静态方法:
class MathUtils {
static multiply(a, b) {
return a * b;
}
}
class AdvancedMath extends MathUtils {
static power(base, exponent) {
return Math.pow(base, exponent);
}
static multiplyAndPower(a, b, exp) {
const product = super.multiply(a, b); // 调用父类静态方法
return this.power(product, exp);
}
}
继承机制的底层实现原理:
// ES6 类继承等价的 ES5 实现
function Animal(name, species) {
this.name = name;
this.species = species;
}
Animal.prototype.speak = function() {
return `${this.name} makes a sound`;
};
function Dog(name, breed) {
Animal.call(this, name, 'Canine'); // 相当于 super()
this.breed = breed;
}
// 设置原型链
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;
Dog.prototype.speak = function() {
return Animal.prototype.speak.call(this) + ' - Woof!'; // 相当于 super.speak()
};
继承的特殊情况处理:
多层继承:
class Animal {
constructor(name) { this.name = name; }
move() { return `${this.name} is moving`; }
}
class Mammal extends Animal {
constructor(name, furColor) {
super(name);
this.furColor = furColor;
}
breathe() { return `${this.name} is breathing`; }
}
class Cat extends Mammal {
constructor(name, furColor, breed) {
super(name, furColor);
this.breed = breed;
}
purr() {
return `${super.move()} and purring`; // 可以调用祖先类方法
}
}
抽象类模式:
class AbstractShape {
constructor() {
if (new.target === AbstractShape) {
throw new Error('Abstract class cannot be instantiated');
}
}
getArea() {
throw new Error('Must implement getArea method');
}
}
class Circle extends AbstractShape {
constructor(radius) {
super();
this.radius = radius;
}
getArea() {
return Math.PI * this.radius * this.radius;
}
}
注意事项:
super() 才能使用 thissuper 只能在类的方法中使用,不能在普通函数中使用实际应用优势:
super 关键字的便捷调用What is Proxy? What functions can it implement?
What is Proxy? What functions can it implement?
考察点:对象代理与元编程。
答案:
Proxy 是 ES6 引入的强大特性,用于定义基本操作的自定义行为(如属性查找、赋值、枚举、函数调用等)。它创建一个对象的代理,从而实现对该对象基本操作的拦截和自定义。
基本语法:
const proxy = new Proxy(target, handler);
常用的拦截操作(traps):
属性访问拦截(get/set):
const user = {
name: 'Alice',
age: 25
};
const userProxy = new Proxy(user, {
get(target, property) {
console.log(`访问属性: ${property}`);
return target[property];
},
set(target, property, value) {
if (property === 'age' && value < 0) {
throw new Error('年龄不能为负数');
}
console.log(`设置属性: ${property} = ${value}`);
target[property] = value;
return true;
}
});
console.log(userProxy.name); // "访问属性: name" -> "Alice"
userProxy.age = 30; // "设置属性: age = 30"
函数调用拦截(apply):
function sum(a, b) {
return a + b;
}
const sumProxy = new Proxy(sum, {
apply(target, thisArg, argumentsList) {
console.log(`调用函数,参数:`, argumentsList);
const result = target.apply(thisArg, argumentsList);
console.log(`返回结果:`, result);
return result;
}
});
sumProxy(1, 2); // 输出调用信息和结果
实际应用场景:
数据验证和格式化:
function createValidatedUser(userData) {
return new Proxy(userData, {
set(target, property, value) {
if (property === 'email' && !value.includes('@')) {
throw new Error('无效的邮箱格式');
}
if (property === 'age') {
value = parseInt(value); // 自动转换类型
}
target[property] = value;
return true;
}
});
}
const user = createValidatedUser({});
user.email = '[email protected]'; // 正常
user.age = '25'; // 自动转换为数字
响应式数据系统:
function createReactive(data, callback) {
return new Proxy(data, {
set(target, property, value) {
const oldValue = target[property];
target[property] = value;
callback(property, value, oldValue);
return true;
}
});
}
const state = createReactive(
{ count: 0 },
(prop, newVal, oldVal) => {
console.log(`${prop} 从 ${oldVal} 变为 ${newVal}`);
}
);
state.count = 1; // "count 从 0 变为 1"
API 包装和缓存:
function createCachedAPI(apiObject) {
const cache = new Map();
return new Proxy(apiObject, {
get(target, property) {
if (typeof target[property] === 'function') {
return function(...args) {
const cacheKey = `${property}_${JSON.stringify(args)}`;
if (cache.has(cacheKey)) {
console.log('从缓存返回:', cacheKey);
return cache.get(cacheKey);
}
const result = target[property].apply(target, args);
cache.set(cacheKey, result);
return result;
};
}
return target[property];
}
});
}
虚拟对象和默认值:
const defaultConfig = new Proxy({}, {
get(target, property) {
if (property in target) {
return target[property];
}
// 提供默认值
const defaults = {
theme: 'light',
language: 'en',
timeout: 5000
};
return defaults[property];
}
});
console.log(defaultConfig.theme); // 'light'
console.log(defaultConfig.customProp); // undefined
数组负索引访问:
function createArrayWithNegativeIndex(arr) {
return new Proxy(arr, {
get(target, property) {
if (typeof property === 'string' && /^-\d+$/.test(property)) {
const index = target.length + parseInt(property);
return target[index];
}
return target[property];
}
});
}
const arr = createArrayWithNegativeIndex([1, 2, 3, 4, 5]);
console.log(arr[-1]); // 5
console.log(arr[-2]); // 4
常用的 Handler 方法:
get(target, property, receiver):属性读取拦截set(target, property, value, receiver):属性设置拦截has(target, property):in 操作符拦截deleteProperty(target, property):delete 操作拦截apply(target, thisArg, argumentsList):函数调用拦截construct(target, argumentsList, newTarget):new 操作拦截ownKeys(target):Object.keys() 等操作拦截Proxy 的优势:
What is an Iterator? How to customize an iterator?
What is an Iterator? How to customize an iterator?
考察点:遍历协议与 Symbol.iterator。
答案:
迭代器(Iterator)是 ES6 引入的遍历机制,定义了一种标准的方式来产生一系列值。迭代器是一个对象,实现了迭代器协议,即具有 next() 方法。
迭代器协议:
// 迭代器对象必须实现 next() 方法
const iterator = {
next() {
return {
value: someValue, // 当前值
done: boolean // 是否完成
};
}
};
自定义迭代器实现:
简单的数字迭代器:
function createRangeIterator(start, end) {
let current = start;
return {
next() {
if (current < end) {
return { value: current++, done: false };
} else {
return { done: true };
}
}
};
}
const iterator = createRangeIterator(1, 4);
console.log(iterator.next()); // { value: 1, done: false }
console.log(iterator.next()); // { value: 2, done: false }
console.log(iterator.next()); // { value: 3, done: false }
console.log(iterator.next()); // { done: true }
实现可迭代对象:
class Range {
constructor(start, end) {
this.start = start;
this.end = end;
}
// 实现 Symbol.iterator 方法
[Symbol.iterator]() {
let current = this.start;
const end = this.end;
return {
next() {
if (current < end) {
return { value: current++, done: false };
} else {
return { done: true };
}
}
};
}
}
const range = new Range(1, 5);
// 可以使用 for...of 遍历
for (const num of range) {
console.log(num); // 1, 2, 3, 4
}
// 可以转换为数组
const numbers = [...range]; // [1, 2, 3, 4]
复杂的自定义迭代器:
树结构深度优先遍历:
class TreeNode {
constructor(value, children = []) {
this.value = value;
this.children = children;
}
*[Symbol.iterator]() {
yield this.value;
for (const child of this.children) {
yield* child; // 委托给子节点的迭代器
}
}
}
const tree = new TreeNode(1, [
new TreeNode(2, [
new TreeNode(4),
new TreeNode(5)
]),
new TreeNode(3, [
new TreeNode(6)
])
]);
for (const value of tree) {
console.log(value); // 1, 2, 4, 5, 3, 6
}
斐波那契数列迭代器:
class FibonacciIterator {
constructor(limit = Infinity) {
this.limit = limit;
this.count = 0;
this.current = 0;
this.next = 1;
}
[Symbol.iterator]() {
return this;
}
next() {
if (this.count < this.limit) {
const value = this.current;
[this.current, this.next] = [this.next, this.current + this.next];
this.count++;
return { value, done: false };
}
return { done: true };
}
}
const fib = new FibonacciIterator(10);
console.log([...fib]); // [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
异步迭代器:
class AsyncDataIterator {
constructor(urls) {
this.urls = urls;
this.index = 0;
}
[Symbol.asyncIterator]() {
return this;
}
async next() {
if (this.index < this.urls.length) {
try {
const response = await fetch(this.urls[this.index++]);
const data = await response.json();
return { value: data, done: false };
} catch (error) {
return { value: error, done: false };
}
}
return { done: true };
}
}
// 使用 for await...of 遍历
async function processData() {
const iterator = new AsyncDataIterator(['/api/data1', '/api/data2']);
for await (const data of iterator) {
console.log(data);
}
}
迭代器的实用工具:
// 迭代器工具函数
class IteratorUtils {
static map(iterable, fn) {
return {
*[Symbol.iterator]() {
for (const item of iterable) {
yield fn(item);
}
}
};
}
static filter(iterable, predicate) {
return {
*[Symbol.iterator]() {
for (const item of iterable) {
if (predicate(item)) {
yield item;
}
}
}
};
}
static take(iterable, count) {
return {
*[Symbol.iterator]() {
let taken = 0;
for (const item of iterable) {
if (taken >= count) break;
yield item;
taken++;
}
}
};
}
}
// 使用示例
const numbers = new Range(1, 100);
const evenNumbers = IteratorUtils.filter(numbers, x => x % 2 === 0);
const firstTen = IteratorUtils.take(evenNumbers, 10);
console.log([...firstTen]); // [2, 4, 6, 8, 10, 12, 14, 16, 18, 20]
迭代器的应用优势:
What are the common methods of Set and Map? How to iterate through them?
What are the common methods of Set and Map? How to iterate through them?
考察点:API 熟悉度。
答案:
Set 和 Map 是 ES6 引入的两种新的数据结构,提供了丰富的 API 用于操作和遍历数据。
Set 常用方法:
基本操作方法:
const mySet = new Set();
// 添加值
mySet.add(1);
mySet.add(2);
mySet.add(2); // 重复值会被忽略
// 检查是否存在
console.log(mySet.has(1)); // true
console.log(mySet.has(3)); // false
// 删除值
mySet.delete(2); // 返回 true
mySet.delete(3); // 返回 false(不存在)
// 获取大小
console.log(mySet.size); // 1
// 清空所有值
mySet.clear();
Set 遍历方法:
const colors = new Set(['red', 'green', 'blue']);
// for...of 遍历
for (const color of colors) {
console.log(color);
}
// forEach 遍历
colors.forEach(color => console.log(color));
// 使用迭代器方法
for (const color of colors.values()) {
console.log(color);
}
// keys() 和 values() 在 Set 中是相同的
for (const color of colors.keys()) {
console.log(color);
}
// entries() 返回 [value, value] 对
for (const [key, value] of colors.entries()) {
console.log(key, value); // key 和 value 相同
}
Map 常用方法:
基本操作方法:
const myMap = new Map();
// 设置键值对
myMap.set('name', 'Alice');
myMap.set('age', 25);
myMap.set(1, 'number key');
// 获取值
console.log(myMap.get('name')); // 'Alice'
console.log(myMap.get('unknown')); // undefined
// 检查键是否存在
console.log(myMap.has('age')); // true
// 删除键值对
myMap.delete('age'); // 返回 true
// 获取大小
console.log(myMap.size); // 2
// 清空所有键值对
myMap.clear();
Map 遍历方法:
const userMap = new Map([
['name', 'Alice'],
['age', 25],
['city', 'Beijing']
]);
// for...of 遍历键值对
for (const [key, value] of userMap) {
console.log(`${key}: ${value}`);
}
// forEach 遍历
userMap.forEach((value, key) => {
console.log(`${key}: ${value}`);
});
// 遍历键
for (const key of userMap.keys()) {
console.log('Key:', key);
}
// 遍历值
for (const value of userMap.values()) {
console.log('Value:', value);
}
// 遍历键值对
for (const [key, value] of userMap.entries()) {
console.log(`${key} => ${value}`);
}
实际应用示例:
Set 用于数据去重:
// 数组去重
const numbers = [1, 2, 2, 3, 3, 4, 5];
const uniqueNumbers = [...new Set(numbers)]; // [1, 2, 3, 4, 5]
// 字符串去重
const text = 'hello';
const uniqueChars = [...new Set(text)]; // ['h', 'e', 'l', 'o']
// 对象数组去重(需要自定义逻辑)
function uniqueBy(array, key) {
const seen = new Set();
return array.filter(item => {
const value = item[key];
if (seen.has(value)) {
return false;
}
seen.add(value);
return true;
});
}
Map 用于数据缓存:
class Cache {
constructor() {
this.cache = new Map();
}
get(key) {
return this.cache.get(key);
}
set(key, value, ttl = 60000) { // 默认1分钟过期
const expireTime = Date.now() + ttl;
this.cache.set(key, { value, expireTime });
}
has(key) {
const item = this.cache.get(key);
if (item && item.expireTime > Date.now()) {
return true;
}
this.cache.delete(key); // 清理过期数据
return false;
}
clear() {
this.cache.clear();
}
}
性能特点:
转换操作:
// Set 与 Array 转换
const array = [1, 2, 3];
const set = new Set(array);
const backToArray = [...set];
// Map 与 Object 转换
const obj = { a: 1, b: 2 };
const map = new Map(Object.entries(obj));
const backToObj = Object.fromEntries(map);
// Map 与 Array 转换
const mapArray = [...map]; // [['a', 1], ['b', 2]]
const mapFromArray = new Map(mapArray);
What are WeakMap and WeakSet? What are their application scenarios?
What are WeakMap and WeakSet? What are their application scenarios?
考察点:内存管理与垃圾回收。
答案:
WeakMap 和 WeakSet 是 Map 和 Set 的"弱引用"版本,它们的键或值是弱引用,不会阻止垃圾回收器回收对象,主要用于避免内存泄漏。
WeakMap 特点和使用:
基本特性:
const wm = new WeakMap();
// 键必须是对象(不能是原始值)
const obj1 = {};
const obj2 = {};
wm.set(obj1, 'value1');
wm.set(obj2, 'value2');
console.log(wm.get(obj1)); // 'value1'
console.log(wm.has(obj2)); // true
// 当对象被垃圾回收时,对应的键值对也会自动删除
// 无法遍历 WeakMap(没有 keys()、values()、entries() 方法)
// 无法获取大小(没有 size 属性)
私有数据存储:
const privateData = new WeakMap();
class Person {
constructor(name, age) {
// 使用对象实例作为键存储私有数据
privateData.set(this, { name, age });
}
getName() {
return privateData.get(this).name;
}
setAge(age) {
privateData.get(this).age = age;
}
getAge() {
return privateData.get(this).age;
}
}
const person = new Person('Alice', 25);
console.log(person.getName()); // 'Alice'
// 无法直接访问私有数据
DOM 元素关联数据:
const elementData = new WeakMap();
function attachData(element, data) {
elementData.set(element, data);
}
function getData(element) {
return elementData.get(element);
}
// 当 DOM 元素被删除时,相关数据也会被自动清理
const button = document.getElementById('myButton');
attachData(button, { clickCount: 0, lastClicked: null });
WeakSet 特点和使用:
基本特性:
const ws = new WeakSet();
const obj1 = {};
const obj2 = {};
ws.add(obj1);
ws.add(obj2);
console.log(ws.has(obj1)); // true
ws.delete(obj2);
// 值必须是对象,不能遍历,没有 size 属性
对象标记和状态跟踪:
const processedObjects = new WeakSet();
function processObject(obj) {
if (processedObjects.has(obj)) {
console.log('对象已处理过');
return;
}
// 处理对象逻辑
console.log('处理对象中...');
// 标记为已处理
processedObjects.add(obj);
}
const data = { id: 1, value: 'test' };
processObject(data); // "处理对象中..."
processObject(data); // "对象已处理过"
应用场景对比:
WeakMap 应用场景:
// 场景1:对象metadata存储
const metadata = new WeakMap();
class Component {
constructor(props) {
metadata.set(this, {
createdAt: Date.now(),
props: { ...props },
renderCount: 0
});
}
render() {
const meta = metadata.get(this);
meta.renderCount++;
meta.lastRendered = Date.now();
}
}
// 场景2:缓存计算结果
const cache = new WeakMap();
function expensiveOperation(obj) {
if (cache.has(obj)) {
return cache.get(obj);
}
const result = performComplexCalculation(obj);
cache.set(obj, result);
return result;
}
WeakSet 应用场景:
// 场景1:循环引用检测
function stringify(obj, seen = new WeakSet()) {
if (seen.has(obj)) {
return '[Circular Reference]';
}
seen.add(obj);
if (typeof obj === 'object' && obj !== null) {
const result = {};
for (const key in obj) {
result[key] = stringify(obj[key], seen);
}
return result;
}
return obj;
}
// 场景2:权限控制
const authorizedUsers = new WeakSet();
function authorize(user) {
authorizedUsers.add(user);
}
function isAuthorized(user) {
return authorizedUsers.has(user);
}
function performAction(user, action) {
if (!isAuthorized(user)) {
throw new Error('未授权访问');
}
// 执行操作
}
与 Map/Set 的区别:
| 特性 | Map/Set | WeakMap/WeakSet |
|---|---|---|
| 键/值类型 | 任意类型 | 只能是对象 |
| 垃圾回收 | 阻止回收 | 不阻止回收 |
| 可遍历性 | 可遍历 | 不可遍历 |
| size 属性 | 有 | 无 |
| 内存泄漏风险 | 有风险 | 避免泄漏 |
使用建议:
How does ES6 implement shallow copy and deep copy of objects?
How does ES6 implement shallow copy and deep copy of objects?
考察点:扩展运算符、Object.assign、递归。
答案:
ES6 提供了多种实现对象拷贝的方法,包括浅拷贝和深拷贝。理解两者的区别和适用场景对于避免意外的数据修改非常重要。
浅拷贝实现方法:
扩展运算符(…):
const original = { name: 'Alice', age: 25, hobbies: ['reading', 'music'] };
const shallowCopy = { ...original };
shallowCopy.name = 'Bob'; // 不影响原对象
shallowCopy.hobbies.push('sports'); // 影响原对象(引用类型)
console.log(original.hobbies); // ['reading', 'music', 'sports']
Object.assign():
const original = { name: 'Alice', profile: { age: 25, city: 'Beijing' } };
const shallowCopy = Object.assign({}, original);
shallowCopy.name = 'Bob'; // 不影响原对象
shallowCopy.profile.age = 30; // 影响原对象(嵌套对象)
console.log(original.profile.age); // 30
数组浅拷贝:
const originalArray = [1, 2, { name: 'Alice' }];
// 方法1:扩展运算符
const copy1 = [...originalArray];
// 方法2:Array.from()
const copy2 = Array.from(originalArray);
// 方法3:slice()
const copy3 = originalArray.slice();
copy1[2].name = 'Bob'; // 影响原数组(对象引用)
深拷贝实现方法:
JSON 序列化(简单场景):
const original = {
name: 'Alice',
age: 25,
profile: {
city: 'Beijing',
hobbies: ['reading', 'music']
}
};
// 简单深拷贝(有限制)
const deepCopy = JSON.parse(JSON.stringify(original));
deepCopy.profile.city = 'Shanghai';
console.log(original.profile.city); // 'Beijing'(不受影响)
// 限制:无法处理函数、undefined、Symbol、Date 等
递归深拷贝实现:
function deepClone(obj, visited = new WeakMap()) {
// 处理 null 和基本类型
if (obj === null || typeof obj !== 'object') {
return obj;
}
// 处理循环引用
if (visited.has(obj)) {
return visited.get(obj);
}
// 处理日期对象
if (obj instanceof Date) {
return new Date(obj);
}
// 处理正则表达式
if (obj instanceof RegExp) {
return new RegExp(obj);
}
// 处理数组
if (Array.isArray(obj)) {
const cloned = [];
visited.set(obj, cloned);
for (let i = 0; i < obj.length; i++) {
cloned[i] = deepClone(obj[i], visited);
}
return cloned;
}
// 处理普通对象
const cloned = {};
visited.set(obj, cloned);
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
cloned[key] = deepClone(obj[key], visited);
}
}
// 处理 Symbol 属性
const symbolKeys = Object.getOwnPropertySymbols(obj);
for (const symKey of symbolKeys) {
cloned[symKey] = deepClone(obj[symKey], visited);
}
return cloned;
}
// 使用示例
const complexObj = {
name: 'Alice',
date: new Date(),
regex: /test/g,
nested: { value: 42 },
arr: [1, 2, { deep: 'value' }]
};
const deepCopy = deepClone(complexObj);
使用 Lodash 库(生产环境推荐):
const _ = require('lodash');
const original = { /* 复杂对象 */ };
const deepCopy = _.cloneDeep(original);
实用的拷贝工具函数:
class CopyUtils {
// 智能拷贝:根据需要选择浅拷贝或深拷贝
static smartCopy(obj, deep = false) {
if (deep) {
return this.deepClone(obj);
}
if (Array.isArray(obj)) {
return [...obj];
}
if (obj && typeof obj === 'object') {
return { ...obj };
}
return obj;
}
// 合并对象(支持深度合并)
static merge(target, source, deep = false) {
const result = deep ? this.deepClone(target) : { ...target };
for (const key in source) {
if (deep &&
result[key] &&
typeof result[key] === 'object' &&
typeof source[key] === 'object') {
result[key] = this.merge(result[key], source[key], true);
} else {
result[key] = deep ? this.deepClone(source[key]) : source[key];
}
}
return result;
}
static deepClone(obj) {
// 这里使用前面实现的 deepClone 函数
return deepClone(obj);
}
}
// 使用示例
const original = { a: 1, b: { c: 2 } };
const shallow = CopyUtils.smartCopy(original, false);
const deep = CopyUtils.smartCopy(original, true);
性能对比和选择建议:
| 方法 | 性能 | 适用场景 | 限制 |
|---|---|---|---|
| 扩展运算符 | 高 | 简单对象浅拷贝 | 只能浅拷贝 |
| Object.assign | 高 | 对象合并和浅拷贝 | 只能浅拷贝 |
| JSON 方法 | 中等 | 简单数据深拷贝 | 不支持函数、Date等 |
| 递归实现 | 较低 | 复杂对象深拷贝 | 实现复杂 |
| Lodash | 中等 | 生产环境深拷贝 | 需要引入库 |
使用建议:
What is async/await? What is its relationship with Promise?
What is async/await? What is its relationship with Promise?
考察点:异步语法糖与错误处理。
答案:
async/await 是 ES2017(ES8)引入的异步编程语法糖,基于 Promise 构建,提供了更接近同步代码风格的异步操作写法,使异步代码更易读和维护。
与 Promise 的关系:
async/await 本质上就是 Promise:
// Promise 写法
function fetchUserData() {
return fetch('/api/user')
.then(response => response.json())
.then(user => {
return fetch(`/api/users/${user.id}/posts`);
})
.then(response => response.json())
.catch(error => {
console.error('获取数据失败:', error);
});
}
// async/await 写法(等价)
async function fetchUserData() {
try {
const response = await fetch('/api/user');
const user = await response.json();
const postsResponse = await fetch(`/api/users/${user.id}/posts`);
const posts = await postsResponse.json();
return posts;
} catch (error) {
console.error('获取数据失败:', error);
}
}
async 函数返回 Promise:
async function getName() {
return 'Alice'; // 等价于 return Promise.resolve('Alice')
}
// 调用 async 函数
getName().then(name => console.log(name)); // 'Alice'
// 或者在另一个 async 函数中
async function greet() {
const name = await getName();
console.log(`Hello, ${name}!`);
}
基本语法和用法:
async 函数声明:
// 函数声明
async function fetchData() {
// 异步操作
}
// 函数表达式
const fetchData = async function() {
// 异步操作
};
// 箭头函数
const fetchData = async () => {
// 异步操作
};
// 对象方法
const api = {
async getData() {
// 异步操作
}
};
// 类方法
class DataService {
async fetchData() {
// 异步操作
}
}
await 表达式:
async function processData() {
// await 只能在 async 函数内使用
const data = await fetchData(); // 等待 Promise 完成
const processed = await processStep(data);
return processed;
}
// 错误用法:await 不能在普通函数中使用
function normalFunction() {
// const data = await fetchData(); // SyntaxError
}
错误处理:
try-catch 处理:
async function handleErrors() {
try {
const response = await fetch('/api/data');
const data = await response.json();
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return data;
} catch (error) {
console.error('操作失败:', error);
throw error; // 重新抛出或处理
}
}
混合错误处理:
async function mixedErrorHandling() {
try {
const data = await fetchData().catch(err => {
console.log('fetch 错误:', err);
return null; // 提供默认值
});
if (data) {
return await processData(data);
}
return 'default data';
} catch (error) {
console.error('处理错误:', error);
}
}
并发处理:
async function parallelOperations() {
// 错误做法:串行执行
const user = await fetchUser();
const posts = await fetchPosts();
const comments = await fetchComments();
// 正确做法:并行执行
const [user, posts, comments] = await Promise.all([
fetchUser(),
fetchPosts(),
fetchComments()
]);
return { user, posts, comments };
}
// 使用 Promise.allSettled 处理部分失败
async function robustParallelOperations() {
const results = await Promise.allSettled([
fetchUser(),
fetchPosts(),
fetchComments()
]);
const [userResult, postsResult, commentsResult] = results;
return {
user: userResult.status === 'fulfilled' ? userResult.value : null,
posts: postsResult.status === 'fulfilled' ? postsResult.value : [],
comments: commentsResult.status === 'fulfilled' ? commentsResult.value : []
};
}
实际应用示例:
API 调用链:
class UserService {
async getUserProfile(userId) {
try {
// 获取用户基本信息
const user = await this.fetchUser(userId);
// 并行获取相关数据
const [posts, followers, following] = await Promise.all([
this.fetchUserPosts(userId),
this.fetchFollowers(userId),
this.fetchFollowing(userId)
]);
return {
...user,
postsCount: posts.length,
followersCount: followers.length,
followingCount: following.length,
recentPosts: posts.slice(0, 5)
};
} catch (error) {
console.error('获取用户资料失败:', error);
throw new Error('无法加载用户资料');
}
}
async fetchUser(id) {
const response = await fetch(`/api/users/${id}`);
if (!response.ok) throw new Error('用户不存在');
return response.json();
}
}
文件处理:
async function processFiles(fileList) {
const results = [];
for (const file of fileList) {
try {
const content = await readFile(file);
const processed = await processContent(content);
const saved = await saveResult(processed);
results.push({ file: file.name, success: true, id: saved.id });
} catch (error) {
results.push({ file: file.name, success: false, error: error.message });
}
}
return results;
}
性能优化技巧:
// 避免不必要的串行等待
async function optimizedFlow() {
// 立即启动所有异步操作
const userPromise = fetchUser();
const configPromise = fetchConfig();
// 需要用户数据的操作
const user = await userPromise;
const postsPromise = fetchUserPosts(user.id);
// 等待所有操作完成
const [config, posts] = await Promise.all([
configPromise,
postsPromise
]);
return { user, config, posts };
}
async/await 的优势:
What is dynamic import of modules? How to use it?
What is dynamic import of modules? How to use it?
考察点:import() 语法与懒加载。
答案:
动态导入是 ES2020 引入的特性,允许在运行时按需加载模块。与静态导入不同,动态导入使用 import() 函数,返回一个 Promise,支持条件加载和懒加载。
基本语法:
// 动态导入语法
import(moduleSpecifier)
.then(module => {
// 使用模块
})
.catch(error => {
// 处理加载错误
});
// 使用 async/await
async function loadModule() {
try {
const module = await import('./module.js');
return module;
} catch (error) {
console.error('模块加载失败:', error);
}
}
主要使用场景:
条件加载:
async function loadFeature(featureName) {
let module;
switch (featureName) {
case 'chart':
module = await import('./charts/ChartComponent.js');
break;
case 'editor':
module = await import('./editor/EditorComponent.js');
break;
case 'calendar':
module = await import('./calendar/CalendarComponent.js');
break;
default:
throw new Error(`未知功能: ${featureName}`);
}
return module.default;
}
// 根据用户选择加载不同功能
document.getElementById('loadChart').addEventListener('click', async () => {
const ChartComponent = await loadFeature('chart');
new ChartComponent().render();
});
路由懒加载:
// React Router 懒加载示例
const routes = [
{
path: '/home',
component: () => import('./components/Home.js')
},
{
path: '/about',
component: () => import('./components/About.js')
},
{
path: '/contact',
component: () => import('./components/Contact.js')
}
];
// Vue Router 懒加载
const router = new VueRouter({
routes: [
{
path: '/user',
component: () => import('./views/User.vue')
}
]
});
功能检测和降级:
async function loadPolyfill() {
// 检测浏览器是否支持某个特性
if (!window.IntersectionObserver) {
await import('./polyfills/intersection-observer.js');
}
if (!window.fetch) {
await import('./polyfills/fetch.js');
}
}
// 根据环境加载不同实现
async function loadStorage() {
if (typeof window !== 'undefined') {
// 浏览器环境
return import('./storage/browser-storage.js');
} else {
// Node.js 环境
return import('./storage/node-storage.js');
}
}
代码分割和性能优化:
class App {
async loadHeavyFeature() {
// 显示加载指示器
this.showLoading();
try {
// 动态加载大型功能模块
const heavyModule = await import('./heavy-feature.js');
const feature = new heavyModule.HeavyFeature();
// 初始化功能
await feature.initialize();
this.hideLoading();
return feature;
} catch (error) {
this.hideLoading();
this.showError('功能加载失败');
throw error;
}
}
}
与静态导入的对比:
// 静态导入 - 编译时确定
import { utils } from './utils.js';
import defaultExport from './module.js';
// 动态导入 - 运行时加载
const utils = await import('./utils.js');
const defaultExport = (await import('./module.js')).default;
// 静态导入的限制
if (condition) {
// import './module.js'; // 语法错误:不能在块级作用域中使用
}
// 动态导入的灵活性
if (condition) {
const module = await import('./module.js'); // 完全可以
}
高级用法:
批量动态导入:
async function loadPlugins(pluginNames) {
const plugins = await Promise.all(
pluginNames.map(name =>
import(`./plugins/${name}.js`)
.catch(err => {
console.warn(`插件 ${name} 加载失败:`, err);
return null;
})
)
);
return plugins.filter(plugin => plugin !== null);
}
缓存机制:
const moduleCache = new Map();
async function importWithCache(moduleSpecifier) {
if (moduleCache.has(moduleSpecifier)) {
return moduleCache.get(moduleSpecifier);
}
const modulePromise = import(moduleSpecifier);
moduleCache.set(moduleSpecifier, modulePromise);
try {
return await modulePromise;
} catch (error) {
// 如果加载失败,从缓存中移除
moduleCache.delete(moduleSpecifier);
throw error;
}
}
模块预加载:
class ModuleManager {
constructor() {
this.preloadPromises = new Map();
}
// 预加载模块但不执行
preload(moduleSpecifier) {
if (!this.preloadPromises.has(moduleSpecifier)) {
const link = document.createElement('link');
link.rel = 'modulepreload';
link.href = moduleSpecifier;
document.head.appendChild(link);
this.preloadPromises.set(
moduleSpecifier,
import(moduleSpecifier)
);
}
return this.preloadPromises.get(moduleSpecifier);
}
// 使用预加载的模块
async use(moduleSpecifier) {
return await this.preload(moduleSpecifier);
}
}
注意事项:
浏览器兼容性和打包工具支持:
How does ES6 implement rest parameters and spread parameters in function parameters?
How does ES6 implement rest parameters and spread parameters in function parameters?
考察点:…rest 与 …spread。
答案:
ES6 通过三个点(…)语法提供了剩余参数(Rest Parameters)和展开参数(Spread Parameters)功能。虽然使用相同的语法,但在不同位置有不同的含义和作用。
剩余参数(Rest Parameters):
收集多余的参数:
// 基本用法:收集剩余参数为数组
function sum(first, ...rest) {
console.log('first:', first); // 第一个参数
console.log('rest:', rest); // 剩余参数组成的数组
return first + rest.reduce((a, b) => a + b, 0);
}
sum(1, 2, 3, 4, 5);
// first: 1
// rest: [2, 3, 4, 5]
// 返回: 15
替代 arguments 对象:
// ES5 使用 arguments(类数组对象)
function oldSum() {
var args = Array.prototype.slice.call(arguments);
return args.reduce(function(a, b) { return a + b; }, 0);
}
// ES6 使用剩余参数(真正的数组)
function newSum(...numbers) {
return numbers.reduce((a, b) => a + b, 0);
}
// 剩余参数具有数组的所有方法
function processNumbers(...nums) {
return nums
.filter(n => n > 0) // 过滤正数
.map(n => n * 2) // 乘以2
.sort((a, b) => a - b); // 排序
}
与普通参数结合:
function greet(greeting, ...names) {
return `${greeting} ${names.join(', ')}!`;
}
greet('Hello', 'Alice', 'Bob', 'Charlie');
// "Hello Alice, Bob, Charlie!"
// 剩余参数必须是最后一个参数
function invalid(first, ...rest, last) {} // 语法错误
展开参数(Spread Parameters):
展开数组作为函数参数:
function multiply(a, b, c) {
return a * b * c;
}
const numbers = [2, 3, 4];
// ES5 使用 apply
var result1 = multiply.apply(null, numbers);
// ES6 使用展开语法
const result2 = multiply(...numbers);
console.log(result2); // 24 (2 * 3 * 4)
数学运算中的应用:
const scores = [85, 92, 78, 96, 88];
// 找出最大值和最小值
const max = Math.max(...scores); // 96
const min = Math.min(...scores); // 78
// 组合多个数组
const moreScores = [91, 87];
const allScores = [...scores, ...moreScores]; // [85, 92, 78, 96, 88, 91, 87]
高级应用场景:
函数重载模拟:
function createUser(name, ...options) {
const config = {
name,
age: 18,
active: true,
roles: ['user']
};
// 处理不同类型的参数
options.forEach(option => {
if (typeof option === 'number') {
config.age = option;
} else if (typeof option === 'boolean') {
config.active = option;
} else if (Array.isArray(option)) {
config.roles = option;
} else if (typeof option === 'object') {
Object.assign(config, option);
}
});
return config;
}
// 各种调用方式
createUser('Alice'); // 默认配置
createUser('Bob', 25); // 指定年龄
createUser('Charlie', 30, false); // 指定年龄和状态
createUser('David', ['admin', 'user']); // 指定角色
createUser('Eve', 28, true, ['moderator'], {city: 'Beijing'}); // 混合参数
柯里化函数实现:
function curry(fn, ...fixedArgs) {
return function(...newArgs) {
const allArgs = [...fixedArgs, ...newArgs];
if (allArgs.length >= fn.length) {
return fn(...allArgs);
} else {
return curry(fn, ...allArgs);
}
};
}
// 使用示例
function add(a, b, c) {
return a + b + c;
}
const addCurried = curry(add);
const add5 = addCurried(5);
const add5And10 = add5(10);
console.log(add5And10(15)); // 30
事件处理器工厂:
function createEventHandler(baseHandler, ...middlewares) {
return function(event, ...extraArgs) {
// 执行中间件
for (const middleware of middlewares) {
const result = middleware(event, ...extraArgs);
if (result === false) {
return; // 中断执行
}
}
// 执行基础处理器
return baseHandler(event, ...extraArgs);
};
}
// 中间件函数
const logger = (event) => console.log('Event:', event.type);
const validator = (event) => event.target.checkValidity();
// 创建带中间件的事件处理器
const submitHandler = createEventHandler(
(event, formData) => {
console.log('提交表单:', formData);
},
logger,
validator
);
参数解构与剩余参数结合:
function processConfig({host, port, ...settings}, ...plugins) {
console.log('连接信息:', { host, port });
console.log('其他设置:', settings);
console.log('插件列表:', plugins);
return {
connection: { host, port },
settings,
plugins: plugins.map(plugin => plugin.name || plugin)
};
}
processConfig(
{
host: 'localhost',
port: 3000,
debug: true,
timeout: 5000
},
{ name: 'auth', version: '1.0' },
{ name: 'cors', version: '2.0' },
'logger'
);
与箭头函数的使用:
// 剩余参数在箭头函数中
const sum = (...numbers) => numbers.reduce((a, b) => a + b, 0);
// 展开参数在箭头函数调用中
const numbers = [1, 2, 3, 4, 5];
const result = sum(...numbers);
// 高阶函数应用
const pipe = (...functions) => (value) =>
functions.reduce((acc, fn) => fn(acc), value);
const addOne = x => x + 1;
const double = x => x * 2;
const square = x => x * x;
const transform = pipe(addOne, double, square);
console.log(transform(3)); // ((3 + 1) * 2)² = 64
注意事项和最佳实践:
What are default values in destructuring assignment? What should be noted?
What are default values in destructuring assignment? What should be noted?
考察点:语法细节与边界情况。
答案:
解构赋值中的默认值是当解构目标为 undefined 时使用的备用值。它提供了一种优雅的方式来处理可能缺失的数据,避免程序出错。
基本语法和使用:
数组解构的默认值:
// 基本默认值
const [a = 1, b = 2, c = 3] = [10, 20];
console.log(a, b, c); // 10, 20, 3
// 只有 undefined 才触发默认值
const [x = 5, y = 6] = [0, null];
console.log(x, y); // 0, null (0和null不会触发默认值)
// undefined 触发默认值
const [m = 100, n = 200] = [undefined, undefined];
console.log(m, n); // 100, 200
对象解构的默认值:
// 基本对象解构默认值
const {name = 'Anonymous', age = 18, city = 'Unknown'} = {
name: 'Alice',
age: 25
};
console.log(name, age, city); // 'Alice', 25, 'Unknown'
// 重命名与默认值结合
const {name: userName = 'Guest', score: userScore = 0} = {name: 'Bob'};
console.log(userName, userScore); // 'Bob', 0
默认值的计算时机:
// 默认值是惰性计算的,只有在需要时才执行
function getDefault() {
console.log('计算默认值');
return 'default';
}
const [a = getDefault()] = ['actual'];
// 不会输出 "计算默认值",因为 a 有值
const [b = getDefault()] = [undefined];
// 输出 "计算默认值",因为 b 是 undefined
复杂场景的默认值:
嵌套解构的默认值:
// 嵌套对象解构
const {
user: {
name = 'Unknown User',
profile: {
age = 0,
email = 'no-email'
} = {}
} = {}
} = {
user: {
name: 'Alice'
// profile 缺失,使用默认值 {}
}
};
console.log(name, age, email); // 'Alice', 0, 'no-email'
函数参数解构的默认值:
// 参数解构与默认值
function createUser({
name = 'Anonymous',
age = 18,
roles = ['user'],
settings = {theme: 'light', lang: 'en'}
} = {}) {
return { name, age, roles, settings };
}
// 各种调用方式
console.log(createUser()); // 全部使用默认值
console.log(createUser({name: 'Alice'})); // 部分使用默认值
console.log(createUser({age: 25, roles: ['admin']})); // 混合使用
数组解构中的对象默认值:
const [
first = {id: 1, name: 'First'},
second = {id: 2, name: 'Second'}
] = [
{id: 10, name: 'Actual First'}
// second 使用默认值
];
console.log(first); // {id: 10, name: 'Actual First'}
console.log(second); // {id: 2, name: 'Second'}
重要注意事项:
undefined vs null 的区别:
// 只有 undefined 触发默认值
const {a = 10, b = 20} = {a: null, b: undefined};
console.log(a, b); // null, 20
// 0, false, '', NaN 都不会触发默认值
const {x = 1, y = 2, z = 3} = {x: 0, y: false, z: ''};
console.log(x, y, z); // 0, false, ''
默认值可以引用其他变量:
// 默认值可以是表达式
let defaultAge = 18;
const {name, age = defaultAge * 2} = {name: 'Alice'};
console.log(age); // 36
// 默认值可以引用前面解构的变量
const {first = 'default', second = first + '2'} = {first: 'test'};
console.log(first, second); // 'test', 'test2'
// 但不能引用后面的变量(TDZ - 暂时性死区)
const {a = b, b = 1} = {}; // ReferenceError
默认值与剩余参数结合:
function processItems([first = 'default', ...rest] = []) {
console.log('First:', first);
console.log('Rest:', rest);
}
processItems(); // First: default, Rest: []
processItems(['item1']); // First: item1, Rest: []
processItems(['a', 'b', 'c']); // First: a, Rest: ['b', 'c']
实际应用场景:
API 响应处理:
async function fetchUserProfile(userId) {
try {
const response = await fetch(`/api/users/${userId}`);
const data = await response.json();
// 使用默认值处理可能缺失的字段
const {
name = 'Unknown User',
email = '[email protected]',
avatar = '/default-avatar.png',
preferences: {
theme = 'light',
language = 'en',
notifications = true
} = {},
stats: {
posts = 0,
followers = 0,
following = 0
} = {}
} = data;
return {
name, email, avatar,
preferences: { theme, language, notifications },
stats: { posts, followers, following }
};
} catch (error) {
// 返回完全的默认用户对象
return {
name: 'Unknown User',
email: '[email protected]',
avatar: '/default-avatar.png',
preferences: { theme: 'light', language: 'en', notifications: true },
stats: { posts: 0, followers: 0, following: 0 }
};
}
}
配置对象处理:
function initializeApp(config = {}) {
const {
apiUrl = 'http://localhost:3000',
timeout = 5000,
retries = 3,
cache: {
enabled = true,
ttl = 300000 // 5分钟
} = {},
features: {
darkMode = false,
analytics = false,
notifications = true
} = {},
endpoints: {
auth = '/auth',
users = '/users',
data = '/data'
} = {}
} = config;
return {
api: { url: apiUrl, timeout, retries },
cache: { enabled, ttl },
features: { darkMode, analytics, notifications },
endpoints: { auth, users, data }
};
}
性能考虑和最佳实践:
What common methods did ES6 add to Object? Please give examples.
What common methods did ES6 add to Object? Please give examples.
考察点:对象操作API的掌握。
答案:
ES6 为 Object 新增了多个实用的静态方法,这些方法提供了更强大和便捷的对象操作能力,涵盖对象创建、属性操作、比较等方面。
主要新增方法:
Object.assign() - 对象合并和拷贝:
// 基本用法:浅拷贝和合并
const target = { a: 1, b: 2 };
const source1 = { b: 3, c: 4 };
const source2 = { c: 5, d: 6 };
const result = Object.assign(target, source1, source2);
console.log(result); // { a: 1, b: 3, c: 5, d: 6 }
console.log(target === result); // true (target 被修改)
// 创建新对象(不修改原对象)
const newObj = Object.assign({}, target, source1);
// 克隆对象
const clone = Object.assign({}, original);
Object.is() - 严格相等比较:
// 与 === 的区别
console.log(Object.is(NaN, NaN)); // true (=== 返回 false)
console.log(Object.is(+0, -0)); // false (=== 返回 true)
console.log(Object.is(5, 5)); // true
console.log(Object.is('hello', 'hello')); // true
// 实际应用
function safeComparison(a, b) {
return Object.is(a, b);
}
// React 等框架用于判断状态是否真正改变
if (!Object.is(prevState, nextState)) {
// 状态确实改变了
}
Object.setPrototypeOf() 和 Object.getPrototypeOf():
// 设置对象原型
const animal = {
makeSound() {
console.log('Some generic sound');
}
};
const dog = {
name: 'Buddy'
};
Object.setPrototypeOf(dog, animal);
dog.makeSound(); // 'Some generic sound'
// 获取对象原型
console.log(Object.getPrototypeOf(dog) === animal); // true
// 检查原型链
console.log(animal.isPrototypeOf(dog)); // true
Object.keys(), Object.values(), Object.entries():
const person = {
name: 'Alice',
age: 30,
city: 'Beijing'
};
// 获取所有键
const keys = Object.keys(person);
console.log(keys); // ['name', 'age', 'city']
// 获取所有值
const values = Object.values(person);
console.log(values); // ['Alice', 30, 'Beijing']
// 获取键值对数组
const entries = Object.entries(person);
console.log(entries); // [['name', 'Alice'], ['age', 30], ['city', 'Beijing']]
// 实际应用:对象转换
const doubled = Object.fromEntries(
Object.entries(person).map(([key, value]) => [
key,
typeof value === 'number' ? value * 2 : value
])
);
ES6+ 后续新增的重要方法:
Object.fromEntries() - 从键值对创建对象(ES2019):
// 从 Map 创建对象
const map = new Map([
['name', 'Alice'],
['age', 25]
]);
const obj = Object.fromEntries(map);
console.log(obj); // { name: 'Alice', age: 25 }
// 从数组创建对象
const pairs = [['a', 1], ['b', 2], ['c', 3]];
const result = Object.fromEntries(pairs);
console.log(result); // { a: 1, b: 2, c: 3 }
// 与 Object.entries() 配合使用
const original = { x: 1, y: 2, z: 3 };
const transformed = Object.fromEntries(
Object.entries(original).map(([key, value]) => [key.toUpperCase(), value * 2])
);
console.log(transformed); // { X: 2, Y: 4, Z: 6 }
Object.getOwnPropertyDescriptors() - 获取所有属性描述符:
const obj = {
name: 'Alice',
get fullInfo() {
return `${this.name} - ${this.age}`;
}
};
const descriptors = Object.getOwnPropertyDescriptors(obj);
console.log(descriptors);
/*
{
name: { value: 'Alice', writable: true, enumerable: true, configurable: true },
fullInfo: { get: [Function: get fullInfo], set: undefined, enumerable: true, configurable: true }
}
*/
// 完整克隆对象(包括属性描述符)
const clone = Object.create(
Object.getPrototypeOf(obj),
Object.getOwnPropertyDescriptors(obj)
);
实际应用场景:
配置对象合并:
function createConfig(userConfig = {}) {
const defaultConfig = {
apiUrl: 'http://localhost:3000',
timeout: 5000,
retries: 3,
cache: true
};
// 合并配置,用户配置优先
return Object.assign({}, defaultConfig, userConfig);
}
const config = createConfig({ timeout: 8000, debug: true });
// { apiUrl: 'http://localhost:3000', timeout: 8000, retries: 3, cache: true, debug: true }
对象转换和过滤:
function filterObject(obj, predicate) {
return Object.fromEntries(
Object.entries(obj).filter(([key, value]) => predicate(key, value))
);
}
function mapObject(obj, mapper) {
return Object.fromEntries(
Object.entries(obj).map(([key, value]) => [key, mapper(value, key)])
);
}
const data = { a: 1, b: 2, c: 3, d: 4 };
// 过滤偶数值
const evenValues = filterObject(data, (key, value) => value % 2 === 0);
console.log(evenValues); // { b: 2, d: 4 }
// 将所有值乘以2
const doubled = mapObject(data, value => value * 2);
console.log(doubled); // { a: 2, b: 4, c: 6, d: 8 }
深度合并工具:
function deepMerge(target, ...sources) {
if (!sources.length) return target;
const source = sources.shift();
if (isObject(target) && isObject(source)) {
for (const key in source) {
if (isObject(source[key])) {
if (!target[key]) Object.assign(target, { [key]: {} });
deepMerge(target[key], source[key]);
} else {
Object.assign(target, { [key]: source[key] });
}
}
}
return deepMerge(target, ...sources);
}
function isObject(item) {
return item && typeof item === 'object' && !Array.isArray(item);
}
对象属性统计:
function analyzeObject(obj) {
const entries = Object.entries(obj);
return {
keyCount: Object.keys(obj).length,
valueTypes: entries.reduce((acc, [key, value]) => {
const type = typeof value;
acc[type] = (acc[type] || 0) + 1;
return acc;
}, {}),
hasGetters: Object.getOwnPropertyDescriptors(obj),
isEmpty: Object.keys(obj).length === 0
};
}
const sample = {
name: 'test',
count: 42,
active: true,
data: null,
get computed() { return this.count * 2; }
};
console.log(analyzeObject(sample));
性能和使用建议:
How to use destructuring assignment in function parameters? What are the advanced uses?
How to use destructuring assignment in function parameters? What are the advanced uses?
考察点:函数参数解构的灵活应用。
答案:
函数参数解构是 ES6 解构赋值的重要应用场景,它让函数调用更加灵活和直观,特别适合处理配置对象、选项参数等复杂场景。
基础对象参数解构:
简单对象参数解构:
// 传统方式
function createUser(options) {
const name = options.name;
const age = options.age;
const email = options.email || '[email protected]';
}
// 解构方式
function createUser({ name, age, email = '[email protected]' }) {
console.log(`Creating user: ${name}, ${age}, ${email}`);
return { name, age, email, id: Date.now() };
}
// 调用
createUser({ name: 'Alice', age: 25 });
// Creating user: Alice, 25, [email protected]
嵌套对象解构:
function updateProfile({
user: { name, id },
settings: { theme = 'light', notifications = true } = {},
metadata: { lastLogin, createdAt } = {}
}) {
return {
userId: id,
displayName: name,
preferences: { theme, notifications },
timestamps: { lastLogin, createdAt }
};
}
// 调用
const result = updateProfile({
user: { name: 'Bob', id: 123 },
settings: { theme: 'dark' },
metadata: { lastLogin: new Date() }
});
数组参数解构:
// 处理坐标点
function calculateDistance([x1, y1], [x2, y2]) {
return Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2));
}
const pointA = [0, 0];
const pointB = [3, 4];
console.log(calculateDistance(pointA, pointB)); // 5
// 处理多个数值
function sum([first, second, ...rest]) {
return first + second + rest.reduce((acc, num) => acc + num, 0);
}
console.log(sum([1, 2, 3, 4, 5])); // 15
高级用法和模式:
默认参数与解构结合:
// 复杂的默认配置
function apiRequest({
url,
method = 'GET',
headers = {},
timeout = 5000,
retries = 3,
cache = true,
auth: { token, type = 'Bearer' } = {}
} = {}) {
const config = {
url,
method,
headers: {
'Content-Type': 'application/json',
...headers,
...(token && { Authorization: `${type} ${token}` })
},
timeout,
retries,
cache
};
return fetch(url, config);
}
// 各种调用方式
apiRequest(); // 使用全部默认值
apiRequest({ url: '/api/users' }); // 只指定 URL
apiRequest({
url: '/api/users',
method: 'POST',
auth: { token: 'abc123' }
}); // 指定部分参数
重命名和计算属性解构:
function processData({
user_name: userName, // 重命名
user_age: age,
'user-email': email, // 特殊键名
[Symbol.iterator]: iterator, // Symbol 属性
...otherProps // 剩余属性
}) {
return {
profile: { userName, age, email },
metadata: otherProps,
hasIterator: !!iterator
};
}
const data = {
'user_name': 'Charlie',
'user_age': 28,
'user-email': '[email protected]',
department: 'Engineering',
level: 'Senior'
};
console.log(processData(data));
混合解构模式:
// 同时解构对象和数组
function analyzeUserActivity({
userId,
actions: [firstAction, ...restActions],
timestamps: { start, end },
metadata = {}
}) {
return {
user: userId,
summary: {
totalActions: restActions.length + 1,
firstAction,
duration: end - start,
metadata
}
};
}
const activityData = {
userId: 456,
actions: ['login', 'browse', 'purchase', 'logout'],
timestamps: { start: 1000, end: 5000 },
metadata: { browser: 'Chrome', os: 'Windows' }
};
console.log(analyzeUserActivity(activityData));
实际应用场景:
React 组件风格的函数:
// 类似 React 组件的 props 处理
function Button({
children,
onClick = () => {},
disabled = false,
variant = 'primary',
size = 'medium',
icon = null,
...htmlProps
}) {
const className = `btn btn-${variant} btn-${size} ${disabled ? 'disabled' : ''}`;
return {
element: 'button',
props: {
className,
disabled,
onClick,
...htmlProps
},
children: [icon, children].filter(Boolean)
};
}
// 使用
const button = Button({
children: 'Click me',
onClick: () => console.log('Clicked!'),
variant: 'success',
'data-testid': 'submit-btn'
});
配置合并工具:
function mergeConfigs(
baseConfig = {},
{
database: { host, port, name, ...dbConfig } = {},
server: { port: serverPort = 3000, ...serverConfig } = {},
logging: { level = 'info', ...logConfig } = {},
...otherConfig
} = {}
) {
return {
...baseConfig,
database: {
host: host || 'localhost',
port: port || 5432,
name: name || 'app',
...dbConfig
},
server: {
port: serverPort,
...serverConfig
},
logging: {
level,
...logConfig
},
...otherConfig
};
}
const config = mergeConfigs(
{ env: 'development' },
{
database: { host: 'db.example.com', ssl: true },
server: { port: 8080 },
redis: { url: 'redis://localhost:6379' }
}
);
高级技巧和模式:
条件解构和动态属性:
function processRequest({
method,
url,
body,
...config
}, options = {}) {
const { includeAuth = false, includeTiming = false } = options;
const baseConfig = { method, url };
if (body && ['POST', 'PUT', 'PATCH'].includes(method)) {
baseConfig.body = JSON.stringify(body);
}
if (includeAuth && config.auth) {
const { auth: { token, type = 'Bearer' } } = config;
baseConfig.headers = {
...baseConfig.headers,
Authorization: `${type} ${token}`
};
}
if (includeTiming) {
const { timeout = 5000, retries = 0 } = config;
Object.assign(baseConfig, { timeout, retries });
}
return baseConfig;
}
带验证的解构模式:
function createProduct({
name,
price,
category,
inventory: { quantity, warehouse } = {},
metadata = {}
}) {
// 参数验证
if (!name || typeof name !== 'string') {
throw new Error('Name is required and must be a string');
}
if (!price || typeof price !== 'number' || price < 0) {
throw new Error('Price must be a positive number');
}
return {
id: Date.now(),
name: name.trim(),
price: Number(price.toFixed(2)),
category: category || 'general',
inventory: {
quantity: quantity || 0,
warehouse: warehouse || 'main'
},
metadata: {
createdAt: new Date(),
...metadata
}
};
}
性能和最佳实践:
...rest 应该有明确的用途= {}调试技巧:
// 保留原始参数用于调试
function debuggableFunction(params = {}) {
console.log('Original params:', params);
const {
name,
age,
settings: { theme = 'light' } = {}
} = params;
console.log('Destructured:', { name, age, theme });
// 函数逻辑...
}
What is tail call optimization? How does ES6 support tail call optimization?
What is tail call optimization? How does ES6 support tail call optimization?
考察点:性能优化与函数调用机制。
答案:
尾调用优化(Tail Call Optimization,TCO)是一种重要的编程语言优化技术,ES6 规范中首次在 JavaScript 中引入了尾调用优化的支持,旨在解决递归调用中的堆栈溢出问题。
什么是尾调用:
尾调用是指函数的最后一个操作是调用另一个函数,且该调用的返回值直接作为当前函数的返回值。
尾调用的定义和识别:
// 这是尾调用
function f(x) {
return g(x); // g(x) 是最后一个操作,直接返回结果
}
// 这不是尾调用 - 还有其他操作
function f(x) {
return g(x) + 1; // 调用 g(x) 后还要执行加法
}
function f(x) {
const result = g(x); // 调用后还要返回
return result;
}
// 这也不是尾调用 - 在表达式中间
function f(x) {
return 1 + g(x); // g(x) 不是最后执行的操作
}
严格的尾调用条件:
// ✅ 正确的尾调用
function factorial(n, acc = 1) {
if (n <= 1) return acc;
return factorial(n - 1, n * acc); // 直接返回递归调用结果
}
// ❌ 不是尾调用
function factorial(n) {
if (n <= 1) return 1;
return n * factorial(n - 1); // 递归调用后还要乘以 n
}
// ✅ 尾调用优化的斐波那契
function fibonacci(n, a = 0, b = 1) {
if (n === 0) return a;
if (n === 1) return b;
return fibonacci(n - 1, b, a + b); // 尾调用
}
// ❌ 非尾调用的斐波那契
function fibonacci(n) {
if (n <= 1) return n;
return fibonacci(n - 1) + fibonacci(n - 2); // 两次递归调用
}
尾调用优化的原理:
调用栈的问题:
// 传统递归 - 会造成栈溢出
function sum(n) {
if (n === 0) return 0;
return n + sum(n - 1); // 每次调用都要保持栈帧
}
// 调用栈状态示例 sum(3):
// sum(3) -> 3 + sum(2)
// sum(2) -> 2 + sum(1)
// sum(1) -> 1 + sum(0)
// sum(0) -> 0
// 需要保持4个栈帧
console.log(sum(10000)); // RangeError: Maximum call stack size exceeded
尾调用优化后的改进:
// 尾调用优化版本
function sum(n, acc = 0) {
if (n === 0) return acc;
return sum(n - 1, acc + n); // 尾调用,可以复用栈帧
}
// 优化后的调用栈(理论上):
// sum(3, 0) 复用栈帧-> sum(2, 3) 复用栈帧-> sum(1, 5) 复用栈帧-> sum(0, 6) -> 6
// 始终只需要1个栈帧
console.log(sum(10000)); // 50005000 (不会栈溢出)
ES6 尾调用优化的规范:
严格模式要求:
// 尾调用优化只在严格模式下生效
'use strict';
function countdown(n) {
console.log(n);
if (n === 0) return 'Done!';
return countdown(n - 1); // 严格模式下可能被优化
}
// 非严格模式下不会优化
function nonStrictCountdown(n) {
console.log(n);
if (n === 0) return 'Done!';
return arguments.callee(n - 1); // 使用了 arguments,无法优化
}
优化条件的限制:
'use strict';
// ✅ 可以优化的尾调用
function optimizable(n) {
if (n <= 0) return 0;
return optimizable(n - 1);
}
// ❌ 不能优化 - 访问了 arguments
function notOptimizable1(n) {
if (n <= 0) return 0;
console.log(arguments); // 访问 arguments 阻止优化
return notOptimizable1(n - 1);
}
// ❌ 不能优化 - 闭包引用了当前函数
function notOptimizable2(n) {
if (n <= 0) return 0;
const inner = () => notOptimizable2; // 闭包引用阻止优化
return notOptimizable2(n - 1);
}
// ❌ 不能优化 - try/catch 语句
function notOptimizable3(n) {
try {
if (n <= 0) return 0;
return notOptimizable3(n - 1);
} catch (e) {
return -1;
}
}
实际应用示例:
数组处理的尾调用优化:
'use strict';
// 尾递归版本的数组求和
function arraySum(arr, index = 0, acc = 0) {
if (index >= arr.length) return acc;
return arraySum(arr, index + 1, acc + arr[index]);
}
// 尾递归版本的数组过滤
function arrayFilter(arr, predicate, index = 0, result = []) {
if (index >= arr.length) return result;
const newResult = predicate(arr[index])
? [...result, arr[index]]
: result;
return arrayFilter(arr, predicate, index + 1, newResult);
}
// 尾递归版本的数组映射
function arrayMap(arr, mapper, index = 0, result = []) {
if (index >= arr.length) return result;
return arrayMap(
arr,
mapper,
index + 1,
[...result, mapper(arr[index])]
);
}
// 测试
const numbers = Array.from({length: 1000}, (_, i) => i + 1);
console.log(arraySum(numbers)); // 500500
console.log(arrayFilter(numbers, x => x % 2 === 0).length); // 500
树结构遍历的尾调用优化:
'use strict';
// 尾调用优化的树遍历
function traverseTree(node, result = [], stack = []) {
if (!node) {
if (stack.length === 0) return result;
const nextNode = stack.pop();
return traverseTree(nextNode, result, stack);
}
const newResult = [...result, node.value];
const newStack = [...stack];
if (node.right) newStack.push(node.right);
if (node.left) newStack.push(node.left);
return traverseTree(null, newResult, newStack);
}
// 尾调用优化的深度优先搜索
function dfsSearch(graph, start, target, visited = new Set(), path = []) {
if (start === target) {
return [...path, start];
}
if (visited.has(start)) {
return null;
}
const newVisited = new Set([...visited, start]);
const newPath = [...path, start];
const neighbors = graph[start] || [];
return searchNeighbors(neighbors, target, newVisited, newPath, 0);
}
function searchNeighbors(neighbors, target, visited, path, index) {
if (index >= neighbors.length) return null;
const result = dfsSearch(neighbors[index], target, visited, path);
if (result) return result;
return searchNeighbors(neighbors, target, visited, path, index + 1);
}
浏览器支持现状:
// 检测尾调用优化支持
function checkTCOSupport() {
'use strict';
function tcoTest(n) {
if (n <= 0) return true;
return tcoTest(n - 1);
}
try {
return tcoTest(10000);
} catch (e) {
return false;
}
}
console.log('TCO Supported:', checkTCOSupport());
// 大多数现代浏览器的实际情况:
// - Safari:有部分支持
// - Chrome/Firefox:规范支持但实现有限
// - Node.js:有限支持
替代方案和实践建议:
蹦床函数模式:
// 手动实现尾调用优化 - 蹦床函数
function trampoline(fn) {
let result = fn();
while (typeof result === 'function') {
result = result();
}
return result;
}
// 使用蹦床函数的阶乘
function factorial(n, acc = 1) {
if (n <= 1) return acc;
return () => factorial(n - 1, n * acc); // 返回函数而不是直接调用
}
// 安全的深度递归
const result = trampoline(() => factorial(10000));
console.log(result);
迭代替代递归:
// 将递归转换为迭代
function factorialIterative(n) {
let result = 1;
while (n > 1) {
result *= n;
n--;
}
return result;
}
function fibonacciIterative(n) {
if (n <= 1) return n;
let a = 0, b = 1, temp;
for (let i = 2; i <= n; i++) {
temp = a + b;
a = b;
b = temp;
}
return b;
}
// 使用栈模拟递归
function treeTraversalIterative(root) {
if (!root) return [];
const result = [];
const stack = [root];
while (stack.length > 0) {
const node = stack.pop();
result.push(node.value);
if (node.right) stack.push(node.right);
if (node.left) stack.push(node.left);
}
return result;
}
性能和最佳实践:
arguments、闭包引用等调试和测试:
'use strict';
// 测试递归深度限制的工具函数
function testRecursionLimit(fn, maxN = 100000) {
let low = 1, high = maxN, result = 0;
while (low <= high) {
const mid = Math.floor((low + high) / 2);
try {
fn(mid);
result = mid;
low = mid + 1;
} catch (e) {
high = mid - 1;
}
}
return result;
}
// 测试不同递归实现的限制
console.log('Max stack depth:', testRecursionLimit(n => {
function test(x) {
if (x <= 0) return true;
return test(x - 1);
}
return test(n);
}));
Please explain ES6’s event loop mechanism and the execution order of microtasks and macrotasks.
考察点:异步执行模型与优先级。
答案:
事件循环(Event Loop)是 JavaScript 异步编程的核心机制,ES6 引入的 Promise、async/await 等特性进一步丰富了异步执行模型。理解事件循环机制对于编写高质量的异步代码至关重要。
事件循环基础概念:
执行栈、任务队列和事件循环:
// 执行栈示例
function third() {
console.log('3');
}
function second() {
console.log('2');
third();
}
function first() {
console.log('1');
second();
console.log('4');
}
first();
// 输出:1, 2, 3, 4
// 执行栈:first() -> second() -> third() -> 依次弹出
宏任务(Macro Tasks)和微任务(Micro Tasks):
console.log('1'); // 同步代码
setTimeout(() => console.log('2'), 0); // 宏任务
Promise.resolve().then(() => console.log('3')); // 微任务
console.log('4'); // 同步代码
// 输出顺序:1, 4, 3, 2
// 解释:同步代码先执行 -> 微任务优先于宏任务
宏任务和微任务的分类:
宏任务类型:
// 宏任务的主要来源
// 1. setTimeout/setInterval
setTimeout(() => console.log('setTimeout'), 0);
setInterval(() => console.log('setInterval'), 100);
// 2. setImmediate (Node.js)
setImmediate(() => console.log('setImmediate'));
// 3. I/O 操作
fs.readFile('file.txt', () => console.log('File read'));
// 4. UI 渲染 (浏览器)
requestAnimationFrame(() => console.log('Animation frame'));
// 5. script 标签加载
// <script src="..."></script>
// 6. MessageChannel
const channel = new MessageChannel();
channel.port1.onmessage = () => console.log('MessageChannel');
channel.port2.postMessage('test');
微任务类型:
// 微任务的主要来源
// 1. Promise.then/catch/finally
Promise.resolve().then(() => console.log('Promise.then'));
Promise.reject().catch(() => console.log('Promise.catch'));
Promise.resolve().finally(() => console.log('Promise.finally'));
// 2. async/await
async function asyncFunc() {
console.log('async function');
await Promise.resolve();
console.log('after await'); // 微任务
}
// 3. queueMicrotask
queueMicrotask(() => console.log('queueMicrotask'));
// 4. MutationObserver (浏览器)
const observer = new MutationObserver(() => {
console.log('DOM mutation');
});
事件循环的详细执行流程:
完整的执行顺序:
console.log('=== 事件循环执行顺序演示 ===');
// 1. 同步代码
console.log('1: 同步代码开始');
// 2. 宏任务
setTimeout(() => {
console.log('6: setTimeout 宏任务');
// 宏任务中的微任务
Promise.resolve().then(() => console.log('7: 宏任务中的微任务'));
}, 0);
// 3. 微任务
Promise.resolve().then(() => {
console.log('4: 第一个微任务');
// 微任务中的微任务
Promise.resolve().then(() => console.log('5: 嵌套微任务'));
});
// 4. 更多微任务
queueMicrotask(() => console.log('3: queueMicrotask'));
// 5. 同步代码
console.log('2: 同步代码结束');
// 输出顺序:
// 1: 同步代码开始
// 2: 同步代码结束
// 3: queueMicrotask
// 4: 第一个微任务
// 5: 嵌套微任务
// 6: setTimeout 宏任务
// 7: 宏任务中的微任务
复杂场景分析:
async function complexEventLoop() {
console.log('A: 函数开始');
setTimeout(() => console.log('F: setTimeout1'), 0);
const promise1 = new Promise(resolve => {
console.log('B: Promise 构造函数');
resolve();
});
promise1.then(() => {
console.log('D: Promise.then1');
setTimeout(() => console.log('H: setTimeout2'), 0);
});
await promise1;
console.log('E: await 后续代码');
Promise.resolve().then(() => console.log('G: Promise.then2'));
console.log('C: 函数结束');
}
complexEventLoop();
// 输出分析:
// A: 函数开始 (同步)
// B: Promise 构造函数 (同步)
// C: 函数结束 (同步)
// D: Promise.then1 (微任务)
// E: await 后续代码 (微任务)
// G: Promise.then2 (微任务)
// F: setTimeout1 (宏任务)
// H: setTimeout2 (宏任务)
不同环境下的差异:
浏览器 vs Node.js:
// 浏览器环境
setTimeout(() => console.log('setTimeout1'), 0);
setImmediate(() => console.log('setImmediate1')); // 浏览器不支持
process.nextTick(() => console.log('nextTick1')); // 浏览器不支持
Promise.resolve().then(() => console.log('Promise1'));
// Node.js 环境特有的执行顺序
// Node.js 有更复杂的事件循环阶段:
// 1. Timer 阶段:setTimeout, setInterval
// 2. I/O callbacks 阶段
// 3. idle, prepare 阶段
// 4. Poll 阶段:新的 I/O 事件
// 5. Check 阶段:setImmediate
// 6. Close callbacks 阶段
// Node.js 微任务优先级:process.nextTick > Promise.then
Node.js 特殊情况:
// Node.js 环境
console.log('开始');
process.nextTick(() => {
console.log('nextTick 1');
process.nextTick(() => console.log('nextTick 2'));
});
Promise.resolve().then(() => {
console.log('Promise 1');
Promise.resolve().then(() => console.log('Promise 2'));
});
setTimeout(() => console.log('setTimeout'), 0);
setImmediate(() => console.log('setImmediate'));
console.log('结束');
// Node.js 输出:
// 开始
// 结束
// nextTick 1
// nextTick 2
// Promise 1
// Promise 2
// setTimeout 或 setImmediate (顺序可能变化)
实际应用场景:
性能优化场景:
// 使用微任务优化 DOM 操作
function optimizedDOMUpdate() {
const updates = [];
function scheduleUpdate(callback) {
updates.push(callback);
if (updates.length === 1) {
// 使用微任务批量处理 DOM 更新
queueMicrotask(() => {
const currentUpdates = [...updates];
updates.length = 0;
// 批量执行 DOM 操作
currentUpdates.forEach(update => update());
});
}
}
return scheduleUpdate;
}
const scheduleUpdate = optimizedDOMUpdate();
// 多个 DOM 更新会被批量处理
scheduleUpdate(() => document.getElementById('el1').textContent = 'A');
scheduleUpdate(() => document.getElementById('el2').textContent = 'B');
scheduleUpdate(() => document.getElementById('el3').textContent = 'C');
异步流程控制:
// 控制异步操作的执行时机
class AsyncScheduler {
constructor() {
this.tasks = [];
this.running = false;
}
addTask(task) {
this.tasks.push(task);
if (!this.running) {
this.runTasks();
}
}
runTasks() {
if (this.tasks.length === 0) {
this.running = false;
return;
}
this.running = true;
// 使用微任务确保任务按顺序执行
queueMicrotask(() => {
const task = this.tasks.shift();
Promise.resolve(task()).then(() => {
// 任务完成后继续执行下一个
this.runTasks();
}).catch(error => {
console.error('Task failed:', error);
this.runTasks();
});
});
}
}
const scheduler = new AsyncScheduler();
scheduler.addTask(async () => {
console.log('Task 1');
await new Promise(resolve => setTimeout(resolve, 100));
});
scheduler.addTask(async () => {
console.log('Task 2');
await new Promise(resolve => setTimeout(resolve, 50));
});
调试和监控:
// 监控事件循环性能
class EventLoopMonitor {
constructor() {
this.metrics = {
macroTaskCount: 0,
microTaskCount: 0,
averageDelay: 0
};
}
measureMacroTaskDelay() {
const start = performance.now();
setTimeout(() => {
const delay = performance.now() - start;
this.metrics.macroTaskCount++;
this.updateAverageDelay(delay);
console.log(`宏任务延迟: ${delay.toFixed(2)}ms`);
}, 0);
}
measureMicroTaskDelay() {
const start = performance.now();
queueMicrotask(() => {
const delay = performance.now() - start;
this.metrics.microTaskCount++;
console.log(`微任务延迟: ${delay.toFixed(2)}ms`);
});
}
updateAverageDelay(delay) {
const count = this.metrics.macroTaskCount;
this.metrics.averageDelay =
(this.metrics.averageDelay * (count - 1) + delay) / count;
}
getMetrics() {
return { ...this.metrics };
}
}
const monitor = new EventLoopMonitor();
// 测试事件循环性能
setInterval(() => {
monitor.measureMacroTaskDelay();
monitor.measureMicroTaskDelay();
}, 1000);
最佳实践和注意事项:
常见面试陷阱:
// 经典面试题
for (var i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 0); // 输出:3, 3, 3
}
for (let j = 0; j < 3; j++) {
setTimeout(() => console.log(j), 0); // 输出:0, 1, 2
}
// 解决方案
for (var k = 0; k < 3; k++) {
((index) => {
setTimeout(() => console.log(index), 0); // 输出:0, 1, 2
})(k);
}
How to implement a complete Promise/A+ specification compliant Promise?
How to implement a complete Promise/A+ specification compliant Promise?
考察点:状态管理与链式调用。
答案:
Promise/A+ 规范是现代 JavaScript Promise 的标准实现规范。实现一个完整的 Promise 需要深入理解状态管理、链式调用、异常处理等核心概念。
Promise/A+ 规范核心要点:
基础状态和术语:
// Promise 的三种状态
const PENDING = 'pending'; // 待定状态
const FULFILLED = 'fulfilled'; // 已完成状态
const REJECTED = 'rejected'; // 已拒绝状态
// Promise 的基本结构
class MyPromise {
constructor(executor) {
this.state = PENDING; // 当前状态
this.value = undefined; // 完成时的值
this.reason = undefined; // 拒绝时的原因
this.onFulfilledCallbacks = []; // 成功回调队列
this.onRejectedCallbacks = []; // 失败回调队列
// 执行 executor
try {
executor(this.resolve.bind(this), this.reject.bind(this));
} catch (error) {
this.reject(error);
}
}
}
状态转换方法:
class MyPromise {
// ... 构造函数
resolve(value) {
// 状态只能从 pending 转换,且只能转换一次
if (this.state === PENDING) {
this.state = FULFILLED;
this.value = value;
// 执行所有成功回调
this.onFulfilledCallbacks.forEach(callback => {
callback(value);
});
}
}
reject(reason) {
// 状态只能从 pending 转换,且只能转换一次
if (this.state === PENDING) {
this.state = REJECTED;
this.reason = reason;
// 执行所有失败回调
this.onRejectedCallbacks.forEach(callback => {
callback(reason);
});
}
}
}
完整的 Promise/A+ 实现:
核心 then 方法实现:
class MyPromise {
// ... 前面的代码
then(onFulfilled, onRejected) {
// 参数校验和默认值处理
const realOnFulfilled = typeof onFulfilled === 'function'
? onFulfilled
: value => value;
const realOnRejected = typeof onRejected === 'function'
? onRejected
: reason => { throw reason; };
// 返回新的 Promise 实现链式调用
const promise2 = new MyPromise((resolve, reject) => {
const fulfilledMicrotask = () => {
// 使用微任务确保异步执行
queueMicrotask(() => {
try {
const x = realOnFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch (error) {
reject(error);
}
});
};
const rejectedMicrotask = () => {
queueMicrotask(() => {
try {
const x = realOnRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (error) {
reject(error);
}
});
};
switch (this.state) {
case FULFILLED:
fulfilledMicrotask();
break;
case REJECTED:
rejectedMicrotask();
break;
case PENDING:
// 添加到回调队列
this.onFulfilledCallbacks.push(fulfilledMicrotask);
this.onRejectedCallbacks.push(rejectedMicrotask);
break;
}
});
return promise2;
}
}
Promise Resolution Procedure(关键算法):
// Promise/A+ 规范的核心解析算法
function resolvePromise(promise2, x, resolve, reject) {
// 2.3.1: 如果 promise2 和 x 指向同一个对象,抛出 TypeError
if (promise2 === x) {
return reject(new TypeError('Chaining cycle detected for promise'));
}
// 2.3.2: 如果 x 是一个 Promise
if (x instanceof MyPromise) {
if (x.state === PENDING) {
x.then(value => {
resolvePromise(promise2, value, resolve, reject);
}, reject);
} else {
x.then(resolve, reject);
}
return;
}
// 2.3.3: 如果 x 是对象或函数
if ((typeof x === 'object' && x !== null) || typeof x === 'function') {
let called = false; // 防止重复调用
try {
// 2.3.3.1: 获取 x.then
const then = x.then;
// 2.3.3.3: 如果 then 是函数
if (typeof then === 'function') {
then.call(
x,
// 2.3.3.3.1: resolvePromise
y => {
if (called) return;
called = true;
resolvePromise(promise2, y, resolve, reject);
},
// 2.3.3.3.2: rejectPromise
r => {
if (called) return;
called = true;
reject(r);
}
);
} else {
// 2.3.3.4: 如果 then 不是函数,以 x 为值解决
resolve(x);
}
} catch (error) {
// 2.3.3.2: 如果检索属性 x.then 抛出异常
if (called) return;
called = true;
reject(error);
}
} else {
// 2.3.4: 如果 x 不是对象或函数,以 x 为值解决
resolve(x);
}
}
完整的 MyPromise 类:
class MyPromise {
constructor(executor) {
this.state = PENDING;
this.value = undefined;
this.reason = undefined;
this.onFulfilledCallbacks = [];
this.onRejectedCallbacks = [];
const resolve = (value) => {
if (this.state === PENDING) {
this.state = FULFILLED;
this.value = value;
this.onFulfilledCallbacks.forEach(callback => callback());
}
};
const reject = (reason) => {
if (this.state === PENDING) {
this.state = REJECTED;
this.reason = reason;
this.onRejectedCallbacks.forEach(callback => callback());
}
};
try {
executor(resolve, reject);
} catch (error) {
reject(error);
}
}
then(onFulfilled, onRejected) {
const realOnFulfilled = typeof onFulfilled === 'function'
? onFulfilled : value => value;
const realOnRejected = typeof onRejected === 'function'
? onRejected : reason => { throw reason; };
const promise2 = new MyPromise((resolve, reject) => {
const fulfilledMicrotask = () => {
queueMicrotask(() => {
try {
const x = realOnFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch (error) {
reject(error);
}
});
};
const rejectedMicrotask = () => {
queueMicrotask(() => {
try {
const x = realOnRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (error) {
reject(error);
}
});
};
switch (this.state) {
case FULFILLED:
fulfilledMicrotask();
break;
case REJECTED:
rejectedMicrotask();
break;
case PENDING:
this.onFulfilledCallbacks.push(fulfilledMicrotask);
this.onRejectedCallbacks.push(rejectedMicrotask);
break;
}
});
return promise2;
}
catch(onRejected) {
return this.then(null, onRejected);
}
finally(onFinally) {
return this.then(
value => MyPromise.resolve(onFinally()).then(() => value),
reason => MyPromise.resolve(onFinally()).then(() => { throw reason; })
);
}
}
静态方法实现:
Promise.resolve 和 Promise.reject:
MyPromise.resolve = function(value) {
// 如果是 MyPromise 实例,直接返回
if (value instanceof MyPromise) {
return value;
}
return new MyPromise(resolve => resolve(value));
};
MyPromise.reject = function(reason) {
return new MyPromise((resolve, reject) => reject(reason));
};
Promise.all 实现:
MyPromise.all = function(promises) {
return new MyPromise((resolve, reject) => {
if (!Array.isArray(promises)) {
return reject(new TypeError('Argument must be an array'));
}
const results = [];
let completedCount = 0;
const total = promises.length;
if (total === 0) {
return resolve([]);
}
promises.forEach((promise, index) => {
MyPromise.resolve(promise).then(
value => {
results[index] = value;
completedCount++;
if (completedCount === total) {
resolve(results);
}
},
reason => {
reject(reason);
}
);
});
});
};
Promise.race 实现:
MyPromise.race = function(promises) {
return new MyPromise((resolve, reject) => {
if (!Array.isArray(promises)) {
return reject(new TypeError('Argument must be an array'));
}
promises.forEach(promise => {
MyPromise.resolve(promise).then(resolve, reject);
});
});
};
Promise.allSettled 实现:
MyPromise.allSettled = function(promises) {
return new MyPromise((resolve) => {
if (!Array.isArray(promises)) {
return resolve([]);
}
const results = [];
let completedCount = 0;
const total = promises.length;
if (total === 0) {
return resolve([]);
}
promises.forEach((promise, index) => {
MyPromise.resolve(promise).then(
value => {
results[index] = { status: 'fulfilled', value };
completedCount++;
if (completedCount === total) resolve(results);
},
reason => {
results[index] = { status: 'rejected', reason };
completedCount++;
if (completedCount === total) resolve(results);
}
);
});
});
};
测试和验证:
// 测试基本功能
function testMyPromise() {
console.log('=== MyPromise 测试 ===');
// 1. 基本 resolve
new MyPromise(resolve => {
setTimeout(() => resolve('success'), 100);
}).then(value => {
console.log('Test 1 passed:', value); // success
});
// 2. 基本 reject
new MyPromise((resolve, reject) => {
setTimeout(() => reject('error'), 100);
}).catch(reason => {
console.log('Test 2 passed:', reason); // error
});
// 3. 链式调用
new MyPromise(resolve => resolve(1))
.then(value => value + 1)
.then(value => value * 2)
.then(value => {
console.log('Test 3 passed:', value); // 4
});
// 4. 异常处理
new MyPromise(resolve => resolve(1))
.then(() => {
throw new Error('Test error');
})
.catch(error => {
console.log('Test 4 passed:', error.message); // Test error
});
// 5. Promise.all
MyPromise.all([
MyPromise.resolve(1),
MyPromise.resolve(2),
MyPromise.resolve(3)
]).then(values => {
console.log('Test 5 passed:', values); // [1, 2, 3]
});
// 6. 循环引用检测
const promise = new MyPromise(resolve => resolve());
const chainedPromise = promise.then(() => chainedPromise);
chainedPromise.catch(error => {
console.log('Test 6 passed:', error.message); // Chaining cycle detected
});
}
// 运行测试
testMyPromise();
性能优化和实际考虑:
class OptimizedPromise extends MyPromise {
constructor(executor) {
super(executor);
// 状态确定后清理回调队列,释放内存
const originalResolve = this.resolve;
const originalReject = this.reject;
this.resolve = (value) => {
originalResolve.call(this, value);
this.onFulfilledCallbacks.length = 0;
this.onRejectedCallbacks.length = 0;
};
this.reject = (reason) => {
originalReject.call(this, reason);
this.onFulfilledCallbacks.length = 0;
this.onRejectedCallbacks.length = 0;
};
}
}
与原生 Promise 的兼容性:
queueMicrotask 保证正确的执行顺序What are the underlying principles of Generator and async/await?
What are the underlying principles of Generator and async/await?
考察点:协程、自动化流程控制。
答案:
Generator 和 async/await 是 JavaScript 中处理异步编程的重要工具。Generator 是 ES6 引入的协程机制,而 async/await 是基于 Generator 和 Promise 的语法糖,它们共同解决了回调地狱和异步流程控制的问题。
Generator 的底层原理:
Generator 函数的执行机制:
// Generator 函数定义
function* simpleGenerator() {
console.log('开始执行');
const a = yield 1;
console.log('第一次恢复,接收到:', a);
const b = yield 2;
console.log('第二次恢复,接收到:', b);
return 3;
}
// Generator 对象创建和执行
const gen = simpleGenerator(); // 不会立即执行
console.log(gen.next()); // { value: 1, done: false }
console.log(gen.next('hello')); // { value: 2, done: false }
console.log(gen.next('world')); // { value: 3, done: true }
// 执行流程:
// 1. 调用 simpleGenerator() 返回 Generator 对象
// 2. 第一次 next() 执行到第一个 yield,暂停并返回 1
// 3. 第二次 next('hello') 从暂停处恢复,'hello' 作为 yield 1 的返回值
// 4. 执行到第二个 yield,暂停并返回 2
// 5. 第三次 next('world') 恢复执行,'world' 作为 yield 2 的返回值
// 6. 执行到 return,结束并返回 3
Generator 的状态机模型:
// 手动实现 Generator 的状态机逻辑
function createGeneratorStateMachine() {
let state = 0;
let lastValue;
return {
next(value) {
switch (state) {
case 0:
console.log('开始执行');
state = 1;
return { value: 1, done: false };
case 1:
lastValue = value;
console.log('第一次恢复,接收到:', lastValue);
state = 2;
return { value: 2, done: false };
case 2:
lastValue = value;
console.log('第二次恢复,接收到:', lastValue);
state = 3;
return { value: 3, done: true };
default:
return { value: undefined, done: true };
}
},
[Symbol.iterator]() {
return this;
}
};
}
// 使用自定义状态机
const customGen = createGeneratorStateMachine();
console.log(customGen.next()); // { value: 1, done: false }
console.log(customGen.next('hello')); // { value: 2, done: false }
console.log(customGen.next('world')); // { value: 3, done: true }
Generator 的异步应用:
// 使用 Generator 处理异步操作
function* asyncGenerator() {
try {
const data1 = yield fetch('/api/data1');
console.log('第一次数据:', data1);
const data2 = yield fetch(`/api/data2/${data1.id}`);
console.log('第二次数据:', data2);
const result = yield processData(data1, data2);
return result;
} catch (error) {
console.error('异步操作失败:', error);
throw error;
}
}
// 手动执行器(类似 co 库的实现)
function runGenerator(generatorFunction) {
const gen = generatorFunction();
function handle(result) {
if (result.done) {
return Promise.resolve(result.value);
}
return Promise.resolve(result.value)
.then(
value => handle(gen.next(value)),
error => handle(gen.throw(error))
);
}
return handle(gen.next());
}
// 使用
runGenerator(asyncGenerator)
.then(result => console.log('最终结果:', result))
.catch(error => console.error('执行失败:', error));
async/await 的底层原理:
async/await 的 Generator + Promise 实现:
// async/await 的等价 Generator 实现
// 原始 async 函数
async function fetchUserData(userId) {
try {
const user = await fetch(`/api/users/${userId}`);
const profile = await fetch(`/api/profiles/${user.id}`);
return { user, profile };
} catch (error) {
console.error('获取用户数据失败:', error);
throw error;
}
}
// 等价的 Generator 实现
function* fetchUserDataGenerator(userId) {
try {
const user = yield fetch(`/api/users/${userId}`);
const profile = yield fetch(`/api/profiles/${user.id}`);
return { user, profile };
} catch (error) {
console.error('获取用户数据失败:', error);
throw error;
}
}
// 自动执行器(async 函数的核心机制)
function asyncWrapper(generatorFunction) {
return function(...args) {
const gen = generatorFunction.apply(this, args);
return new Promise((resolve, reject) => {
function step(nextF) {
let next;
try {
next = nextF();
} catch (e) {
return reject(e);
}
if (next.done) {
return resolve(next.value);
}
Promise.resolve(next.value).then(
v => step(() => gen.next(v)),
e => step(() => gen.throw(e))
);
}
step(() => gen.next(undefined));
});
};
}
// 将 Generator 函数转换为 async 函数
const fetchUserDataAsync = asyncWrapper(fetchUserDataGenerator);
// 使用效果完全一致
fetchUserDataAsync(123)
.then(result => console.log(result))
.catch(error => console.error(error));
深入理解 await 的执行机制:
// await 的执行步骤分析
async function awaitAnalysis() {
console.log('1: 函数开始执行');
// await 会暂停函数执行,将后续代码包装成微任务
const result = await Promise.resolve('异步结果');
console.log('3: await 后的代码', result);
return '函数完成';
}
console.log('0: 调用 async 函数之前');
const promise = awaitAnalysis();
console.log('2: async 函数调用后,await 之后的代码被暂停');
promise.then(result => {
console.log('4: async 函数完成', result);
});
// 输出顺序:
// 0: 调用 async 函数之前
// 1: 函数开始执行
// 2: async 函数调用后,await 之后的代码被暂停
// 3: await 后的代码 异步结果
// 4: async 函数完成 函数完成
高级应用和模式:
实现自定义的 async/await:
// 完整的 async/await 实现
class AsyncFunction {
constructor(generatorFunction) {
this.generatorFunction = generatorFunction;
}
async execute(...args) {
const generator = this.generatorFunction.apply(this, args);
let result = generator.next();
while (!result.done) {
try {
// 等待当前 yield 的值
const value = await Promise.resolve(result.value);
result = generator.next(value);
} catch (error) {
// 将错误传递给 Generator
result = generator.throw(error);
}
}
return result.value;
}
// 使其可以像 async 函数一样调用
static create(generatorFunction) {
const asyncFn = new AsyncFunction(generatorFunction);
return (...args) => asyncFn.execute(...args);
}
}
// 使用自定义 async/await
const customAsync = AsyncFunction.create(function* (url) {
const response = yield fetch(url);
const data = yield response.json();
return data;
});
customAsync('/api/data')
.then(data => console.log('数据:', data))
.catch(error => console.error('错误:', error));
Generator 的高级模式 - 双向通信:
// Generator 的双向数据流
function* bidirectionalGenerator() {
let received = yield 'first';
console.log('接收到:', received);
let calculated = received * 2;
received = yield calculated;
console.log('再次接收到:', received);
return received + calculated;
}
function runBidirectional() {
const gen = bidirectionalGenerator();
let result = gen.next(); // 开始执行
console.log('第一次输出:', result.value); // 'first'
result = gen.next(10); // 发送 10
console.log('第二次输出:', result.value); // 20
result = gen.next(5); // 发送 5
console.log('最终结果:', result.value); // 25
}
runBidirectional();
协程式的并发控制:
// 使用 Generator 实现协程调度
class CoroutineScheduler {
constructor() {
this.tasks = [];
this.running = false;
}
spawn(generatorFunction, ...args) {
const task = {
id: Date.now() + Math.random(),
generator: generatorFunction(...args),
completed: false
};
this.tasks.push(task);
if (!this.running) {
this.run();
}
return task.id;
}
async run() {
this.running = true;
while (this.tasks.length > 0) {
for (let i = this.tasks.length - 1; i >= 0; i--) {
const task = this.tasks[i];
try {
const result = task.generator.next();
if (result.done) {
console.log(`任务 ${task.id} 完成:`, result.value);
this.tasks.splice(i, 1);
} else if (result.value instanceof Promise) {
// 如果 yield 了一个 Promise,等待它
await result.value;
}
} catch (error) {
console.error(`任务 ${task.id} 错误:`, error);
this.tasks.splice(i, 1);
}
}
// 让出执行权,允许其他代码执行
await new Promise(resolve => setTimeout(resolve, 0));
}
this.running = false;
}
}
// 使用协程调度器
const scheduler = new CoroutineScheduler();
function* task1() {
console.log('任务1: 步骤1');
yield new Promise(resolve => setTimeout(resolve, 100));
console.log('任务1: 步骤2');
yield;
console.log('任务1: 完成');
return 'task1 result';
}
function* task2() {
console.log('任务2: 步骤1');
yield;
console.log('任务2: 步骤2');
yield new Promise(resolve => setTimeout(resolve, 50));
console.log('任务2: 完成');
return 'task2 result';
}
scheduler.spawn(task1);
scheduler.spawn(task2);
性能和内存考虑:
// Generator 的惰性求值和内存效率
function* infiniteSequence() {
let i = 0;
while (true) {
yield i++;
}
}
// 只在需要时计算值,不会占用大量内存
function* fibonacciGenerator() {
let [a, b] = [0, 1];
while (true) {
yield a;
[a, b] = [b, a + b];
}
}
// 高效的数据处理管道
function* map(iterable, mapper) {
for (const item of iterable) {
yield mapper(item);
}
}
function* filter(iterable, predicate) {
for (const item of iterable) {
if (predicate(item)) {
yield item;
}
}
}
function* take(iterable, count) {
let taken = 0;
for (const item of iterable) {
if (taken >= count) break;
yield item;
taken++;
}
}
// 构建高效的数据处理链
const result = [...take(
filter(
map(fibonacciGenerator(), x => x * 2),
x => x % 4 === 0
),
5
)];
console.log(result); // [0, 4, 16, 40, 104]
调试和错误处理:
// Generator 错误处理
function* generatorWithError() {
try {
const result1 = yield Promise.reject('第一个错误');
} catch (error) {
console.log('捕获到错误:', error);
const result2 = yield Promise.resolve('恢复执行');
return result2;
}
}
// async/await 错误处理
async function asyncWithError() {
try {
const result1 = await Promise.reject('第一个错误');
} catch (error) {
console.log('捕获到错误:', error);
const result2 = await Promise.resolve('恢复执行');
return result2;
}
}
// 两者在错误处理上的行为基本一致
最佳实践和使用建议:
总结:
Generator 是基于协程的底层机制,提供了函数执行的暂停和恢复能力。async/await 是基于 Generator 和 Promise 的高级抽象,提供了更直观的异步编程体验。两者都解决了异步流程控制的问题,但 async/await 因其简洁性和直观性成为了现代 JavaScript 异步编程的首选方案。
What advanced scenarios can Proxy implement? Please give examples.
What advanced scenarios can Proxy implement? Please give examples.
考察点:数据劫持、响应式、AOP。
答案:
Proxy 是 ES6 引入的强大元编程工具,它允许我们拦截并自定义对象的基本操作(如属性查找、赋值、枚举、函数调用等)。Proxy 可以实现许多高级编程模式和框架级功能。
1. 响应式系统实现(Vue3 风格):
// 简化版的 Vue3 响应式系统
class ReactiveSystem {
constructor() {
this.activeEffect = null;
this.targetMap = new WeakMap();
}
// 依赖收集
track(target, key) {
if (!this.activeEffect) return;
let depsMap = this.targetMap.get(target);
if (!depsMap) {
this.targetMap.set(target, (depsMap = new Map()));
}
let dep = depsMap.get(key);
if (!dep) {
depsMap.set(key, (dep = new Set()));
}
dep.add(this.activeEffect);
}
// 触发更新
trigger(target, key) {
const depsMap = this.targetMap.get(target);
if (!depsMap) return;
const dep = depsMap.get(key);
if (dep) {
dep.forEach(effect => effect());
}
}
// 创建响应式对象
reactive(target) {
return new Proxy(target, {
get(obj, key) {
const result = Reflect.get(obj, key);
this.track(obj, key);
// 如果值是对象,递归创建响应式
if (typeof result === 'object' && result !== null) {
return this.reactive(result);
}
return result;
},
set(obj, key, value) {
const oldValue = obj[key];
const result = Reflect.set(obj, key, value);
if (oldValue !== value) {
this.trigger(obj, key);
}
return result;
}
});
}
// 副作用函数
effect(fn) {
const effect = () => {
this.activeEffect = effect;
try {
return fn();
} finally {
this.activeEffect = null;
}
};
effect();
return effect;
}
}
// 使用响应式系统
const reactiveSystem = new ReactiveSystem();
const state = reactiveSystem.reactive({
count: 0,
user: {
name: 'Alice',
age: 25
}
});
// 创建副作用
reactiveSystem.effect(() => {
console.log(`Count: ${state.count}`);
});
reactiveSystem.effect(() => {
console.log(`User: ${state.user.name}, Age: ${state.user.age}`);
});
// 触发更新
state.count++; // 输出: Count: 1
state.user.name = 'Bob'; // 输出: User: Bob, Age: 25
2. AOP(面向切面编程)实现:
// AOP 切面编程框架
class AOPProxy {
constructor(target) {
this.target = target;
this.beforeHooks = new Map();
this.afterHooks = new Map();
this.aroundHooks = new Map();
return new Proxy(target, {
get: (obj, prop) => {
const value = obj[prop];
if (typeof value === 'function') {
return this.wrapMethod(prop, value);
}
return value;
}
});
}
wrapMethod(methodName, originalMethod) {
return (...args) => {
const context = { methodName, args, target: this.target };
try {
// 执行前置钩子
this.executeHooks(this.beforeHooks, methodName, context);
let result;
const aroundHooks = this.aroundHooks.get(methodName) || [];
if (aroundHooks.length > 0) {
// 使用环绕钩子
result = this.executeAroundHooks(aroundHooks, originalMethod, context);
} else {
// 直接执行原方法
result = originalMethod.apply(this.target, args);
}
context.result = result;
// 执行后置钩子
this.executeHooks(this.afterHooks, methodName, context);
return result;
} catch (error) {
context.error = error;
this.executeHooks(this.afterHooks, methodName, context);
throw error;
}
};
}
executeHooks(hookMap, methodName, context) {
const hooks = hookMap.get(methodName) || [];
hooks.forEach(hook => hook(context));
}
executeAroundHooks(hooks, originalMethod, context) {
let index = 0;
const proceed = () => {
if (index < hooks.length) {
return hooks[index++](context, proceed);
} else {
return originalMethod.apply(context.target, context.args);
}
};
return proceed();
}
// 添加前置钩子
before(methodName, hook) {
if (!this.beforeHooks.has(methodName)) {
this.beforeHooks.set(methodName, []);
}
this.beforeHooks.get(methodName).push(hook);
return this;
}
// 添加后置钩子
after(methodName, hook) {
if (!this.afterHooks.has(methodName)) {
this.afterHooks.set(methodName, []);
}
this.afterHooks.get(methodName).push(hook);
return this;
}
// 添加环绕钩子
around(methodName, hook) {
if (!this.aroundHooks.has(methodName)) {
this.aroundHooks.set(methodName, []);
}
this.aroundHooks.get(methodName).push(hook);
return this;
}
}
// 使用示例
class UserService {
getUser(id) {
console.log(`获取用户 ${id}`);
return { id, name: `User${id}` };
}
updateUser(id, data) {
console.log(`更新用户 ${id}:`, data);
return { id, ...data };
}
}
const userService = new AOPProxy(new UserService())
.before('getUser', (context) => {
console.log('前置: 验证权限');
console.log('前置: 记录日志');
})
.after('getUser', (context) => {
console.log('后置: 缓存结果');
console.log('后置: 性能监控');
})
.around('updateUser', (context, proceed) => {
console.log('环绕: 开始事务');
try {
const result = proceed();
console.log('环绕: 提交事务');
return result;
} catch (error) {
console.log('环绕: 回滚事务');
throw error;
}
});
// 调用被增强的方法
const user = userService.getUser(123);
userService.updateUser(123, { name: 'Updated Name' });
3. 数据校验和类型检查:
// 强类型数据校验代理
class TypedObject {
constructor(schema) {
this.schema = schema;
this.data = {};
return new Proxy(this.data, {
set: (target, key, value) => {
this.validate(key, value);
return Reflect.set(target, key, value);
},
get: (target, key) => {
if (key === 'toJSON') {
return () => ({ ...target });
}
return Reflect.get(target, key);
},
has: (target, key) => {
return key in this.schema || Reflect.has(target, key);
},
ownKeys: (target) => {
return [...Object.keys(this.schema), ...Reflect.ownKeys(target)];
}
});
}
validate(key, value) {
const fieldSchema = this.schema[key];
if (!fieldSchema) {
throw new Error(`未知字段: ${key}`);
}
// 类型检查
if (fieldSchema.type && typeof value !== fieldSchema.type) {
throw new TypeError(`字段 ${key} 期望类型 ${fieldSchema.type},实际类型 ${typeof value}`);
}
// 必填检查
if (fieldSchema.required && (value === undefined || value === null)) {
throw new Error(`字段 ${key} 是必填的`);
}
// 自定义校验器
if (fieldSchema.validator && !fieldSchema.validator(value)) {
throw new Error(`字段 ${key} 校验失败`);
}
// 范围检查
if (fieldSchema.min !== undefined && value < fieldSchema.min) {
throw new Error(`字段 ${key} 不能小于 ${fieldSchema.min}`);
}
if (fieldSchema.max !== undefined && value > fieldSchema.max) {
throw new Error(`字段 ${key} 不能大于 ${fieldSchema.max}`);
}
}
}
// 使用类型化对象
const userSchema = {
id: { type: 'number', required: true },
name: {
type: 'string',
required: true,
validator: (value) => value.length >= 2
},
age: {
type: 'number',
min: 0,
max: 150
},
email: {
type: 'string',
validator: (value) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value)
}
};
const user = new TypedObject(userSchema);
try {
user.id = 123;
user.name = 'Alice';
user.age = 25;
user.email = '[email protected]';
console.log('用户数据:', user.toJSON());
// user.age = 200; // 抛出错误: 字段 age 不能大于 150
// user.email = 'invalid'; // 抛出错误: 字段 email 校验失败
} catch (error) {
console.error(error.message);
}
4. 自动持久化和缓存系统:
// 自动持久化代理
class PersistentObject {
constructor(key, initialData = {}) {
this.storageKey = key;
this.data = this.loadFromStorage() || initialData;
this.debounceTimer = null;
return new Proxy(this.data, {
set: (target, prop, value) => {
const result = Reflect.set(target, prop, value);
this.scheduleSave();
return result;
},
deleteProperty: (target, prop) => {
const result = Reflect.deleteProperty(target, prop);
this.scheduleSave();
return result;
}
});
}
loadFromStorage() {
try {
const stored = localStorage.getItem(this.storageKey);
return stored ? JSON.parse(stored) : null;
} catch (error) {
console.error('加载数据失败:', error);
return null;
}
}
saveToStorage() {
try {
localStorage.setItem(this.storageKey, JSON.stringify(this.data));
console.log(`数据已保存到 ${this.storageKey}`);
} catch (error) {
console.error('保存数据失败:', error);
}
}
scheduleSave() {
// 防抖保存,避免频繁写入
if (this.debounceTimer) {
clearTimeout(this.debounceTimer);
}
this.debounceTimer = setTimeout(() => {
this.saveToStorage();
}, 500);
}
}
// 使用自动持久化对象
const settings = new PersistentObject('app-settings', {
theme: 'light',
language: 'zh-CN',
notifications: true
});
// 自动保存到 localStorage
settings.theme = 'dark';
settings.autoSave = true;
delete settings.notifications;
5. API 代理和模拟:
// API 代理,支持缓存、重试、模拟等功能
class APIProxy {
constructor(baseURL, options = {}) {
this.baseURL = baseURL;
this.cache = new Map();
this.mockData = options.mockData || {};
this.retryCount = options.retryCount || 3;
this.timeout = options.timeout || 5000;
return new Proxy({}, {
get: (target, prop) => {
return this.createAPIMethod(prop);
}
});
}
createAPIMethod(endpoint) {
return async (options = {}) => {
const {
method = 'GET',
params = {},
data = null,
useCache = true,
useMock = false
} = options;
const cacheKey = `${method}:${endpoint}:${JSON.stringify(params)}`;
// 检查缓存
if (useCache && this.cache.has(cacheKey)) {
console.log('从缓存返回:', cacheKey);
return this.cache.get(cacheKey);
}
// 使用模拟数据
if (useMock && this.mockData[endpoint]) {
console.log('使用模拟数据:', endpoint);
const mockResult = typeof this.mockData[endpoint] === 'function'
? this.mockData[endpoint](params, data)
: this.mockData[endpoint];
if (useCache) {
this.cache.set(cacheKey, mockResult);
}
return mockResult;
}
// 实际API调用
return this.makeRequest(endpoint, { method, params, data, cacheKey, useCache });
};
}
async makeRequest(endpoint, { method, params, data, cacheKey, useCache }) {
let attempt = 0;
while (attempt < this.retryCount) {
try {
const url = new URL(endpoint, this.baseURL);
// 添加查询参数
Object.entries(params).forEach(([key, value]) => {
url.searchParams.append(key, value);
});
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
const response = await fetch(url, {
method,
body: data ? JSON.stringify(data) : null,
headers: {
'Content-Type': 'application/json'
},
signal: controller.signal
});
clearTimeout(timeoutId);
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
const result = await response.json();
// 缓存成功结果
if (useCache) {
this.cache.set(cacheKey, result);
}
return result;
} catch (error) {
attempt++;
console.warn(`API调用失败 (尝试 ${attempt}/${this.retryCount}):`, error.message);
if (attempt >= this.retryCount) {
throw error;
}
// 等待后重试
await new Promise(resolve => setTimeout(resolve, 1000 * attempt));
}
}
}
}
// 使用API代理
const api = new APIProxy('https://api.example.com', {
mockData: {
users: [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' }
],
posts: (params) => ({
data: [`Post ${params.page || 1}`],
total: 100
})
},
retryCount: 2,
timeout: 3000
});
// 使用方式
async function testAPI() {
try {
// 使用模拟数据
const users = await api.users({ useMock: true });
console.log('用户列表:', users);
// 实际API调用(带缓存)
const posts = await api.posts({
params: { page: 1, limit: 10 },
useCache: true
});
console.log('帖子列表:', posts);
} catch (error) {
console.error('API调用失败:', error);
}
}
testAPI();
6. 无限嵌套对象代理:
// 深度代理,支持无限嵌套
class DeepProxy {
constructor(target, handler) {
this.handler = handler;
return this.createProxy(target);
}
createProxy(target) {
return new Proxy(target, {
get: (obj, prop) => {
const value = Reflect.get(obj, prop);
// 调用用户定义的get处理器
if (this.handler.get) {
const result = this.handler.get(obj, prop, value);
if (result !== undefined) return result;
}
// 如果值是对象,递归创建代理
if (value && typeof value === 'object') {
return this.createProxy(value);
}
return value;
},
set: (obj, prop, value) => {
// 调用用户定义的set处理器
if (this.handler.set) {
const result = this.handler.set(obj, prop, value);
if (result === false) return false;
}
return Reflect.set(obj, prop, value);
},
has: (obj, prop) => {
if (this.handler.has) {
return this.handler.has(obj, prop);
}
return Reflect.has(obj, prop);
}
});
}
}
// 使用深度代理实现路径访问
const deepObject = new DeepProxy({}, {
get: (obj, prop, value) => {
// 如果属性不存在,自动创建嵌套对象
if (value === undefined && typeof prop === 'string') {
obj[prop] = {};
return obj[prop];
}
},
set: (obj, prop, value) => {
console.log(`设置 ${prop} = ${value}`);
return true; // 继续默认行为
}
});
// 可以无限嵌套访问
deepObject.user.profile.personal.name = 'Alice';
deepObject.user.profile.settings.theme = 'dark';
console.log('深度对象:', JSON.stringify(deepObject, null, 2));
性能考虑和最佳实践:
// Proxy 性能监控装饰器
function withPerformanceMonitoring(target, options = {}) {
const stats = {
getCount: 0,
setCount: 0,
totalTime: 0
};
return new Proxy(target, {
get(obj, prop) {
if (prop === '__stats__') {
return stats;
}
const start = performance.now();
const result = Reflect.get(obj, prop);
const duration = performance.now() - start;
stats.getCount++;
stats.totalTime += duration;
if (options.logSlowAccess && duration > (options.threshold || 1)) {
console.warn(`慢速属性访问: ${String(prop)} 耗时 ${duration.toFixed(2)}ms`);
}
return result;
},
set(obj, prop, value) {
const start = performance.now();
const result = Reflect.set(obj, prop, value);
const duration = performance.now() - start;
stats.setCount++;
stats.totalTime += duration;
return result;
}
});
}
// 使用性能监控
const monitoredObject = withPerformanceMonitoring({
data: new Array(10000).fill(0).map((_, i) => ({ id: i, value: Math.random() }))
}, {
logSlowAccess: true,
threshold: 0.1
});
// 访问属性
monitoredObject.data.forEach(item => item.value);
console.log('性能统计:', monitoredObject.__stats__);
Proxy 的高级应用场景还包括:数据绑定、ORM 实现、动态接口适配、调试工具、安全沙箱等。它的强大之处在于能够透明地拦截和自定义对象操作,为元编程和框架开发提供了无限可能。
Advanced applications of Symbol: How to use it to implement singleton pattern and custom iterators?
考察点:Symbol的唯一性、隐藏属性、以及在设计模式中的应用。
答案:
Symbol 是 ES6 引入的全新原始数据类型,具有唯一性和不可枚举的特点。这些特性使得 Symbol 在实现高级编程模式、隐藏属性、元编程等场景中非常有用。
1. 使用 Symbol 实现单例模式:
// 基础的 Symbol 单例模式
const SingletonSymbol = Symbol('Singleton');
class DatabaseConnection {
constructor() {
// 检查是否已经有实例
if (DatabaseConnection[SingletonSymbol]) {
return DatabaseConnection[SingletonSymbol];
}
// 初始化实例
this.isConnected = false;
this.connectionCount = 0;
// 将实例保存到 Symbol 属性中
DatabaseConnection[SingletonSymbol] = this;
return this;
}
connect() {
if (!this.isConnected) {
this.isConnected = true;
this.connectionCount++;
console.log(`数据库连接建立 #${this.connectionCount}`);
}
return this;
}
disconnect() {
if (this.isConnected) {
this.isConnected = false;
console.log('数据库连接断开');
}
return this;
}
getStatus() {
return {
connected: this.isConnected,
connectionCount: this.connectionCount
};
}
}
// 测试单例模式
const db1 = new DatabaseConnection();
const db2 = new DatabaseConnection();
console.log(db1 === db2); // true - 同一个实例
db1.connect();
console.log(db2.getStatus()); // { connected: true, connectionCount: 1 }
2. 高级单例模式 - 多实例单例:
// 支持多种类型的单例管理器
class SingletonManager {
constructor() {
this.instances = new Map();
}
// 为每个类型创建唯一的 Symbol
createSingletonType(typeName) {
const typeSymbol = Symbol(`Singleton:${typeName}`);
return {
symbol: typeSymbol,
getInstance(Class, ...args) {
if (!this.instances.has(typeSymbol)) {
const instance = new Class(...args);
this.instances.set(typeSymbol, instance);
}
return this.instances.get(typeSymbol);
}.bind(this),
hasInstance() {
return this.instances.has(typeSymbol);
}.bind(this),
clearInstance() {
this.instances.delete(typeSymbol);
}.bind(this)
};
}
}
const singletonManager = new SingletonManager();
// 不同类型的单例
const dbSingleton = singletonManager.createSingletonType('Database');
const cacheSingleton = singletonManager.createSingletonType('Cache');
const loggerSingleton = singletonManager.createSingletonType('Logger');
class Database {
constructor(config) {
this.config = config;
console.log('Database 实例创建:', config);
}
}
class Cache {
constructor(size) {
this.size = size;
console.log('Cache 实例创建, 大小:', size);
}
}
// 使用多实例单例
const db1 = dbSingleton.getInstance(Database, { host: 'localhost' });
const db2 = dbSingleton.getInstance(Database, { host: 'remote' }); // 不会创建新实例
const cache1 = cacheSingleton.getInstance(Cache, 1000);
const cache2 = cacheSingleton.getInstance(Cache, 2000); // 不会创建新实例
console.log(db1 === db2); // true
console.log(cache1 === cache2); // true
console.log(db1 === cache1); // false - 不同类型的单例
3. 自定义迭代器的基础实现:
// 基础的自定义迭代器
class NumberRange {
constructor(start, end, step = 1) {
this.start = start;
this.end = end;
this.step = step;
}
// 实现 Symbol.iterator 方法
[Symbol.iterator]() {
let current = this.start;
const end = this.end;
const step = this.step;
return {
next() {
if (current < end) {
const value = current;
current += step;
return { value, done: false };
} else {
return { done: true };
}
}
};
}
}
// 使用自定义迭代器
const range = new NumberRange(1, 10, 2);
console.log('使用 for...of:');
for (const num of range) {
console.log(num); // 1, 3, 5, 7, 9
}
console.log('使用扩展运算符:');
console.log([...range]); // [1, 3, 5, 7, 9]
console.log('手动迭代:');
const iterator = range[Symbol.iterator]();
let result = iterator.next();
while (!result.done) {
console.log(result.value);
result = iterator.next();
}
4. 高级迭代器 - 可配置的迭代行为:
// 支持多种迭代模式的集合类
class AdvancedCollection {
constructor(data = []) {
this.data = [...data];
// 定义不同的迭代模式
this.iterationModes = {
[Symbol.for('forward')]: this.forwardIterator.bind(this),
[Symbol.for('backward')]: this.backwardIterator.bind(this),
[Symbol.for('random')]: this.randomIterator.bind(this),
[Symbol.for('filter')]: this.filterIterator.bind(this),
[Symbol.for('chunk')]: this.chunkIterator.bind(this)
};
}
// 默认迭代器(正向)
[Symbol.iterator]() {
return this.forwardIterator();
}
// 正向迭代器
forwardIterator() {
let index = 0;
const data = this.data;
return {
next() {
if (index < data.length) {
return { value: data[index++], done: false };
}
return { done: true };
}
};
}
// 反向迭代器
backwardIterator() {
let index = this.data.length - 1;
const data = this.data;
return {
next() {
if (index >= 0) {
return { value: data[index--], done: false };
}
return { done: true };
}
};
}
// 随机顺序迭代器
randomIterator() {
const shuffledData = [...this.data].sort(() => Math.random() - 0.5);
let index = 0;
return {
next() {
if (index < shuffledData.length) {
return { value: shuffledData[index++], done: false };
}
return { done: true };
}
};
}
// 过滤迭代器
filterIterator(predicate = () => true) {
const filteredData = this.data.filter(predicate);
let index = 0;
return {
next() {
if (index < filteredData.length) {
return { value: filteredData[index++], done: false };
}
return { done: true };
}
};
}
// 分块迭代器
chunkIterator(size = 2) {
let index = 0;
const data = this.data;
return {
next() {
if (index < data.length) {
const chunk = data.slice(index, index + size);
index += size;
return { value: chunk, done: false };
}
return { done: true };
}
};
}
// 获取指定模式的迭代器
iterate(mode, ...args) {
const iteratorFactory = this.iterationModes[Symbol.for(mode)];
if (iteratorFactory) {
return iteratorFactory(...args);
}
throw new Error(`未知的迭代模式: ${mode}`);
}
// 添加元素
add(item) {
this.data.push(item);
return this;
}
}
// 使用高级迭代器
const collection = new AdvancedCollection([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
console.log('正向迭代:');
for (const item of collection) {
console.log(item); // 1, 2, 3, ..., 10
}
console.log('反向迭代:');
const backwardIter = collection.iterate('backward');
let result = backwardIter.next();
while (!result.done) {
console.log(result.value); // 10, 9, 8, ..., 1
result = backwardIter.next();
}
console.log('过滤迭代(偶数):');
const evenIter = collection.iterate('filter', x => x % 2 === 0);
console.log([...{ [Symbol.iterator]: () => evenIter }]); // [2, 4, 6, 8, 10]
console.log('分块迭代(每块3个):');
const chunkIter = collection.iterate('chunk', 3);
result = chunkIter.next();
while (!result.done) {
console.log(result.value); // [1,2,3], [4,5,6], [7,8,9], [10]
result = chunkIter.next();
}
5. Symbol 在私有属性中的应用:
// 使用 Symbol 创建真正私有的属性
const createPrivateScope = () => {
// 私有 Symbol 键,外部无法访问
const _id = Symbol('id');
const _name = Symbol('name');
const _balance = Symbol('balance');
const _transactions = Symbol('transactions');
class BankAccount {
constructor(name, initialBalance = 0) {
this[_id] = Date.now() + Math.random();
this[_name] = name;
this[_balance] = initialBalance;
this[_transactions] = [];
}
// 公共方法
deposit(amount) {
if (amount <= 0) {
throw new Error('存款金额必须大于0');
}
this[_balance] += amount;
this[_transactions].push({
type: 'deposit',
amount,
timestamp: new Date(),
balance: this[_balance]
});
return this;
}
withdraw(amount) {
if (amount <= 0) {
throw new Error('取款金额必须大于0');
}
if (this[_balance] < amount) {
throw new Error('余额不足');
}
this[_balance] -= amount;
this[_transactions].push({
type: 'withdraw',
amount,
timestamp: new Date(),
balance: this[_balance]
});
return this;
}
getBalance() {
return this[_balance];
}
getAccountInfo() {
return {
id: this[_id],
name: this[_name],
balance: this[_balance],
transactionCount: this[_transactions].length
};
}
getTransactionHistory() {
// 返回交易记录的副本,防止外部修改
return this[_transactions].map(t => ({ ...t }));
}
// 私有方法(通过 Symbol 实现)
[Symbol.for('internal.validateAmount')](amount) {
return typeof amount === 'number' && amount > 0 && isFinite(amount);
}
}
return BankAccount;
};
const BankAccount = createPrivateScope();
const account = new BankAccount('Alice', 1000);
console.log('账户信息:', account.getAccountInfo());
account.deposit(500).withdraw(200);
console.log('余额:', account.getBalance());
console.log('交易历史:', account.getTransactionHistory());
// 尝试直接访问私有属性
console.log('直接访问私有属性:');
console.log(Object.keys(account)); // 空数组,看不到私有属性
console.log(Object.getOwnPropertyNames(account)); // 空数组
console.log(Object.getOwnPropertySymbols(account)); // 可以看到 Symbol 键,但无法使用
6. Symbol 在元编程中的应用:
// 使用 Symbol 创建元编程工具
class MetaProgrammingHelper {
constructor() {
// 创建元数据存储的 Symbol 键
this.META_KEY = Symbol('metadata');
this.HOOKS_KEY = Symbol('hooks');
this.OBSERVERS_KEY = Symbol('observers');
}
// 为对象添加元数据
addMetadata(target, key, value) {
if (!target[this.META_KEY]) {
target[this.META_KEY] = new Map();
}
target[this.META_KEY].set(key, value);
return this;
}
// 获取元数据
getMetadata(target, key) {
const meta = target[this.META_KEY];
return meta ? meta.get(key) : undefined;
}
// 添加生命周期钩子
addHook(target, event, callback) {
if (!target[this.HOOKS_KEY]) {
target[this.HOOKS_KEY] = new Map();
}
if (!target[this.HOOKS_KEY].has(event)) {
target[this.HOOKS_KEY].set(event, []);
}
target[this.HOOKS_KEY].get(event).push(callback);
return this;
}
// 触发钩子
triggerHook(target, event, ...args) {
const hooks = target[this.HOOKS_KEY];
if (hooks && hooks.has(event)) {
hooks.get(event).forEach(callback => {
try {
callback.call(target, ...args);
} catch (error) {
console.error(`钩子执行失败 [${event}]:`, error);
}
});
}
}
// 创建增强的类
enhance(Class) {
const self = this;
return class extends Class {
constructor(...args) {
super(...args);
// 初始化元编程能力
this[self.META_KEY] = new Map();
this[self.HOOKS_KEY] = new Map();
this[self.OBSERVERS_KEY] = new Set();
// 触发构造钩子
self.triggerHook(this, 'constructed', args);
}
// 添加元数据方法
setMeta(key, value) {
self.addMetadata(this, key, value);
self.triggerHook(this, 'metaChanged', key, value);
return this;
}
getMeta(key) {
return self.getMetadata(this, key);
}
// 添加钩子方法
on(event, callback) {
self.addHook(this, event, callback);
return this;
}
emit(event, ...args) {
self.triggerHook(this, event, ...args);
return this;
}
};
}
}
// 使用元编程助手
const metaHelper = new MetaProgrammingHelper();
class User {
constructor(name, email) {
this.name = name;
this.email = email;
}
updateEmail(newEmail) {
const oldEmail = this.email;
this.email = newEmail;
// 触发更新事件
if (this.emit) {
this.emit('emailUpdated', oldEmail, newEmail);
}
}
}
// 增强 User 类
const EnhancedUser = metaHelper.enhance(User);
const user = new EnhancedUser('Alice', '[email protected]');
// 添加元数据
user.setMeta('createdAt', new Date())
.setMeta('department', 'Engineering')
.setMeta('level', 'Senior');
// 添加事件监听器
user.on('constructed', (args) => {
console.log('用户创建:', args);
})
.on('emailUpdated', function(oldEmail, newEmail) {
console.log(`邮箱更新: ${oldEmail} -> ${newEmail}`);
this.setMeta('lastEmailUpdate', new Date());
})
.on('metaChanged', (key, value) => {
console.log(`元数据变更: ${key} = ${value}`);
});
// 测试增强功能
console.log('部门:', user.getMeta('department'));
user.updateEmail('[email protected]');
console.log('最后邮箱更新时间:', user.getMeta('lastEmailUpdate'));
7. Symbol 在迭代器协议中的高级应用:
// 实现支持多种迭代策略的树结构
class TreeNode {
constructor(value) {
this.value = value;
this.children = [];
this.parent = null;
}
addChild(child) {
if (child instanceof TreeNode) {
child.parent = this;
this.children.push(child);
}
return this;
}
// 深度优先迭代器
*[Symbol.iterator]() {
yield this.value;
for (const child of this.children) {
yield* child;
}
}
// 广度优先迭代器
*[Symbol.for('breadthFirst')]() {
const queue = [this];
while (queue.length > 0) {
const node = queue.shift();
yield node.value;
queue.push(...node.children);
}
}
// 叶子节点迭代器
*[Symbol.for('leaves')]() {
if (this.children.length === 0) {
yield this.value;
} else {
for (const child of this.children) {
yield* child[Symbol.for('leaves')]();
}
}
}
// 层级迭代器
*[Symbol.for('levels')]() {
let currentLevel = [this];
while (currentLevel.length > 0) {
const levelValues = currentLevel.map(node => node.value);
yield levelValues;
const nextLevel = [];
for (const node of currentLevel) {
nextLevel.push(...node.children);
}
currentLevel = nextLevel;
}
}
// 获取指定类型的迭代器
iterate(type = 'depth') {
const iteratorMap = {
'depth': this[Symbol.iterator].bind(this),
'breadth': this[Symbol.for('breadthFirst')].bind(this),
'leaves': this[Symbol.for('leaves')].bind(this),
'levels': this[Symbol.for('levels')].bind(this)
};
const iterator = iteratorMap[type];
if (!iterator) {
throw new Error(`不支持的迭代类型: ${type}`);
}
return iterator();
}
}
// 构建树结构
const root = new TreeNode('A');
const b = new TreeNode('B');
const c = new TreeNode('C');
const d = new TreeNode('D');
const e = new TreeNode('E');
const f = new TreeNode('F');
root.addChild(b).addChild(c);
b.addChild(d).addChild(e);
c.addChild(f);
// 测试不同的迭代方式
console.log('深度优先:', [...root]); // ['A', 'B', 'D', 'E', 'C', 'F']
console.log('广度优先:', [...root.iterate('breadth')]); // ['A', 'B', 'C', 'D', 'E', 'F']
console.log('叶子节点:', [...root.iterate('leaves')]); // ['D', 'E', 'F']
console.log('按层遍历:', [...root.iterate('levels')]); // [['A'], ['B', 'C'], ['D', 'E', 'F']]
Symbol 的最佳实践:
Symbol 的这些高级应用展示了它在现代 JavaScript 开发中的重要价值,特别是在框架开发、库设计和元编程领域。
How to implement a simple reactive system (like Vue3 reactivity principle)?
How to implement a simple reactive system (like Vue3 reactivity principle)?
考察点:Proxy、依赖收集、数据劫持。
答案:
响应式系统是现代前端框架的核心机制,它能够自动追踪数据变化并触发相应的更新。Vue3 使用 Proxy 重写了响应式系统,相比 Vue2 的 Object.defineProperty 方案具有更好的性能和功能完整性。
1. 响应式系统核心概念:
// 响应式系统的核心组件
class ReactivitySystem {
constructor() {
// 当前正在执行的副作用函数
this.activeEffect = null;
// 存储目标对象 -> 属性 -> 依赖集合的映射
// WeakMap { target -> Map { key -> Set<effect> } }
this.targetMap = new WeakMap();
// 副作用函数栈,支持嵌套 effect
this.effectStack = [];
}
// 依赖收集 - 在属性访问时调用
track(target, key) {
// 如果没有活跃的副作用函数,则不需要收集依赖
if (!this.activeEffect) return;
// 获取目标对象的依赖映射
let depsMap = this.targetMap.get(target);
if (!depsMap) {
this.targetMap.set(target, (depsMap = new Map()));
}
// 获取属性的依赖集合
let dep = depsMap.get(key);
if (!dep) {
depsMap.set(key, (dep = new Set()));
}
// 建立双向引用:effect 知道自己依赖哪些属性,属性知道哪些 effect 依赖自己
dep.add(this.activeEffect);
this.activeEffect.deps.push(dep);
console.log(`👀 Track: ${target.constructor.name}.${String(key)}`);
}
// 触发更新 - 在属性设置时调用
trigger(target, key, newValue, oldValue) {
const depsMap = this.targetMap.get(target);
if (!depsMap) return;
const dep = depsMap.get(key);
if (!dep) return;
console.log(`🔥 Trigger: ${target.constructor.name}.${String(key)} = ${newValue}`);
// 创建新的 Set 避免在遍历过程中修改原 Set
const effectsToRun = new Set(dep);
effectsToRun.forEach(effect => {
// 避免自递归(effect 内部修改了触发自己的属性)
if (effect !== this.activeEffect) {
if (effect.scheduler) {
// 如果有调度器,使用调度器执行
effect.scheduler(effect);
} else {
// 否则直接执行
effect();
}
}
});
}
// 创建响应式对象
reactive(target) {
// 如果不是对象,直接返回
if (!isObject(target)) return target;
// 如果已经是响应式对象,直接返回
if (target.__v_reactive) return target;
const proxy = new Proxy(target, {
get: (obj, key, receiver) => {
// 标记为响应式对象
if (key === '__v_reactive') return true;
const result = Reflect.get(obj, key, receiver);
// 收集依赖
this.track(obj, key);
// 如果属性值是对象,递归创建响应式
if (isObject(result)) {
return this.reactive(result);
}
return result;
},
set: (obj, key, value, receiver) => {
const oldValue = obj[key];
const result = Reflect.set(obj, key, value, receiver);
// 只有当值真正改变时才触发更新
if (oldValue !== value) {
this.trigger(obj, key, value, oldValue);
}
return result;
},
deleteProperty: (obj, key) => {
const hadKey = hasOwnProperty.call(obj, key);
const oldValue = obj[key];
const result = Reflect.deleteProperty(obj, key);
if (result && hadKey) {
this.trigger(obj, key, undefined, oldValue);
}
return result;
}
});
return proxy;
}
// 副作用函数
effect(fn, options = {}) {
const effect = () => {
// 清理之前的依赖
cleanup(effect);
// 设置当前活跃的 effect
this.activeEffect = effect;
this.effectStack.push(effect);
try {
// 执行副作用函数
return fn();
} finally {
// 恢复之前的 effect
this.effectStack.pop();
this.activeEffect = this.effectStack[this.effectStack.length - 1] || null;
}
};
// 为 effect 添加属性
effect.deps = []; // 存储依赖的属性集合
effect.options = options;
effect.scheduler = options.scheduler;
// 如果不是懒执行,立即执行一次
if (!options.lazy) {
effect();
}
return effect;
}
}
// 工具函数
function isObject(value) {
return typeof value === 'object' && value !== null;
}
function cleanup(effect) {
const { deps } = effect;
if (deps.length) {
for (let i = 0; i < deps.length; i++) {
deps[i].delete(effect);
}
deps.length = 0;
}
}
const hasOwnProperty = Object.prototype.hasOwnProperty;
2. 基础响应式系统使用示例:
// 创建响应式系统实例
const reactivity = new ReactivitySystem();
// 创建响应式数据
const state = reactivity.reactive({
count: 0,
user: {
name: 'Alice',
age: 25,
profile: {
email: '[email protected]'
}
},
items: [1, 2, 3]
});
// 创建副作用函数
console.log('=== 创建副作用函数 ===');
reactivity.effect(() => {
console.log(`计数器: ${state.count}`);
});
reactivity.effect(() => {
console.log(`用户信息: ${state.user.name} (${state.user.age}岁)`);
});
reactivity.effect(() => {
console.log(`邮箱: ${state.user.profile.email}`);
});
// 触发更新
console.log('\n=== 触发更新 ===');
state.count++; // 输出: 计数器: 1
state.user.name = 'Bob'; // 输出: 用户信息: Bob (25岁)
state.user.profile.email = '[email protected]'; // 输出: 邮箱: [email protected]
// 嵌套对象也是响应式的
console.log('\n=== 嵌套对象响应式 ===');
state.user.profile.settings = { theme: 'dark' };
reactivity.effect(() => {
if (state.user.profile.settings) {
console.log(`主题: ${state.user.profile.settings.theme}`);
}
});
state.user.profile.settings.theme = 'light'; // 输出: 主题: light
3. 高级特性:computed 计算属性:
// 扩展响应式系统,添加计算属性支持
class EnhancedReactivitySystem extends ReactivitySystem {
// 计算属性
computed(getter, options = {}) {
let value;
let dirty = true; // 标记是否需要重新计算
// 创建 effect,但不立即执行
const effect = this.effect(getter, {
lazy: true,
scheduler: () => {
if (!dirty) {
dirty = true;
// 触发计算属性的依赖更新
this.trigger(computedRef, 'value');
}
}
});
const computedRef = {
get value() {
// 只有在 dirty 时才重新计算
if (dirty) {
value = effect();
dirty = false;
}
// 收集对计算属性的依赖
this.track(computedRef, 'value');
return value;
}.bind(this)
};
return computedRef;
}
// watch 监听器
watch(source, callback, options = {}) {
let getter;
if (typeof source === 'function') {
getter = source;
} else {
// 如果是响应式对象,创建一个访问它的函数
getter = () => traverse(source);
}
let oldValue;
let cleanup;
const onInvalidate = (fn) => {
cleanup = fn;
};
const job = () => {
if (cleanup) cleanup();
const newValue = effect();
if (newValue !== oldValue || options.deep) {
callback(newValue, oldValue, onInvalidate);
oldValue = newValue;
}
};
const effect = this.effect(getter, {
lazy: true,
scheduler: job
});
if (options.immediate) {
job();
} else {
oldValue = effect();
}
// 返回停止监听的函数
return () => {
cleanup && cleanup();
// 停止 effect
effect.deps.forEach(dep => dep.delete(effect));
};
}
}
// 深度遍历对象的所有属性(用于 watch)
function traverse(value, seen = new Set()) {
if (!isObject(value) || seen.has(value)) {
return value;
}
seen.add(value);
if (Array.isArray(value)) {
for (let i = 0; i < value.length; i++) {
traverse(value[i], seen);
}
} else {
for (const key in value) {
traverse(value[key], seen);
}
}
return value;
}
// 使用增强的响应式系统
const enhanced = new EnhancedReactivitySystem();
const data = enhanced.reactive({
firstName: 'John',
lastName: 'Doe',
age: 30
});
// 计算属性
const fullName = enhanced.computed(() => {
console.log('💭 计算 fullName');
return `${data.firstName} ${data.lastName}`;
});
const isAdult = enhanced.computed(() => {
console.log('💭 计算 isAdult');
return data.age >= 18;
});
// 使用计算属性
console.log('\n=== 计算属性示例 ===');
console.log('全名:', fullName.value); // John Doe
console.log('是否成年:', isAdult.value); // true
// 修改依赖数据
data.firstName = 'Jane';
console.log('更新后全名:', fullName.value); // Jane Doe(重新计算)
console.log('是否成年:', isAdult.value); // true(从缓存读取)
// watch 监听器
console.log('\n=== Watch 示例 ===');
const stopWatching = enhanced.watch(
() => data.age,
(newAge, oldAge) => {
console.log(`年龄变化: ${oldAge} -> ${newAge}`);
}
);
enhanced.watch(
data,
() => {
console.log('数据对象发生了变化');
},
{ deep: true }
);
data.age = 25; // 年龄变化: 30 -> 25 & 数据对象发生了变化
data.firstName = 'Alice'; // 数据对象发生了变化
// 停止监听
stopWatching();
data.age = 20; // 不会触发年龄变化的回调
4. 响应式数组和集合:
// 扩展以支持数组和集合的响应式
class FullReactivitySystem extends EnhancedReactivitySystem {
reactive(target) {
if (!isObject(target)) return target;
if (target.__v_reactive) return target;
// 数组的特殊处理
if (Array.isArray(target)) {
return this.createReactiveArray(target);
}
// Map 的特殊处理
if (target instanceof Map) {
return this.createReactiveMap(target);
}
// Set 的特殊处理
if (target instanceof Set) {
return this.createReactiveSet(target);
}
return super.reactive(target);
}
createReactiveArray(target) {
const reactiveArray = new Proxy(target, {
get: (arr, key, receiver) => {
if (key === '__v_reactive') return true;
// 处理数组方法
if (arrayInstrumentations[key]) {
return arrayInstrumentations[key].bind(this, arr);
}
const result = Reflect.get(arr, key, receiver);
// 收集依赖
if (typeof key !== 'symbol' && !isNaN(key)) {
this.track(arr, key);
}
// 收集 length 依赖
if (key === 'length') {
this.track(arr, 'length');
}
return isObject(result) ? this.reactive(result) : result;
},
set: (arr, key, value, receiver) => {
const oldValue = arr[key];
const oldLength = arr.length;
const result = Reflect.set(arr, key, value, receiver);
if (oldValue !== value) {
this.trigger(arr, key, value, oldValue);
// 如果修改了数组长度
if (key === 'length' || (typeof key !== 'symbol' && !isNaN(key))) {
if (arr.length !== oldLength) {
this.trigger(arr, 'length', arr.length, oldLength);
}
}
}
return result;
}
});
return reactiveArray;
}
}
// 数组方法的响应式包装
const arrayInstrumentations = {
push(arr, ...items) {
const length = arr.length;
const result = Array.prototype.push.apply(arr, items);
this.trigger(arr, 'length', arr.length, length);
return result;
},
pop(arr) {
const length = arr.length;
const result = Array.prototype.pop.call(arr);
if (length > 0) {
this.trigger(arr, 'length', arr.length, length);
this.trigger(arr, length - 1, undefined, result);
}
return result;
},
shift(arr) {
const length = arr.length;
const result = Array.prototype.shift.call(arr);
if (length > 0) {
this.trigger(arr, 'length', arr.length, length);
// 所有索引都发生了变化
for (let i = 0; i < arr.length; i++) {
this.trigger(arr, i.toString(), arr[i], undefined);
}
}
return result;
},
unshift(arr, ...items) {
const length = arr.length;
const result = Array.prototype.unshift.apply(arr, items);
this.trigger(arr, 'length', arr.length, length);
// 所有索引都发生了变化
for (let i = 0; i < arr.length; i++) {
this.trigger(arr, i.toString(), arr[i], undefined);
}
return result;
},
splice(arr, start, deleteCount, ...items) {
const length = arr.length;
const result = Array.prototype.splice.apply(arr, [start, deleteCount, ...items]);
if (result.length > 0 || items.length > 0) {
this.trigger(arr, 'length', arr.length, length);
// 从 start 开始的所有索引都可能发生了变化
for (let i = start; i < Math.max(length, arr.length); i++) {
this.trigger(arr, i.toString(), arr[i], undefined);
}
}
return result;
}
};
// 测试响应式数组
const fullReactivity = new FullReactivitySystem();
const reactiveData = fullReactivity.reactive({
numbers: [1, 2, 3],
users: [
{ name: 'Alice', age: 25 },
{ name: 'Bob', age: 30 }
]
});
console.log('\n=== 响应式数组测试 ===');
fullReactivity.effect(() => {
console.log('数组长度:', reactiveData.numbers.length);
console.log('数组内容:', reactiveData.numbers.join(', '));
});
fullReactivity.effect(() => {
console.log('用户列表:', reactiveData.users.map(u => u.name).join(', '));
});
// 测试数组操作
console.log('\n--- 数组操作 ---');
reactiveData.numbers.push(4); // 触发长度和内容更新
reactiveData.numbers[0] = 10; // 触发索引更新
reactiveData.users.push({ name: 'Charlie', age: 28 }); // 触发用户列表更新
5. 性能优化和调度系统:
// 添加调度和批量更新优化
class OptimizedReactivitySystem extends FullReactivitySystem {
constructor() {
super();
this.updateQueue = new Set();
this.isFlushPending = false;
}
// 批量更新调度器
queueUpdate(effect) {
this.updateQueue.add(effect);
if (!this.isFlushPending) {
this.isFlushPending = true;
// 使用微任务异步批量执行更新
queueMicrotask(() => {
this.flushUpdates();
});
}
}
flushUpdates() {
console.log(`🔄 批量执行 ${this.updateQueue.size} 个更新`);
this.updateQueue.forEach(effect => {
if (effect.active !== false) {
effect();
}
});
this.updateQueue.clear();
this.isFlushPending = false;
}
// 重写 effect 方法,添加调度支持
effect(fn, options = {}) {
const effect = super.effect(fn, {
...options,
scheduler: options.scheduler || ((effect) => {
this.queueUpdate(effect);
})
});
effect.active = true;
return effect;
}
// 停止 effect
stop(effect) {
effect.active = false;
effect.deps.forEach(dep => dep.delete(effect));
effect.deps.length = 0;
}
}
// 使用优化的响应式系统
const optimized = new OptimizedReactivitySystem();
const testData = optimized.reactive({
count: 0,
name: 'Test'
});
console.log('\n=== 批量更新测试 ===');
// 创建多个 effect
const effect1 = optimized.effect(() => {
console.log('Effect 1: count =', testData.count);
});
const effect2 = optimized.effect(() => {
console.log('Effect 2: count * 2 =', testData.count * 2);
});
const effect3 = optimized.effect(() => {
console.log('Effect 3: name =', testData.name);
});
// 同步进行多个更新
console.log('\n--- 同步更新(会被批量处理)---');
testData.count = 1;
testData.count = 2;
testData.count = 3;
testData.name = 'Updated';
// 等待微任务队列执行
setTimeout(() => {
console.log('\n--- 停止某个 effect ---');
optimized.stop(effect2);
testData.count = 4; // effect2 不会再执行
}, 0);
响应式系统的核心特性总结:
这个实现展示了现代响应式系统的核心原理,类似于 Vue3 的实现思路,但进行了简化以便理解。在实际框架中,还会有更多的优化和边界情况处理。
What are the differences between ES6 modules and CommonJS? How to make them compatible?
What are the differences between ES6 modules and CommonJS? How to make them compatible?
考察点:静态/动态加载、导入导出机制。
答案:
ES6 模块(ESM)和 CommonJS(CJS)是 JavaScript 中两种主要的模块化规范。理解它们的区别以及如何兼容使用对于现代 JavaScript 开发至关重要。
1. 基础语法对比:
// === ES6 Modules (ESM) ===
// 导出方式
// 1. 命名导出
export const PI = 3.14159;
export function add(a, b) {
return a + b;
}
export class Calculator {
multiply(a, b) {
return a * b;
}
}
// 2. 默认导出
export default class MathUtils {
static square(x) {
return x * x;
}
}
// 3. 重新导出
export { sin, cos } from 'math-library';
export * from 'utilities';
// 导入方式
import MathUtils, { PI, add, Calculator } from './math.js';
import * as math from './math.js';
import { add as addition } from './math.js';
// === CommonJS (CJS) ===
// 导出方式
// 1. exports 对象
exports.PI = 3.14159;
exports.add = function(a, b) {
return a + b;
};
// 2. module.exports
class Calculator {
multiply(a, b) {
return a * b;
}
}
module.exports = Calculator;
// 3. 混合导出
module.exports = {
PI: 3.14159,
add: function(a, b) {
return a + b;
},
Calculator: Calculator
};
// 导入方式
const MathUtils = require('./math.js');
const { PI, add } = require('./math.js');
2. 核心区别分析:
// === 1. 加载时机差异 ===
// ES6 Modules: 静态加载(编译时)
import { debugFunction } from './debug.js'; // 编译时就确定了依赖
if (process.env.NODE_ENV === 'development') {
// ❌ 错误:ES6 import 不能在条件语句中
// import { devTool } from './dev-tools.js';
// ✅ 正确:使用动态 import
const { devTool } = await import('./dev-tools.js');
}
// CommonJS: 动态加载(运行时)
if (process.env.NODE_ENV === 'development') {
// ✅ 正确:CommonJS require 可以在条件语句中
const { devTool } = require('./dev-tools.js');
}
// === 2. 值引用差异 ===
// ES6 Modules: 活绑定(live binding)
// counter.js (ESM)
export let count = 0;
export function increment() {
count++;
}
// main.js (ESM)
import { count, increment } from './counter.js';
console.log(count); // 0
increment();
console.log(count); // 1 - 值实时更新
// CommonJS: 值拷贝
// counter.js (CJS)
let count = 0;
function increment() {
count++;
}
module.exports = { count, increment };
// main.js (CJS)
const { count, increment } = require('./counter.js');
console.log(count); // 0
increment();
console.log(count); // 0 - 值不会更新
3. 详细特性对比表:
// 创建对比演示
class ModuleComparisonDemo {
static demonstrateDifferences() {
console.log('=== ES6 Modules vs CommonJS 对比 ===\n');
const comparison = {
'特性': {
'ES6 Modules': 'CommonJS'
},
'语法': {
'ES6 Modules': 'import/export',
'CommonJS': 'require()/module.exports'
},
'加载方式': {
'ES6 Modules': '静态加载(编译时)',
'CommonJS': '动态加载(运行时)'
},
'值引用': {
'ES6 Modules': '活绑定(live binding)',
'CommonJS': '值拷贝(copy)'
},
'顶层 this': {
'ES6 Modules': 'undefined',
'CommonJS': 'module.exports'
},
'循环依赖': {
'ES6 Modules': '更好的支持',
'CommonJS': '可能出现问题'
},
'条件导入': {
'ES6 Modules': '不支持(需动态import)',
'CommonJS': '支持'
},
'Tree Shaking': {
'ES6 Modules': '支持',
'CommonJS': '不支持'
},
'浏览器支持': {
'ES6 Modules': '原生支持(现代浏览器)',
'CommonJS': '需要构建工具'
},
'Node.js 支持': {
'ES6 Modules': 'v12+ 实验性,v14+ 稳定',
'CommonJS': '原生支持'
}
};
Object.entries(comparison).forEach(([feature, values]) => {
if (feature === '特性') return;
console.log(`${feature}:`);
console.log(` ES6: ${values['ES6 Modules']}`);
console.log(` CJS: ${values['CommonJS']}\n`);
});
}
}
ModuleComparisonDemo.demonstrateDifferences();
4. 循环依赖处理差异:
// === ES6 Modules 循环依赖 ===
// a.js (ESM)
import { b } from './b.js';
export const a = 'a';
console.log('a.js:', b);
// b.js (ESM)
import { a } from './a.js';
export const b = 'b';
console.log('b.js:', a); // undefined,但不会出错
// === CommonJS 循环依赖 ===
// a.js (CJS)
const { b } = require('./b.js');
module.exports = { a: 'a' };
console.log('a.js:', b);
// b.js (CJS)
const { a } = require('./a.js'); // 可能获取到不完整的 exports
module.exports = { b: 'b' };
console.log('b.js:', a); // 可能是 undefined 或不完整对象
// 解决循环依赖的最佳实践
class CircularDependencyResolver {
// 方法1:延迟导入
static lazyImport() {
// 在需要时才导入
const getModuleB = () => import('./b.js');
return {
async useB() {
const { b } = await getModuleB();
return b;
}
};
}
// 方法2:依赖注入
static dependencyInjection() {
class ModuleA {
constructor(moduleB = null) {
this.moduleB = moduleB;
}
setModuleB(moduleB) {
this.moduleB = moduleB;
}
useB() {
return this.moduleB?.someMethod();
}
}
return ModuleA;
}
// 方法3:事件驱动
static eventDriven() {
const EventEmitter = require('events');
const eventBus = new EventEmitter();
return {
moduleA: {
init() {
eventBus.on('moduleB:ready', (moduleB) => {
console.log('Module A received Module B:', moduleB);
});
},
notifyReady() {
eventBus.emit('moduleA:ready', this);
}
}
};
}
}
5. 兼容性解决方案:
// === 1. 构建工具配置兼容 ===
// webpack.config.js
module.exports = {
resolve: {
// 支持不同的模块扩展名
extensions: ['.mjs', '.js', '.json'],
// 模块解析优先级
mainFields: ['module', 'main']
},
module: {
rules: [
{
test: /\.m?js$/,
use: {
loader: 'babel-loader',
options: {
presets: [
['@babel/preset-env', {
modules: false // 保持 ES6 模块用于 tree shaking
}]
]
}
}
}
]
}
};
// === 2. package.json 配置 ===
// package.json
{
"name": "my-package",
"version": "1.0.0",
"type": "module", // 设置为 ES6 模块
"main": "./dist/index.cjs", // CommonJS 入口
"module": "./dist/index.mjs", // ES6 模块入口
"exports": {
".": {
"import": "./dist/index.mjs",
"require": "./dist/index.cjs"
}
}
}
// === 3. 通用模块适配器 ===
class UniversalModuleAdapter {
// 创建同时支持 ES6 和 CommonJS 的模块
static createUniversalModule(moduleFactory) {
// UMD (Universal Module Definition) 模式
return `
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
// AMD
define([], factory);
} else if (typeof module === 'object' && module.exports) {
// CommonJS
module.exports = factory();
} else {
// Browser globals
root.MyModule = factory();
}
}(typeof self !== 'undefined' ? self : this, function () {
${moduleFactory.toString()}
return moduleFactory();
}));
`;
}
// ES6 和 CommonJS 互相调用的桥接
static createBridge() {
return {
// ES6 调用 CommonJS
async importCommonJS(modulePath) {
// 在 Node.js 中
if (typeof require !== 'undefined') {
return require(modulePath);
}
// 在浏览器中需要通过构建工具处理
throw new Error('CommonJS modules not supported in browser');
},
// CommonJS 调用 ES6
requireESModule: (modulePath) => {
// Node.js 14+ 支持
return import(modulePath);
}
};
}
}
// === 4. 条件导出适配 ===
class ConditionalExports {
// 基于环境的模块导出
static createConditionalExport() {
// 检测当前环境
const isNode = typeof process !== 'undefined' && process.versions?.node;
const isBrowser = typeof window !== 'undefined';
if (isNode) {
// Node.js 环境:支持 CommonJS 和 ES6
return {
async loadModule(specifier) {
try {
// 优先尝试 ES6 import
return await import(specifier);
} catch (error) {
// 回退到 CommonJS require
if (typeof require !== 'undefined') {
return require(specifier);
}
throw error;
}
}
};
} else if (isBrowser) {
// 浏览器环境:只支持 ES6(或需要构建工具)
return {
async loadModule(specifier) {
return await import(specifier);
}
};
}
}
}
6. 实际项目中的最佳实践:
// === 1. 渐进式迁移策略 ===
class MigrationStrategy {
// 从 CommonJS 迁移到 ES6 的步骤
static getSteps() {
return [
{
step: 1,
title: '添加 ES6 模块支持',
actions: [
'在 package.json 中添加 "type": "module"',
'或使用 .mjs 扩展名',
'更新 Node.js 到支持 ES6 模块的版本'
]
},
{
step: 2,
title: '逐步转换模块',
actions: [
'从叶子节点(无依赖的模块)开始',
'将 require() 替换为 import',
'将 module.exports 替换为 export',
'处理循环依赖问题'
]
},
{
step: 3,
title: '更新构建配置',
actions: [
'配置 webpack/rollup 支持 ES6 模块',
'启用 tree shaking',
'配置多入口支持'
]
},
{
step: 4,
title: '测试和验证',
actions: [
'确保所有测试通过',
'验证 tree shaking 效果',
'检查打包体积优化'
]
}
];
}
// 兼容性检查工具
static checkCompatibility() {
const results = {
nodeVersion: process.version,
esModuleSupport: false,
dynamicImportSupport: false
};
// 检查 ES6 模块支持
try {
new Function('import("./test")')();
results.esModuleSupport = true;
} catch (e) {
results.esModuleSupport = false;
}
// 检查动态 import 支持
try {
eval('import("./test")');
results.dynamicImportSupport = true;
} catch (e) {
results.dynamicImportSupport = false;
}
return results;
}
}
// === 2. 模块加载器 ===
class ModuleLoader {
constructor() {
this.cache = new Map();
this.loading = new Map();
}
// 通用模块加载器
async load(specifier, options = {}) {
// 检查缓存
if (this.cache.has(specifier)) {
return this.cache.get(specifier);
}
// 避免重复加载
if (this.loading.has(specifier)) {
return this.loading.get(specifier);
}
const loadPromise = this.loadModule(specifier, options);
this.loading.set(specifier, loadPromise);
try {
const module = await loadPromise;
this.cache.set(specifier, module);
return module;
} finally {
this.loading.delete(specifier);
}
}
async loadModule(specifier, options) {
const { format = 'auto' } = options;
switch (format) {
case 'esm':
return await this.loadESModule(specifier);
case 'cjs':
return this.loadCommonJS(specifier);
case 'auto':
default:
return await this.loadAuto(specifier);
}
}
async loadESModule(specifier) {
try {
return await import(specifier);
} catch (error) {
throw new Error(`Failed to load ES module: ${specifier}. ${error.message}`);
}
}
loadCommonJS(specifier) {
if (typeof require === 'undefined') {
throw new Error('CommonJS require is not available in this environment');
}
try {
return require(specifier);
} catch (error) {
throw new Error(`Failed to load CommonJS module: ${specifier}. ${error.message}`);
}
}
async loadAuto(specifier) {
// 尝试不同的加载方式
const attempts = [
() => this.loadESModule(specifier),
() => Promise.resolve(this.loadCommonJS(specifier))
];
for (const attempt of attempts) {
try {
return await attempt();
} catch (error) {
// 继续下一种方式
console.warn(`Load attempt failed for ${specifier}:`, error.message);
}
}
throw new Error(`Failed to load module ${specifier} with any method`);
}
}
// 使用示例
const loader = new ModuleLoader();
async function demonstrateModuleLoading() {
try {
console.log('=== 模块加载演示 ===');
// 自动检测加载
const mathModule = await loader.load('./math-utils');
console.log('Math module loaded:', mathModule);
// 强制指定格式
const esmModule = await loader.load('./esm-module', { format: 'esm' });
console.log('ESM module loaded:', esmModule);
} catch (error) {
console.error('Module loading failed:', error);
}
}
7. 性能和优化考虑:
// 模块加载性能优化
class ModuleOptimizer {
// Tree Shaking 示例
static demonstrateTreeShaking() {
// 库文件 - utils.js
const createUtils = () => ({
// 会被使用的函数
add: (a, b) => a + b,
multiply: (a, b) => a * b,
// 不会被使用的函数(会被 tree shake)
subtract: (a, b) => a - b,
divide: (a, b) => a / b,
complexCalculation: () => {
// 复杂计算逻辑
return Array.from({length: 1000}, (_, i) => i * i).reduce((a, b) => a + b);
}
});
return createUtils();
}
// 代码分割和懒加载
static async implementCodeSplitting() {
const routes = {
'/home': () => import('./pages/home.js'),
'/about': () => import('./pages/about.js'),
'/contact': () => import('./pages/contact.js')
};
return {
async loadRoute(path) {
const loader = routes[path];
if (!loader) {
throw new Error(`Route ${path} not found`);
}
console.log(`Loading route: ${path}`);
const module = await loader();
console.log(`Route ${path} loaded successfully`);
return module;
}
};
}
// 模块预加载策略
static implementPreloading() {
const preloadQueue = [];
let isPreloading = false;
return {
addToPreload(specifier) {
preloadQueue.push(specifier);
this.startPreloading();
},
async startPreloading() {
if (isPreloading || preloadQueue.length === 0) return;
isPreloading = true;
while (preloadQueue.length > 0) {
const specifier = preloadQueue.shift();
try {
await import(specifier);
console.log(`Preloaded: ${specifier}`);
} catch (error) {
console.warn(`Failed to preload: ${specifier}`, error);
}
// 避免阻塞主线程
await new Promise(resolve => setTimeout(resolve, 0));
}
isPreloading = false;
}
};
}
}
总结和建议:
ES6 模块代表了 JavaScript 模块化的未来方向,虽然迁移需要时间,但长期来看能带来更好的开发体验和性能优化。
How to implement an async iterator? What are the application scenarios?
How to implement an async iterator? What are the application scenarios?
考察点:Symbol.asyncIterator、for await…of。
答案:
异步迭代器是 ES2018 (ES9) 引入的特性,它允许我们以迭代的方式处理异步数据流。异步迭代器结合了迭代器模式和 Promise,为处理流式数据、API 分页、实时数据等场景提供了优雅的解决方案。
1. 异步迭代器的基础实现:
// 基础异步迭代器实现
class AsyncRange {
constructor(start, end, delay = 100) {
this.start = start;
this.end = end;
this.delay = delay;
}
// 实现 Symbol.asyncIterator 方法
[Symbol.asyncIterator]() {
let current = this.start;
const end = this.end;
const delay = this.delay;
return {
async next() {
// 模拟异步操作
await new Promise(resolve => setTimeout(resolve, delay));
if (current <= end) {
return { value: current++, done: false };
} else {
return { done: true };
}
}
};
}
}
// 使用异步迭代器
async function demonstrateAsyncIterator() {
console.log('=== 异步迭代器基础使用 ===');
const asyncRange = new AsyncRange(1, 5, 200);
// 使用 for await...of 语法
console.log('使用 for await...of:');
for await (const num of asyncRange) {
console.log(`异步获取到数字: ${num}`);
}
console.log('迭代完成\n');
// 手动使用异步迭代器
console.log('手动使用异步迭代器:');
const iterator = asyncRange[Symbol.asyncIterator]();
let result = await iterator.next();
while (!result.done) {
console.log(`手动获取到数字: ${result.value}`);
result = await iterator.next();
}
console.log('手动迭代完成\n');
}
demonstrateAsyncIterator();
2. 高级异步迭代器 - 数据流处理:
// 异步数据流迭代器
class AsyncDataStream {
constructor(dataSource, options = {}) {
this.dataSource = dataSource;
this.batchSize = options.batchSize || 10;
this.delay = options.delay || 100;
this.transform = options.transform || (x => x);
this.filter = options.filter || (() => true);
}
async *[Symbol.asyncIterator]() {
let offset = 0;
let hasMore = true;
while (hasMore) {
try {
// 获取一批数据
const batch = await this.fetchBatch(offset, this.batchSize);
if (!batch || batch.length === 0) {
hasMore = false;
break;
}
// 处理批次数据
for (const item of batch) {
// 应用过滤器
if (this.filter(item)) {
// 应用转换器
const transformedItem = await this.transform(item);
yield transformedItem;
}
}
offset += batch.length;
// 如果批次大小小于预期,说明已经到达末尾
if (batch.length < this.batchSize) {
hasMore = false;
}
// 添加延迟,避免过于频繁的请求
if (this.delay > 0) {
await new Promise(resolve => setTimeout(resolve, this.delay));
}
} catch (error) {
console.error('获取数据批次失败:', error);
throw error;
}
}
}
async fetchBatch(offset, limit) {
// 模拟异步数据获取(如 API 调用)
if (typeof this.dataSource === 'function') {
return await this.dataSource(offset, limit);
} else if (Array.isArray(this.dataSource)) {
return this.dataSource.slice(offset, offset + limit);
} else {
throw new Error('不支持的数据源类型');
}
}
// 添加操作符方法,支持链式调用
map(mapFn) {
return new AsyncDataStream(
this.dataSource,
{
...this.options,
transform: async (item) => {
const transformed = await this.transform(item);
return await mapFn(transformed);
}
}
);
}
filter(filterFn) {
return new AsyncDataStream(
this.dataSource,
{
...this.options,
filter: async (item) => {
const passed = await this.filter(item);
return passed && await filterFn(item);
}
}
);
}
take(count) {
let taken = 0;
return {
async *[Symbol.asyncIterator]() {
for await (const item of this) {
if (taken >= count) break;
yield item;
taken++;
}
}
};
}
}
// 使用数据流迭代器
async function demonstrateDataStream() {
console.log('=== 异步数据流处理 ===');
// 模拟 API 数据源
const mockAPI = async (offset, limit) => {
console.log(`📡 API 调用: offset=${offset}, limit=${limit}`);
// 模拟网络延迟
await new Promise(resolve => setTimeout(resolve, 300));
// 生成模拟数据
const data = Array.from({ length: limit }, (_, i) => ({
id: offset + i + 1,
name: `User ${offset + i + 1}`,
score: Math.floor(Math.random() * 100)
}));
// 模拟数据结束条件
return offset >= 25 ? [] : data;
};
// 创建数据流
const userStream = new AsyncDataStream(mockAPI, {
batchSize: 5,
delay: 200,
filter: (user) => user.score >= 60, // 只要分数 >= 60 的用户
transform: async (user) => ({
...user,
grade: user.score >= 90 ? 'A' : user.score >= 80 ? 'B' : 'C',
timestamp: new Date().toISOString()
})
});
// 处理数据流
let count = 0;
for await (const user of userStream.take(8)) {
console.log(`👤 用户 ${++count}:`, {
name: user.name,
score: user.score,
grade: user.grade
});
}
console.log('数据流处理完成\n');
}
demonstrateDataStream();
3. 实际应用场景 - 文件流处理:
// 异步文件读取迭代器
class AsyncFileReader {
constructor(filePath, options = {}) {
this.filePath = filePath;
this.chunkSize = options.chunkSize || 1024;
this.encoding = options.encoding || 'utf8';
}
async *[Symbol.asyncIterator]() {
const fs = require('fs').promises;
let fileHandle;
try {
fileHandle = await fs.open(this.filePath, 'r');
const stats = await fileHandle.stat();
const fileSize = stats.size;
let position = 0;
const buffer = Buffer.alloc(this.chunkSize);
while (position < fileSize) {
const { bytesRead } = await fileHandle.read(
buffer,
0,
this.chunkSize,
position
);
if (bytesRead === 0) break;
const chunk = buffer.slice(0, bytesRead).toString(this.encoding);
yield chunk;
position += bytesRead;
}
} finally {
if (fileHandle) {
await fileHandle.close();
}
}
}
// 按行读取
async *lines() {
let leftover = '';
for await (const chunk of this) {
const lines = (leftover + chunk).split('\n');
leftover = lines.pop() || ''; // 保存最后一行的残留部分
for (const line of lines) {
yield line;
}
}
// 处理文件末尾的残留内容
if (leftover) {
yield leftover;
}
}
}
// 使用文件读取迭代器
async function demonstrateFileReading() {
console.log('=== 异步文件读取 ===');
// 创建示例文件内容(在实际项目中,这个文件应该已存在)
const fs = require('fs').promises;
const testFile = './test-data.txt';
try {
await fs.writeFile(testFile,
'Line 1: Hello World\nLine 2: Async Iterator\nLine 3: File Processing\nLine 4: Demo Complete'
);
const fileReader = new AsyncFileReader(testFile, { chunkSize: 10 });
console.log('按块读取:');
let chunkCount = 0;
for await (const chunk of fileReader) {
console.log(`📄 块 ${++chunkCount}: "${chunk}"`);
}
console.log('\n按行读取:');
let lineCount = 0;
for await (const line of fileReader.lines()) {
console.log(`📝 行 ${++lineCount}: "${line}"`);
}
// 清理测试文件
await fs.unlink(testFile);
} catch (error) {
console.error('文件操作失败:', error.message);
}
console.log('文件处理完成\n');
}
// 仅在 Node.js 环境中运行
if (typeof require !== 'undefined') {
demonstrateFileReading();
}
4. Web API 集成 - 分页数据处理:
// 分页 API 异步迭代器
class PaginatedAPIIterator {
constructor(baseUrl, options = {}) {
this.baseUrl = baseUrl;
this.pageSize = options.pageSize || 20;
this.headers = options.headers || {};
this.params = options.params || {};
this.totalPages = null;
}
async *[Symbol.asyncIterator]() {
let currentPage = 1;
while (true) {
try {
const response = await this.fetchPage(currentPage);
if (!response.data || response.data.length === 0) {
break;
}
// 更新总页数信息
if (response.pagination) {
this.totalPages = response.pagination.totalPages;
}
// 逐个产出数据项
for (const item of response.data) {
yield {
...item,
_metadata: {
page: currentPage,
totalPages: this.totalPages
}
};
}
// 检查是否还有更多页面
if (this.totalPages && currentPage >= this.totalPages) {
break;
}
// 如果当前页数据少于页大小,说明已经到最后一页
if (response.data.length < this.pageSize) {
break;
}
currentPage++;
} catch (error) {
console.error(`获取第 ${currentPage} 页数据失败:`, error.message);
throw error;
}
}
}
async fetchPage(page) {
const url = new URL(this.baseUrl);
// 添加分页参数
url.searchParams.set('page', page);
url.searchParams.set('limit', this.pageSize);
// 添加其他参数
Object.entries(this.params).forEach(([key, value]) => {
url.searchParams.set(key, value);
});
console.log(`🌐 请求第 ${page} 页数据: ${url}`);
const response = await fetch(url, {
headers: this.headers
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
return await response.json();
}
// 添加过滤器
filter(predicate) {
const originalIterator = this[Symbol.asyncIterator].bind(this);
return {
async *[Symbol.asyncIterator]() {
for await (const item of { [Symbol.asyncIterator]: originalIterator }) {
if (await predicate(item)) {
yield item;
}
}
}
};
}
// 添加映射器
map(mapper) {
const originalIterator = this[Symbol.asyncIterator].bind(this);
return {
async *[Symbol.asyncIterator]() {
for await (const item of { [Symbol.asyncIterator]: originalIterator }) {
yield await mapper(item);
}
}
};
}
}
// 模拟分页 API 使用
async function demonstratePaginatedAPI() {
console.log('=== 分页 API 数据处理 ===');
// 模拟 fetch 函数(在实际环境中会使用真实的 fetch)
global.fetch = async (url) => {
const urlObj = new URL(url);
const page = parseInt(urlObj.searchParams.get('page') || '1');
const limit = parseInt(urlObj.searchParams.get('limit') || '20');
// 模拟 API 响应
await new Promise(resolve => setTimeout(resolve, 200));
const totalItems = 45;
const totalPages = Math.ceil(totalItems / limit);
const startIndex = (page - 1) * limit;
const endIndex = Math.min(startIndex + limit, totalItems);
const data = [];
for (let i = startIndex; i < endIndex; i++) {
data.push({
id: i + 1,
name: `Item ${i + 1}`,
category: i % 3 === 0 ? 'A' : i % 3 === 1 ? 'B' : 'C',
price: Math.floor(Math.random() * 100) + 10
});
}
return {
ok: true,
async json() {
return {
data,
pagination: {
currentPage: page,
totalPages,
totalItems,
hasNext: page < totalPages
}
};
}
};
};
// 使用分页迭代器
const apiIterator = new PaginatedAPIIterator('https://api.example.com/items', {
pageSize: 10,
params: { category: 'products' }
});
// 过滤和处理数据
const filteredItems = apiIterator
.filter(item => item.price > 50)
.map(item => ({
...item,
formattedPrice: `$${item.price}`,
isExpensive: item.price > 80
}));
let count = 0;
for await (const item of filteredItems) {
console.log(`🛍️ 商品 ${++count}:`, {
name: item.name,
category: item.category,
price: item.formattedPrice,
expensive: item.isExpensive,
page: item._metadata.page
});
// 限制输出数量
if (count >= 8) break;
}
console.log('API 数据处理完成\n');
}
demonstratePaginatedAPI();
5. 实时数据流 - WebSocket 异步迭代器:
// WebSocket 异步迭代器
class WebSocketAsyncIterator {
constructor(url, options = {}) {
this.url = url;
this.options = options;
this.messageQueue = [];
this.resolvers = [];
this.isConnected = false;
this.isClosed = false;
}
async connect() {
return new Promise((resolve, reject) => {
// 在实际环境中使用 WebSocket
// 这里模拟 WebSocket 连接
this.ws = {
readyState: 1, // WebSocket.OPEN
close: () => {
this.isClosed = true;
this.resolveAll(null, true);
},
send: (data) => {
console.log('📤 发送消息:', data);
}
};
this.isConnected = true;
// 模拟接收消息
this.simulateMessages();
resolve();
});
}
simulateMessages() {
let messageCount = 0;
const interval = setInterval(() => {
if (this.isClosed) {
clearInterval(interval);
return;
}
const message = {
id: ++messageCount,
type: 'data',
payload: {
timestamp: new Date().toISOString(),
value: Math.random() * 100,
status: 'active'
}
};
this.handleMessage(message);
if (messageCount >= 10) {
clearInterval(interval);
this.close();
}
}, 500);
}
handleMessage(message) {
if (this.resolvers.length > 0) {
const resolver = this.resolvers.shift();
resolver(message);
} else {
this.messageQueue.push(message);
}
}
async *[Symbol.asyncIterator]() {
if (!this.isConnected) {
await this.connect();
}
try {
while (!this.isClosed) {
const message = await this.getNextMessage();
if (message === null) break;
yield message;
}
} finally {
this.close();
}
}
async getNextMessage() {
if (this.messageQueue.length > 0) {
return this.messageQueue.shift();
}
if (this.isClosed) {
return null;
}
return new Promise((resolve) => {
this.resolvers.push(resolve);
});
}
resolveAll(value, isDone = false) {
while (this.resolvers.length > 0) {
const resolver = this.resolvers.shift();
resolver(isDone ? null : value);
}
}
close() {
if (!this.isClosed) {
this.isClosed = true;
if (this.ws) {
this.ws.close();
}
this.resolveAll(null, true);
}
}
send(message) {
if (this.isConnected && !this.isClosed) {
this.ws.send(JSON.stringify(message));
}
}
}
// 使用 WebSocket 异步迭代器
async function demonstrateWebSocketStream() {
console.log('=== WebSocket 实时数据流 ===');
const wsIterator = new WebSocketAsyncIterator('wss://api.example.com/stream');
try {
let messageCount = 0;
for await (const message of wsIterator) {
console.log(`📨 收到消息 ${++messageCount}:`, {
id: message.id,
timestamp: message.payload.timestamp,
value: message.payload.value.toFixed(2)
});
}
console.log('WebSocket 连接已关闭');
} catch (error) {
console.error('WebSocket 错误:', error);
}
console.log('实时数据流处理完成\n');
}
demonstrateWebSocketStream();
6. 异步迭代器工具库:
// 异步迭代器工具函数
class AsyncIteratorUtils {
// 将普通迭代器转换为异步迭代器
static async *fromSync(syncIterable) {
for (const item of syncIterable) {
yield item;
}
}
// 将 Promise 数组转换为异步迭代器
static async *fromPromises(promises) {
for (const promise of promises) {
yield await promise;
}
}
// 合并多个异步迭代器
static async *merge(...asyncIterables) {
const iterators = asyncIterables.map(iterable =>
iterable[Symbol.asyncIterator]()
);
const promises = iterators.map((iterator, index) =>
iterator.next().then(result => ({ ...result, index }))
);
while (promises.length > 0) {
const { value, done, index } = await Promise.race(promises);
if (!done) {
yield value;
// 替换已完成的 Promise
promises[index] = iterators[index].next()
.then(result => ({ ...result, index }));
} else {
// 移除已完成的迭代器
promises.splice(index, 1);
iterators.splice(index, 1);
// 更新剩余 Promise 的索引
for (let i = index; i < promises.length; i++) {
promises[i] = promises[i].then(result => ({
...result,
index: result.index > index ? result.index - 1 : result.index
}));
}
}
}
}
// 限制并发数量
static async *concurrent(asyncIterable, concurrency = 3) {
const iterator = asyncIterable[Symbol.asyncIterator]();
const running = new Set();
let done = false;
while (!done || running.size > 0) {
// 填充运行队列
while (running.size < concurrency && !done) {
const { value, done: isDone } = await iterator.next();
if (isDone) {
done = true;
break;
}
const promise = Promise.resolve(value)
.then(result => ({ result, success: true }))
.catch(error => ({ error, success: false }))
.finally(() => running.delete(promise));
running.add(promise);
}
if (running.size > 0) {
const { result, error, success } = await Promise.race(running);
if (success) {
yield result;
} else {
throw error;
}
}
}
}
// 收集异步迭代器的所有值
static async collect(asyncIterable) {
const results = [];
for await (const item of asyncIterable) {
results.push(item);
}
return results;
}
// 异步 reduce
static async reduce(asyncIterable, reducer, initialValue) {
let accumulator = initialValue;
let index = 0;
for await (const item of asyncIterable) {
accumulator = await reducer(accumulator, item, index++);
}
return accumulator;
}
}
// 使用工具库
async function demonstrateUtils() {
console.log('=== 异步迭代器工具库 ===');
// 创建测试数据
const asyncRange1 = new AsyncRange(1, 3, 100);
const asyncRange2 = new AsyncRange(10, 12, 150);
// 合并多个异步迭代器
console.log('合并异步迭代器:');
const merged = AsyncIteratorUtils.merge(asyncRange1, asyncRange2);
for await (const value of merged) {
console.log(`📊 合并值: ${value}`);
}
// 收集所有值
const range3 = new AsyncRange(1, 5, 50);
const collected = await AsyncIteratorUtils.collect(range3);
console.log('📦 收集的值:', collected);
// 异步 reduce
const range4 = new AsyncRange(1, 4, 30);
const sum = await AsyncIteratorUtils.reduce(
range4,
(acc, value) => acc + value,
0
);
console.log('📈 求和结果:', sum);
console.log('工具库演示完成\n');
}
demonstrateUtils();
异步迭代器的应用场景总结:
异步迭代器提供了一种优雅的方式来处理异步数据流,结合现代 JavaScript 的 async/await 语法,能够编写出既高效又易读的异步代码。
What are the common methods of Reflect API? What is its relationship with Proxy?
What are the common methods of Reflect API? What is its relationship with Proxy?
考察点:元编程与对象操作。
答案:
Reflect API 是 ES6 引入的新全局对象,它提供了一组与 Proxy 处理器方法对应的静态方法。Reflect 的设计目标是让对象操作变得更加函数化和统一化,同时为 Proxy 提供了默认行为的实现。
1. Reflect API 的常用方法概览:
// Reflect API 方法完整示例
class ReflectDemo {
constructor() {
this.name = 'Demo';
this.value = 42;
}
method() {
return 'Hello from method';
}
static staticMethod() {
return 'Hello from static method';
}
}
const demo = new ReflectDemo();
const obj = { x: 1, y: 2, z: 3 };
console.log('=== Reflect API 方法演示 ===\n');
// 1. Reflect.get - 获取属性值
console.log('1. Reflect.get:');
console.log('demo.name =', Reflect.get(demo, 'name')); // 'Demo'
console.log('obj.x =', Reflect.get(obj, 'x')); // 1
console.log('obj["y"] =', Reflect.get(obj, 'y')); // 2
console.log();
// 2. Reflect.set - 设置属性值
console.log('2. Reflect.set:');
console.log('设置 obj.x = 10:', Reflect.set(obj, 'x', 10)); // true
console.log('验证 obj.x =', obj.x); // 10
console.log('设置 demo.value = 100:', Reflect.set(demo, 'value', 100)); // true
console.log('验证 demo.value =', demo.value); // 100
console.log();
// 3. Reflect.has - 检查属性是否存在
console.log('3. Reflect.has:');
console.log('obj 中是否有 x:', Reflect.has(obj, 'x')); // true
console.log('obj 中是否有 w:', Reflect.has(obj, 'w')); // false
console.log('demo 中是否有 name:', Reflect.has(demo, 'name')); // true
console.log();
// 4. Reflect.deleteProperty - 删除属性
console.log('4. Reflect.deleteProperty:');
console.log('删除前 obj:', obj);
console.log('删除 obj.z:', Reflect.deleteProperty(obj, 'z')); // true
console.log('删除后 obj:', obj);
console.log();
// 5. Reflect.ownKeys - 获取所有自有属性键
console.log('5. Reflect.ownKeys:');
console.log('obj 的所有键:', Reflect.ownKeys(obj)); // ['x', 'y']
console.log('demo 的所有键:', Reflect.ownKeys(demo)); // ['name', 'value']
console.log();
// 6. Reflect.getPrototypeOf - 获取原型
console.log('6. Reflect.getPrototypeOf:');
console.log('obj 的原型是 Object.prototype:',
Reflect.getPrototypeOf(obj) === Object.prototype); // true
console.log('demo 的原型是 ReflectDemo.prototype:',
Reflect.getPrototypeOf(demo) === ReflectDemo.prototype); // true
console.log();
// 7. Reflect.setPrototypeOf - 设置原型
console.log('7. Reflect.setPrototypeOf:');
const newProto = { customMethod() { return 'custom'; } };
console.log('设置新原型:', Reflect.setPrototypeOf(obj, newProto)); // true
console.log('调用原型方法:', obj.customMethod()); // 'custom'
console.log();
2. 深入理解各个方法的使用场景:
// 详细的 Reflect 方法使用案例
class AdvancedReflectUsage {
// Reflect.defineProperty - 定义属性
static demonstrateDefineProperty() {
console.log('=== Reflect.defineProperty 演示 ===');
const obj = {};
// 定义数据属性
const success1 = Reflect.defineProperty(obj, 'name', {
value: 'Alice',
writable: true,
enumerable: true,
configurable: true
});
console.log('定义数据属性成功:', success1); // true
// 定义访问器属性
let _age = 25;
const success2 = Reflect.defineProperty(obj, 'age', {
get() { return _age; },
set(value) {
if (value >= 0) _age = value;
},
enumerable: true,
configurable: true
});
console.log('定义访问器属性成功:', success2); // true
console.log('obj:', obj); // { name: 'Alice' }
console.log('obj.age:', obj.age); // 25
obj.age = 30;
console.log('设置后 obj.age:', obj.age); // 30
console.log();
return obj;
}
// Reflect.getOwnPropertyDescriptor - 获取属性描述符
static demonstrateGetOwnPropertyDescriptor() {
console.log('=== Reflect.getOwnPropertyDescriptor 演示 ===');
const obj = this.demonstrateDefineProperty();
const nameDesc = Reflect.getOwnPropertyDescriptor(obj, 'name');
console.log('name 属性描述符:', nameDesc);
const ageDesc = Reflect.getOwnPropertyDescriptor(obj, 'age');
console.log('age 属性描述符:', {
get: ageDesc.get ? '[Function: get]' : undefined,
set: ageDesc.set ? '[Function: set]' : undefined,
enumerable: ageDesc.enumerable,
configurable: ageDesc.configurable
});
console.log();
}
// Reflect.apply - 调用函数
static demonstrateApply() {
console.log('=== Reflect.apply 演示 ===');
function greet(greeting, punctuation) {
return `${greeting}, ${this.name}${punctuation}`;
}
const person = { name: 'Bob' };
// 使用 Reflect.apply 调用函数
const result1 = Reflect.apply(greet, person, ['Hello', '!']);
console.log('Reflect.apply 结果:', result1); // 'Hello, Bob!'
// 对比传统方式
const result2 = greet.call(person, 'Hi', '.');
console.log('传统 call 结果:', result2); // 'Hi, Bob.'
// 数组方法的应用
const numbers = [1, 2, 3, 4, 5];
const max = Reflect.apply(Math.max, null, numbers);
console.log('数组最大值:', max); // 5
console.log();
}
// Reflect.construct - 构造实例
static demonstrateConstruct() {
console.log('=== Reflect.construct 演示 ===');
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
introduce() {
return `I'm ${this.name}, ${this.age} years old.`;
}
}
// 使用 Reflect.construct 创建实例
const person1 = Reflect.construct(Person, ['Charlie', 28]);
console.log('Reflect.construct 创建:', person1.introduce());
// 对比 new 操作符
const person2 = new Person('David', 32);
console.log('new 操作符创建:', person2.introduce());
// 指定不同的原型
class Employee extends Person {
constructor(name, age, department) {
super(name, age);
this.department = department;
}
}
const employee = Reflect.construct(Person, ['Eve', 27], Employee);
console.log('指定原型创建:', employee instanceof Employee); // true
console.log('employee:', employee);
console.log();
}
}
// 运行演示
AdvancedReflectUsage.demonstrateDefineProperty();
AdvancedReflectUsage.demonstrateGetOwnPropertyDescriptor();
AdvancedReflectUsage.demonstrateApply();
AdvancedReflectUsage.demonstrateConstruct();
3. Reflect 与 Proxy 的关系:
// Reflect 与 Proxy 的完美结合
class ProxyReflectIntegration {
// 基础的 Proxy + Reflect 组合
static createBasicProxy(target) {
return new Proxy(target, {
get(obj, prop, receiver) {
console.log(`📖 读取属性: ${String(prop)}`);
return Reflect.get(obj, prop, receiver);
},
set(obj, prop, value, receiver) {
console.log(`✏️ 设置属性: ${String(prop)} = ${value}`);
return Reflect.set(obj, prop, value, receiver);
},
has(obj, prop) {
console.log(`🔍 检查属性: ${String(prop)}`);
return Reflect.has(obj, prop);
},
deleteProperty(obj, prop) {
console.log(`🗑️ 删除属性: ${String(prop)}`);
return Reflect.deleteProperty(obj, prop);
}
});
}
// 高级的验证代理
static createValidatedProxy(target, validators = {}) {
return new Proxy(target, {
set(obj, prop, value, receiver) {
// 使用验证器
if (validators[prop]) {
const isValid = validators[prop](value);
if (!isValid) {
throw new Error(`Invalid value for ${String(prop)}: ${value}`);
}
}
console.log(`✅ 验证通过,设置: ${String(prop)} = ${value}`);
return Reflect.set(obj, prop, value, receiver);
},
get(obj, prop, receiver) {
const value = Reflect.get(obj, prop, receiver);
// 自动转换某些属性
if (prop === 'fullName' && !Reflect.has(obj, prop)) {
return `${obj.firstName || ''} ${obj.lastName || ''}`.trim();
}
return value;
},
ownKeys(obj) {
const keys = Reflect.ownKeys(obj);
// 添加计算属性到键列表
if (obj.firstName && obj.lastName && !keys.includes('fullName')) {
keys.push('fullName');
}
return keys;
},
getOwnPropertyDescriptor(obj, prop) {
if (prop === 'fullName' && obj.firstName && obj.lastName) {
return {
enumerable: true,
configurable: true,
get() {
return `${obj.firstName} ${obj.lastName}`;
}
};
}
return Reflect.getOwnPropertyDescriptor(obj, prop);
}
});
}
// 方法调用拦截器
static createMethodInterceptor(target) {
return new Proxy(target, {
get(obj, prop, receiver) {
const value = Reflect.get(obj, prop, receiver);
// 如果是方法,包装它
if (typeof value === 'function') {
return new Proxy(value, {
apply(fn, thisArg, argumentsList) {
console.log(`🎯 调用方法: ${String(prop)}`);
console.log(`📥 参数:`, argumentsList);
const startTime = performance.now();
try {
const result = Reflect.apply(fn, thisArg, argumentsList);
const endTime = performance.now();
console.log(`⏱️ 执行时间: ${(endTime - startTime).toFixed(2)}ms`);
console.log(`📤 返回值:`, result);
return result;
} catch (error) {
console.log(`❌ 方法执行失败:`, error.message);
throw error;
}
}
});
}
return value;
}
});
}
}
// 使用示例
console.log('=== Proxy + Reflect 集成演示 ===\n');
// 1. 基础代理
console.log('1. 基础代理:');
const basicObj = { name: 'Test', value: 123 };
const basicProxy = ProxyReflectIntegration.createBasicProxy(basicObj);
console.log('访问 name:', basicProxy.name);
basicProxy.newProp = 'new value';
console.log('检查 newProp:', 'newProp' in basicProxy);
delete basicProxy.value;
console.log();
// 2. 验证代理
console.log('2. 验证代理:');
const user = {};
const validatedUser = ProxyReflectIntegration.createValidatedProxy(user, {
age: (value) => typeof value === 'number' && value >= 0 && value <= 150,
email: (value) => typeof value === 'string' && value.includes('@')
});
validatedUser.firstName = 'John';
validatedUser.lastName = 'Doe';
validatedUser.age = 30;
console.log('计算属性 fullName:', validatedUser.fullName);
console.log('对象键:', Object.keys(validatedUser));
try {
validatedUser.age = -5; // 会抛出错误
} catch (error) {
console.log('验证失败:', error.message);
}
console.log();
// 3. 方法拦截器
console.log('3. 方法拦截器:');
class Calculator {
add(a, b) {
return a + b;
}
multiply(a, b) {
const result = a * b;
// 模拟一些计算时间
for (let i = 0; i < 1000000; i++) {
Math.random();
}
return result;
}
divide(a, b) {
if (b === 0) throw new Error('Division by zero');
return a / b;
}
}
const calc = new Calculator();
const interceptedCalc = ProxyReflectIntegration.createMethodInterceptor(calc);
console.log('执行加法:');
interceptedCalc.add(5, 3);
console.log('\n执行乘法:');
interceptedCalc.multiply(4, 7);
console.log('\n执行除法(错误情况):');
try {
interceptedCalc.divide(10, 0);
} catch (error) {
console.log('捕获到错误');
}
4. Reflect 的优势和最佳实践:
// Reflect API 的优势展示
class ReflectAdvantages {
// 1. 函数式的对象操作
static functionalObjectOperations() {
console.log('=== 函数式对象操作 ===');
const obj = { a: 1, b: 2, c: 3 };
// 传统方式 vs Reflect 方式
console.log('传统方式的问题:');
// 传统的属性操作可能会有异常
try {
obj.newProp = 'value'; // 可能会静默失败(在严格模式下)
delete obj.c;
console.log('has property a:', 'a' in obj);
} catch (error) {
console.log('操作失败:', error.message);
}
console.log('\nReflect 方式的优势:');
// Reflect 方法总是返回布尔值表示成功/失败
const setResult = Reflect.set(obj, 'reflectProp', 'reflect value');
console.log('设置属性成功:', setResult);
const deleteResult = Reflect.deleteProperty(obj, 'b');
console.log('删除属性成功:', deleteResult);
const hasResult = Reflect.has(obj, 'a');
console.log('属性存在:', hasResult);
console.log('最终对象:', obj);
console.log();
}
// 2. 更好的错误处理
static betterErrorHandling() {
console.log('=== 更好的错误处理 ===');
const obj = {};
// 使用 Object.defineProperty 的问题
try {
Object.defineProperty(obj, 'prop1', {
value: 'value1',
writable: false,
configurable: false
});
// 这会抛出错误
Object.defineProperty(obj, 'prop1', {
value: 'new value'
});
} catch (error) {
console.log('Object.defineProperty 错误:', error.message);
}
// 使用 Reflect.defineProperty 的优势
const success1 = Reflect.defineProperty(obj, 'prop2', {
value: 'value2',
writable: false,
configurable: false
});
console.log('第一次定义成功:', success1); // true
const success2 = Reflect.defineProperty(obj, 'prop2', {
value: 'new value'
});
console.log('第二次定义成功:', success2); // false,但不会抛错
console.log();
}
// 3. 与 Proxy 的完美配合
static proxyIntegration() {
console.log('=== 与 Proxy 的完美配合 ===');
// 没有 Reflect 的 Proxy 处理器
const withoutReflect = new Proxy({}, {
get(target, property) {
console.log(`Getting ${String(property)}`);
return target[property]; // 可能不会正确处理 receiver
},
set(target, property, value) {
console.log(`Setting ${String(property)} = ${value}`);
target[property] = value;
return true; // 总是返回 true,可能不准确
}
});
// 使用 Reflect 的 Proxy 处理器
const withReflect = new Proxy({}, {
get(target, property, receiver) {
console.log(`[Reflect] Getting ${String(property)}`);
return Reflect.get(target, property, receiver); // 正确处理 receiver
},
set(target, property, value, receiver) {
console.log(`[Reflect] Setting ${String(property)} = ${value}`);
return Reflect.set(target, property, value, receiver); // 返回实际结果
}
});
console.log('测试不使用 Reflect 的代理:');
withoutReflect.test = 'value';
console.log('获取值:', withoutReflect.test);
console.log('\n测试使用 Reflect 的代理:');
withReflect.test = 'value';
console.log('获取值:', withReflect.test);
console.log();
}
// 4. 动态对象操作工具
static createObjectUtilities() {
console.log('=== 动态对象操作工具 ===');
class ObjectUtils {
// 安全的属性设置
static safeSet(obj, path, value) {
const keys = path.split('.');
let current = obj;
for (let i = 0; i < keys.length - 1; i++) {
const key = keys[i];
if (!Reflect.has(current, key) || typeof current[key] !== 'object') {
const success = Reflect.set(current, key, {});
if (!success) return false;
}
current = current[key];
}
return Reflect.set(current, keys[keys.length - 1], value);
}
// 安全的属性获取
static safeGet(obj, path, defaultValue = undefined) {
const keys = path.split('.');
let current = obj;
for (const key of keys) {
if (!Reflect.has(current, key)) {
return defaultValue;
}
current = Reflect.get(current, key);
}
return current;
}
// 深度合并对象
static deepMerge(target, source) {
const result = { ...target };
for (const key of Reflect.ownKeys(source)) {
const sourceValue = Reflect.get(source, key);
const targetValue = Reflect.get(result, key);
if (typeof sourceValue === 'object' && sourceValue !== null &&
typeof targetValue === 'object' && targetValue !== null) {
Reflect.set(result, key, this.deepMerge(targetValue, sourceValue));
} else {
Reflect.set(result, key, sourceValue);
}
}
return result;
}
// 对象属性映射
static mapObject(obj, mapper) {
const result = {};
for (const key of Reflect.ownKeys(obj)) {
const value = Reflect.get(obj, key);
const [newKey, newValue] = mapper(key, value);
Reflect.set(result, newKey, newValue);
}
return result;
}
}
// 测试工具函数
const testObj = { a: { b: { c: 1 } } };
console.log('安全设置属性:');
ObjectUtils.safeSet(testObj, 'x.y.z', 'deep value');
console.log('设置后:', testObj);
console.log('安全获取属性:');
console.log('存在的路径:', ObjectUtils.safeGet(testObj, 'a.b.c'));
console.log('不存在的路径:', ObjectUtils.safeGet(testObj, 'a.b.d', 'default'));
console.log('深度合并:');
const obj1 = { a: { x: 1, y: 2 }, b: 3 };
const obj2 = { a: { y: 20, z: 30 }, c: 4 };
const merged = ObjectUtils.deepMerge(obj1, obj2);
console.log('合并结果:', merged);
console.log('对象映射:');
const numbers = { one: 1, two: 2, three: 3 };
const doubled = ObjectUtils.mapObject(numbers, (key, value) => [
key.toUpperCase(),
value * 2
]);
console.log('映射结果:', doubled);
return ObjectUtils;
}
}
// 运行所有演示
ReflectAdvantages.functionalObjectOperations();
ReflectAdvantages.betterErrorHandling();
ReflectAdvantages.proxyIntegration();
ReflectAdvantages.createObjectUtilities();
Reflect API 的核心价值总结:
Reflect API 虽然看似简单,但它是现代 JavaScript 元编程的重要基础,特别是在结合 Proxy 使用时,能够创建出功能强大且安全的对象操作模式。
How does ES6 optimize performance? What new features help improve performance?
How does ES6 optimize performance? What new features help improve performance?
考察点:尾调用优化、懒加载、数据结构选择。
答案:
ES6 引入了多个有助于性能提升的新特性,这些特性不仅提供了更简洁的语法,更重要的是在底层实现上带来了性能优化。让我们深入分析这些性能优化点。
1. let/const vs var 的性能优化:
// 性能对比演示:let/const vs var
class ScopePerformanceComparison {
// 测试块级作用域的性能优势
static testBlockScopePerformance() {
console.log('=== 块级作用域性能测试 ===');
// 使用 var 的传统循环(可能的性能问题)
console.time('var 循环');
for (var i = 0; i < 1000000; i++) {
// var 变量会被提升,可能影响优化
var temp = i * 2;
}
console.timeEnd('var 循环');
// 使用 let 的现代循环(更好的优化)
console.time('let 循环');
for (let j = 0; j < 1000000; j++) {
// let 变量有明确的作用域,利于引擎优化
let temp = j * 2;
}
console.timeEnd('let 循环');
// const 用于不变值(最佳优化)
console.time('const 常量');
const multiplier = 2;
for (let k = 0; k < 1000000; k++) {
// 引擎知道 multiplier 不会改变,可以进行激进优化
const result = k * multiplier;
}
console.timeEnd('const 常量');
console.log();
}
}
// 运行测试
ScopePerformanceComparison.testBlockScopePerformance();
2. 箭头函数的性能优势:
// 箭头函数性能分析
class ArrowFunctionPerformance {
constructor() {
this.data = Array.from({ length: 100000 }, (_, i) => i);
}
// 测试函数绑定性能
testFunctionBinding() {
console.log('=== 函数绑定性能测试 ===');
// 传统函数需要绑定 this
console.time('传统函数 + bind');
const traditionalResult = this.data.map(function(x) {
return this.processValue(x);
}.bind(this));
console.timeEnd('传统函数 + bind');
// 箭头函数自动绑定词法 this
console.time('箭头函数');
const arrowResult = this.data.map(x => this.processValue(x));
console.timeEnd('箭头函数');
console.log('结果长度一致:', traditionalResult.length === arrowResult.length);
console.log();
}
processValue(x) {
return x * 2 + 1;
}
}
const arrowPerf = new ArrowFunctionPerformance();
arrowPerf.testFunctionBinding();
3. 新数据结构的性能优势:
// ES6 数据结构性能分析
class ES6DataStructurePerformance {
static testSetVsArray() {
console.log('=== Set vs Array 性能测试 ===');
const size = 10000;
const testData = Array.from({ length: size }, (_, i) => i);
// 创建测试数据
const testArray = [...testData];
const testSet = new Set(testData);
// 查找性能测试
const searchItems = Array.from({ length: 1000 }, () =>
Math.floor(Math.random() * size));
console.time('Array includes 查找');
let foundInArray = 0;
for (const item of searchItems) {
if (testArray.includes(item)) {
foundInArray++;
}
}
console.timeEnd('Array includes 查找');
console.time('Set has 查找');
let foundInSet = 0;
for (const item of searchItems) {
if (testSet.has(item)) {
foundInSet++;
}
}
console.timeEnd('Set has 查找');
console.log('Set查找速度明显快于Array.includes');
console.log();
}
static testMapVsObject() {
console.log('=== Map vs Object 性能测试 ===');
const size = 10000;
const testObj = {};
const testMap = new Map();
// 插入性能测试
console.time('Object 属性设置');
for (let i = 0; i < size; i++) {
testObj[`key${i}`] = `value${i}`;
}
console.timeEnd('Object 属性设置');
console.time('Map set 操作');
for (let i = 0; i < size; i++) {
testMap.set(`key${i}`, `value${i}`);
}
console.timeEnd('Map set 操作');
console.log('Map在频繁的键值操作中性能更稳定');
console.log();
}
}
ES6DataStructurePerformance.testSetVsArray();
ES6DataStructurePerformance.testMapVsObject();
4. 迭代器和 for…of 的性能:
// 迭代器性能分析
class IteratorPerformance {
constructor() {
this.testArray = Array.from({ length: 100000 }, (_, i) => i);
}
// 数组遍历性能对比
testArrayIteration() {
console.log('=== 数组遍历性能测试 ===');
let sum = 0;
// 传统 for 循环
console.time('传统 for 循环');
sum = 0;
for (let i = 0; i < this.testArray.length; i++) {
sum += this.testArray[i];
}
console.timeEnd('传统 for 循环');
// for...of 循环
console.time('for...of 循环');
sum = 0;
for (const value of this.testArray) {
sum += value;
}
console.timeEnd('for...of 循环');
// forEach 方法
console.time('forEach 方法');
sum = 0;
this.testArray.forEach(value => sum += value);
console.timeEnd('forEach 方法');
console.log('for...of 在大多数情况下性能接近传统for循环');
console.log();
}
}
const iterPerf = new IteratorPerformance();
iterPerf.testArrayIteration();
5. 模板字符串性能分析:
// 模板字符串性能分析
class TemplateStringPerformance {
static testStringConcatenation() {
console.log('=== 字符串拼接性能测试 ===');
const name = 'JavaScript';
const version = 'ES6';
const year = 2015;
const iterations = 100000;
// 传统字符串拼接
console.time('传统拼接');
for (let i = 0; i < iterations; i++) {
const result = 'Hello ' + name + ', welcome to ' + version + ' (' + year + ')!';
}
console.timeEnd('传统拼接');
// 模板字符串
console.time('模板字符串');
for (let i = 0; i < iterations; i++) {
const result = `Hello ${name}, welcome to ${version} (${year})!`;
}
console.timeEnd('模板字符串');
console.log('模板字符串性能与传统拼接相当,但代码更清晰');
console.log();
}
}
TemplateStringPerformance.testStringConcatenation();
6. 解构赋值的性能考量:
// 解构赋值性能分析
class DestructuringPerformance {
static testObjectDestructuring() {
console.log('=== 对象解构性能测试 ===');
const testObj = {
a: 1, b: 2, c: 3, d: 4, e: 5,
nested: { x: 10, y: 20, z: 30 }
};
const iterations = 1000000;
// 传统属性访问
console.time('传统属性访问');
for (let i = 0; i < iterations; i++) {
const a = testObj.a;
const b = testObj.b;
const c = testObj.c;
}
console.timeEnd('传统属性访问');
// 对象解构
console.time('对象解构');
for (let i = 0; i < iterations; i++) {
const { a, b, c } = testObj;
}
console.timeEnd('对象解构');
console.log('适度的解构性能良好,但避免过度深层解构');
console.log();
}
}
DestructuringPerformance.testObjectDestructuring();
7. ES6 模块的性能优势:
// ES6 模块性能优势演示
console.log('=== ES6 模块性能优势 ===');
console.log('1. 静态分析:编译时确定依赖关系');
console.log('2. Tree Shaking:死代码消除');
console.log('3. 循环依赖处理:更安全的依赖管理');
console.log('4. 懒加载:动态导入支持按需加载');
// 动态导入示例(性能优化)
async function loadModuleWhenNeeded() {
if (someCondition) {
// 只在需要时加载模块
const module = await import('./heavyModule.js');
return module.default;
}
}
// Tree Shaking 友好的导出
export const utilFunction1 = () => { /* ... */ };
export const utilFunction2 = () => { /* ... */ };
// 未使用的 utilFunction2 会被打包工具移除
8. 生成器的内存优势:
// 生成器内存性能演示
class GeneratorPerformance {
// 传统方式:一次性生成所有数据
static generateArrayTraditional(size) {
console.time('传统数组生成');
const result = [];
for (let i = 0; i < size; i++) {
result.push(i * i);
}
console.timeEnd('传统数组生成');
return result;
}
// 生成器方式:按需生成
static *generateNumbersLazy(size) {
for (let i = 0; i < size; i++) {
yield i * i;
}
}
static testMemoryUsage() {
console.log('=== 生成器内存性能测试 ===');
const size = 100000;
// 传统方式:占用大量内存
const traditionalArray = this.generateArrayTraditional(size);
console.log('传统数组内存占用:约', (size * 8 / 1024 / 1024).toFixed(2), 'MB');
// 生成器方式:按需生成,节省内存
console.time('生成器创建');
const lazyGenerator = this.generateNumbersLazy(size);
console.timeEnd('生成器创建');
console.log('生成器内存占用:极小(延迟计算)');
// 使用生成器进行部分计算
console.time('生成器部分使用');
let sum = 0;
let count = 0;
for (const value of lazyGenerator) {
sum += value;
count++;
if (count >= 1000) break; // 只使用前1000个
}
console.timeEnd('生成器部分使用');
console.log('只计算了需要的部分,节省了99%的计算');
console.log();
}
}
GeneratorPerformance.testMemoryUsage();
9. 实际应用中的性能优化策略:
// ES6 性能优化最佳实践
class ES6PerformanceBestPractices {
// 1. 合理使用解构赋值
static optimizedDestructuring() {
// ❌ 避免过度深层解构
function inefficient({ user: { profile: { settings: { theme } } } }) {
return theme;
}
// ✅ 推荐的优化方式
function efficient({ user }) {
return user.profile.settings.theme;
}
}
// 2. 智能使用 const 和 let
static optimizedVariableDeclarations() {
// ✅ 使用 const 帮助引擎优化
const MULTIPLIER = 2;
const processNumbers = (numbers) => {
return numbers.map(num => num * MULTIPLIER); // 引擎可以内联常量
};
}
// 3. 高效的数组操作
static optimizedArrayOperations(data) {
// ❌ 多次遍历创建中间数组
const inefficient = data
.filter(item => item.active)
.map(item => item.value * 2)
.filter(value => value > 100);
// ✅ 单次遍历优化
const efficient = data.reduce((acc, item) => {
if (item.active) {
const processed = item.value * 2;
if (processed > 100) {
acc.push(processed);
}
}
return acc;
}, []);
return { inefficient, efficient };
}
// 4. 字符串操作优化
static optimizedStringOperations(items) {
// ❌ 频繁的字符串拼接
let inefficient = '';
for (const item of items) {
inefficient += `<li>${item}</li>`;
}
// ✅ 数组缓冲优化
const buffer = [];
for (const item of items) {
buffer.push(`<li>${item}</li>`);
}
const efficient = buffer.join('');
return { inefficient, efficient };
}
}
10. 性能监控和测试工具:
// ES6 性能测试工具类
class PerformanceProfiler {
static profile(name, fn, iterations = 1000000) {
console.log(`\n=== 性能测试: ${name} ===`);
// 预热
for (let i = 0; i < 1000; i++) {
fn();
}
// 正式测试
const startTime = performance.now();
const startMemory = performance.memory ? performance.memory.usedJSHeapSize : 0;
for (let i = 0; i < iterations; i++) {
fn();
}
const endTime = performance.now();
const endMemory = performance.memory ? performance.memory.usedJSHeapSize : 0;
console.log(`执行时间: ${(endTime - startTime).toFixed(2)}ms`);
console.log(`每次调用: ${((endTime - startTime) / iterations * 1000).toFixed(4)}μs`);
if (performance.memory) {
console.log(`内存变化: ${((endMemory - startMemory) / 1024 / 1024).toFixed(2)}MB`);
}
return {
totalTime: endTime - startTime,
avgTime: (endTime - startTime) / iterations,
memoryDelta: endMemory - startMemory
};
}
static compare(tests) {
console.log('\n=== 性能对比结果 ===');
const results = {};
for (const [name, fn] of Object.entries(tests)) {
results[name] = this.profile(name, fn, 100000);
}
// 找出最快的实现
const fastest = Object.keys(results).reduce((a, b) =>
results[a].totalTime < results[b].totalTime ? a : b);
console.log(`\n最快实现: ${fastest}`);
// 显示相对性能
Object.keys(results).forEach(name => {
const ratio = (results[name].totalTime / results[fastest].totalTime).toFixed(2);
console.log(`${name}: ${ratio}x ${name === fastest ? '(基准)' : '慢于基准'}`);
});
return results;
}
}
// 使用示例
const tests = {
'ES5 函数': function() {
return [1, 2, 3].map(function(x) { return x * 2; });
},
'ES6 箭头函数': function() {
return [1, 2, 3].map(x => x * 2);
},
'ES6 解构': function() {
const [a, b, c] = [1, 2, 3];
return [a * 2, b * 2, c * 2];
}
};
PerformanceProfiler.compare(tests);
ES6 性能优化核心要点总结:
语法级优化:
const/let 提供更好的作用域管理,帮助引擎优化数据结构优化:
Set/Map 在特定场景下性能显著优于传统结构WeakMap/WeakSet 防止内存泄漏迭代优化:
for...of 和迭代器支持更高效的遍历模块化优势:
最佳实践:
这些优化不仅提升了代码的可读性和维护性,更重要的是为 JavaScript 引擎提供了更多优化机会,从而获得更好的运行时性能。
How to implement a simple template engine (supporting interpolation expressions)?
How to implement a simple template engine (supporting interpolation expressions)?
考察点:字符串处理与正则表达式。
答案:
实现一个模板引擎涉及字符串解析、表达式求值、作用域管理等多个技术点。让我们从基础到高级,逐步实现一个功能完整的ES6模板引擎。
1. 基础版本 - 简单插值:
// 基础模板引擎实现
class SimpleTemplateEngine {
constructor() {
// 插值表达式的正则模式:{{ expression }}
this.interpolationPattern = /\{\{([^}]+)\}\}/g;
}
// 基础渲染方法
render(template, data = {}) {
return template.replace(this.interpolationPattern, (match, expression) => {
// 清理表达式(移除首尾空格)
const cleanExpression = expression.trim();
try {
// 简单的属性访问
return this.evaluateExpression(cleanExpression, data);
} catch (error) {
console.warn(`模板表达式错误: ${cleanExpression}`, error);
return match; // 返回原始表达式
}
});
}
// 简单的表达式求值
evaluateExpression(expression, data) {
// 支持点号访问:user.name
const properties = expression.split('.');
let result = data;
for (const prop of properties) {
if (result && typeof result === 'object' && prop in result) {
result = result[prop];
} else {
return ''; // 属性不存在返回空字符串
}
}
return result != null ? String(result) : '';
}
// 使用示例
static demo() {
console.log('=== 基础模板引擎演示 ===');
const engine = new SimpleTemplateEngine();
// 测试数据
const data = {
name: 'Alice',
age: 30,
user: {
profile: {
email: '[email protected]'
}
}
};
// 测试模板
const templates = [
'Hello, {{ name }}!',
'You are {{ age }} years old.',
'Email: {{ user.profile.email }}',
'Missing: {{ missing.property }}' // 测试不存在的属性
];
templates.forEach(template => {
const result = engine.render(template, data);
console.log(`模板: ${template}`);
console.log(`结果: ${result}\n`);
});
}
}
SimpleTemplateEngine.demo();
2. 进阶版本 - 支持表达式和过滤器:
// 进阶模板引擎 - 支持表达式计算和过滤器
class AdvancedTemplateEngine {
constructor() {
// 更复杂的插值模式,支持过滤器:{{ expression | filter }}
this.interpolationPattern = /\{\{([^}]+)\}\}/g;
this.filterPattern = /\|/;
// 内置过滤器
this.filters = new Map([
['upper', value => String(value).toUpperCase()],
['lower', value => String(value).toLowerCase()],
['capitalize', value => {
const str = String(value);
return str.charAt(0).toUpperCase() + str.slice(1).toLowerCase();
}],
['json', value => JSON.stringify(value)],
['default', (value, defaultValue = '') => value != null ? value : defaultValue],
['truncate', (value, length = 50) => {
const str = String(value);
return str.length > length ? str.substring(0, length) + '...' : str;
}],
['date', value => {
if (value instanceof Date) return value.toLocaleDateString();
if (typeof value === 'string' || typeof value === 'number') {
return new Date(value).toLocaleDateString();
}
return String(value);
}]
]);
}
// 添加自定义过滤器
addFilter(name, filterFunction) {
this.filters.set(name, filterFunction);
return this;
}
// 渲染模板
render(template, data = {}) {
return template.replace(this.interpolationPattern, (match, expression) => {
try {
return this.processExpression(expression.trim(), data);
} catch (error) {
console.warn(`模板表达式错误: ${expression}`, error);
return match;
}
});
}
// 处理表达式(包括过滤器)
processExpression(expression, data) {
// 检查是否有过滤器
if (this.filterPattern.test(expression)) {
const [expr, ...filterChain] = expression.split('|').map(s => s.trim());
let result = this.evaluateExpression(expr, data);
// 应用过滤器链
for (const filterExpr of filterChain) {
result = this.applyFilter(result, filterExpr);
}
return result;
} else {
return this.evaluateExpression(expression, data);
}
}
// 应用过滤器
applyFilter(value, filterExpression) {
// 解析过滤器名称和参数:filter(arg1, arg2)
const filterMatch = filterExpression.match(/^([^(]+)(?:\(([^)]*)\))?$/);
if (!filterMatch) {
throw new Error(`无效的过滤器表达式: ${filterExpression}`);
}
const filterName = filterMatch[1].trim();
const argsString = filterMatch[2];
if (!this.filters.has(filterName)) {
throw new Error(`未知的过滤器: ${filterName}`);
}
const filter = this.filters.get(filterName);
// 解析参数
if (argsString) {
const args = this.parseArguments(argsString);
return filter(value, ...args);
} else {
return filter(value);
}
}
// 解析过滤器参数
parseArguments(argsString) {
const args = [];
const argPattern = /(?:[^,"]|"[^"]*")+/g;
const matches = argsString.match(argPattern) || [];
for (const match of matches) {
const arg = match.trim();
// 解析不同类型的参数
if (arg.startsWith('"') && arg.endsWith('"')) {
// 字符串参数
args.push(arg.slice(1, -1));
} else if (!isNaN(arg)) {
// 数字参数
args.push(Number(arg));
} else if (arg === 'true' || arg === 'false') {
// 布尔参数
args.push(arg === 'true');
} else {
// 其他类型保持字符串
args.push(arg);
}
}
return args;
}
// 支持更复杂的表达式求值
evaluateExpression(expression, data) {
// 支持数组索引:items[0],对象属性:user.name
try {
// 创建安全的求值环境
const safeEval = this.createSafeEvaluator(data);
return safeEval(expression);
} catch (error) {
// 如果安全求值失败,回退到简单属性访问
return this.simplePropertyAccess(expression, data);
}
}
// 创建安全的求值器
createSafeEvaluator(data) {
return (expression) => {
// 白名单安全的表达式模式
const safePatterns = [
/^[a-zA-Z_$][a-zA-Z0-9_$]*$/, // 简单变量名
/^[a-zA-Z_$][a-zA-Z0-9_$.[\]"']*$/ // 属性访问和数组访问
];
const isSafe = safePatterns.some(pattern => pattern.test(expression));
if (!isSafe) {
throw new Error(`不安全的表达式: ${expression}`);
}
// 使用 Function 构造器创建安全的求值环境
const keys = Object.keys(data);
const values = keys.map(key => data[key]);
try {
const func = new Function(...keys, `return ${expression};`);
return func(...values);
} catch (error) {
throw new Error(`表达式求值失败: ${expression}`);
}
};
}
// 简单属性访问(回退方案)
simplePropertyAccess(expression, data) {
const properties = expression.split('.');
let result = data;
for (const prop of properties) {
if (result && typeof result === 'object') {
// 处理数组索引:prop[index]
const arrayMatch = prop.match(/^([^[]+)\[(\d+)\]$/);
if (arrayMatch) {
const [, arrayName, index] = arrayMatch;
result = result[arrayName];
if (Array.isArray(result)) {
result = result[parseInt(index)];
} else {
return '';
}
} else {
result = result[prop];
}
} else {
return '';
}
}
return result != null ? String(result) : '';
}
// 使用示例
static demo() {
console.log('=== 进阶模板引擎演示 ===');
const engine = new AdvancedTemplateEngine();
// 添加自定义过滤器
engine.addFilter('repeat', (value, times = 2) =>
String(value).repeat(times));
// 测试数据
const data = {
name: 'alice johnson',
age: 25,
items: ['apple', 'banana', 'orange'],
user: {
profile: {
bio: 'This is a very long biography that should be truncated when displayed in the template to maintain readability.'
}
},
createdAt: new Date(),
description: null
};
// 测试模板
const templates = [
'{{ name | capitalize }}',
'{{ name | upper }}',
'{{ items | json }}',
'{{ items[1] | upper }}',
'{{ user.profile.bio | truncate(30) }}',
'{{ description | default("No description") }}',
'{{ createdAt | date }}',
'{{ name | repeat(3) }}',
'{{ age }} years old'
];
templates.forEach(template => {
const result = engine.render(template, data);
console.log(`模板: ${template}`);
console.log(`结果: ${result}\n`);
});
}
}
AdvancedTemplateEngine.demo();
3. 完整版本 - 支持条件语句和循环:
// 完整功能模板引擎
class FullFeaturedTemplateEngine {
constructor() {
// 各种模板语法的正则模式
this.patterns = {
interpolation: /\{\{([^}]+)\}\}/g,
ifBlock: /\{\{\s*#if\s+([^}]+)\s*\}\}([\s\S]*?)\{\{\s*\/if\s*\}\}/g,
ifElseBlock: /\{\{\s*#if\s+([^}]+)\s*\}\}([\s\S]*?)\{\{\s*#else\s*\}\}([\s\S]*?)\{\{\s*\/if\s*\}\}/g,
eachBlock: /\{\{\s*#each\s+([^}]+)\s*\}\}([\s\S]*?)\{\{\s*\/each\s*\}\}/g,
unlessBlock: /\{\{\s*#unless\s+([^}]+)\s*\}\}([\s\S]*?)\{\{\s*\/unless\s*\}\}/g
};
// 继承过滤器功能
this.filters = new Map([
['upper', value => String(value).toUpperCase()],
['lower', value => String(value).toLowerCase()],
['capitalize', value => {
const str = String(value);
return str.charAt(0).toUpperCase() + str.slice(1).toLowerCase();
}],
['json', value => JSON.stringify(value, null, 2)],
['default', (value, defaultValue = '') => value != null ? value : defaultValue],
['truncate', (value, length = 50) => {
const str = String(value);
return str.length > length ? str.substring(0, length) + '...' : str;
}],
['length', value => Array.isArray(value) || typeof value === 'string' ? value.length : 0],
['join', (value, separator = ',') => Array.isArray(value) ? value.join(separator) : String(value)]
]);
}
// 主渲染方法
render(template, data = {}) {
let result = template;
// 1. 处理条件语句 (if/else)
result = this.processIfElseBlocks(result, data);
result = this.processIfBlocks(result, data);
result = this.processUnlessBlocks(result, data);
// 2. 处理循环语句 (each)
result = this.processEachBlocks(result, data);
// 3. 处理插值表达式
result = this.processInterpolation(result, data);
return result;
}
// 处理 if/else 条件块
processIfElseBlocks(template, data) {
return template.replace(this.patterns.ifElseBlock, (match, condition, ifContent, elseContent) => {
try {
const isTrue = this.evaluateCondition(condition.trim(), data);
return isTrue ? this.render(ifContent, data) : this.render(elseContent, data);
} catch (error) {
console.warn(`条件语句错误: ${condition}`, error);
return '';
}
});
}
// 处理 if 条件块
processIfBlocks(template, data) {
return template.replace(this.patterns.ifBlock, (match, condition, content) => {
try {
const isTrue = this.evaluateCondition(condition.trim(), data);
return isTrue ? this.render(content, data) : '';
} catch (error) {
console.warn(`条件语句错误: ${condition}`, error);
return '';
}
});
}
// 处理 unless 条件块
processUnlessBlocks(template, data) {
return template.replace(this.patterns.unlessBlock, (match, condition, content) => {
try {
const isTrue = this.evaluateCondition(condition.trim(), data);
return !isTrue ? this.render(content, data) : '';
} catch (error) {
console.warn(`Unless语句错误: ${condition}`, error);
return '';
}
});
}
// 处理 each 循环块
processEachBlocks(template, data) {
return template.replace(this.patterns.eachBlock, (match, expression, content) => {
try {
const [itemName, arrayExpr] = this.parseEachExpression(expression.trim());
const array = this.evaluateExpression(arrayExpr, data);
if (!Array.isArray(array)) {
return '';
}
return array.map((item, index) => {
// 为每个循环项创建新的数据作用域
const itemData = {
...data,
[itemName]: item,
[`${itemName}Index`]: index,
[`${itemName}First`]: index === 0,
[`${itemName}Last`]: index === array.length - 1
};
return this.render(content, itemData);
}).join('');
} catch (error) {
console.warn(`Each循环错误: ${expression}`, error);
return '';
}
});
}
// 解析 each 表达式:item in items
parseEachExpression(expression) {
const match = expression.match(/^(\w+)\s+in\s+(.+)$/);
if (!match) {
throw new Error(`无效的each表达式: ${expression}`);
}
return [match[1], match[2]];
}
// 处理插值表达式
processInterpolation(template, data) {
return template.replace(this.patterns.interpolation, (match, expression) => {
try {
return this.processExpression(expression.trim(), data);
} catch (error) {
console.warn(`插值表达式错误: ${expression}`, error);
return match;
}
});
}
// 处理表达式(包括过滤器)
processExpression(expression, data) {
if (expression.includes('|')) {
const [expr, ...filterChain] = expression.split('|').map(s => s.trim());
let result = this.evaluateExpression(expr, data);
for (const filterExpr of filterChain) {
result = this.applyFilter(result, filterExpr);
}
return result;
} else {
return this.evaluateExpression(expression, data);
}
}
// 应用过滤器
applyFilter(value, filterExpression) {
const filterMatch = filterExpression.match(/^([^(]+)(?:\(([^)]*)\))?$/);
if (!filterMatch) {
throw new Error(`无效的过滤器表达式: ${filterExpression}`);
}
const filterName = filterMatch[1].trim();
const argsString = filterMatch[2];
if (!this.filters.has(filterName)) {
throw new Error(`未知的过滤器: ${filterName}`);
}
const filter = this.filters.get(filterName);
if (argsString) {
const args = this.parseArguments(argsString);
return filter(value, ...args);
} else {
return filter(value);
}
}
// 解析参数
parseArguments(argsString) {
const args = [];
const argPattern = /(?:[^,"]|"[^"]*")+/g;
const matches = argsString.match(argPattern) || [];
for (const match of matches) {
const arg = match.trim();
if (arg.startsWith('"') && arg.endsWith('"')) {
args.push(arg.slice(1, -1));
} else if (!isNaN(arg)) {
args.push(Number(arg));
} else if (arg === 'true' || arg === 'false') {
args.push(arg === 'true');
} else {
args.push(arg);
}
}
return args;
}
// 条件求值
evaluateCondition(condition, data) {
// 处理比较操作符
const comparisonOperators = ['===', '!==', '==', '!=', '>=', '<=', '>', '<'];
for (const op of comparisonOperators) {
if (condition.includes(op)) {
const [left, right] = condition.split(op).map(s => s.trim());
const leftValue = this.evaluateExpression(left, data);
const rightValue = this.evaluateExpression(right, data);
switch (op) {
case '===': return leftValue === rightValue;
case '!==': return leftValue !== rightValue;
case '==': return leftValue == rightValue;
case '!=': return leftValue != rightValue;
case '>=': return leftValue >= rightValue;
case '<=': return leftValue <= rightValue;
case '>': return leftValue > rightValue;
case '<': return leftValue < rightValue;
}
}
}
// 简单的真值检查
const value = this.evaluateExpression(condition, data);
return Boolean(value);
}
// 表达式求值
evaluateExpression(expression, data) {
try {
// 处理字符串字面量
if ((expression.startsWith('"') && expression.endsWith('"')) ||
(expression.startsWith("'") && expression.endsWith("'"))) {
return expression.slice(1, -1);
}
// 处理数字字面量
if (!isNaN(expression) && !isNaN(parseFloat(expression))) {
return parseFloat(expression);
}
// 处理布尔字面量
if (expression === 'true') return true;
if (expression === 'false') return false;
if (expression === 'null') return null;
if (expression === 'undefined') return undefined;
// 处理属性访问
return this.safePropertyAccess(expression, data);
} catch (error) {
return '';
}
}
// 安全的属性访问
safePropertyAccess(expression, data) {
const properties = expression.split('.');
let result = data;
for (const prop of properties) {
if (result && typeof result === 'object') {
const arrayMatch = prop.match(/^([^[]+)\[(\d+)\]$/);
if (arrayMatch) {
const [, arrayName, index] = arrayMatch;
result = result[arrayName];
if (Array.isArray(result)) {
result = result[parseInt(index)];
} else {
return '';
}
} else {
result = result[prop];
}
} else {
return '';
}
}
return result != null ? String(result) : '';
}
// 添加自定义过滤器
addFilter(name, filterFunction) {
this.filters.set(name, filterFunction);
return this;
}
// 完整功能演示
static demo() {
console.log('=== 完整功能模板引擎演示 ===');
const engine = new FullFeaturedTemplateEngine();
// 测试数据
const data = {
title: 'My Blog',
user: {
name: 'Alice',
isAdmin: true,
posts: [
{ title: 'First Post', content: 'Hello World!', published: true },
{ title: 'Draft Post', content: 'Work in progress...', published: false },
{ title: 'Latest Post', content: 'This is the latest content.', published: true }
]
},
items: ['apple', 'banana', 'orange'],
showFooter: false
};
// 复杂模板示例
const template = `
<!DOCTYPE html>
<html>
<head>
<title>{{ title }}</title>
</head>
<body>
<h1>Welcome to {{ title }}</h1>
{{#if user.isAdmin}}
<p>Admin Panel: <a href="/admin">Manage Site</a></p>
{{/if}}
{{#unless showFooter}}
<p>Footer is hidden</p>
{{/unless}}
<h2>Published Posts:</h2>
{{#each post in user.posts}}
{{#if post.published}}
<article>
<h3>{{ post.title }}</h3>
<p>{{ post.content | truncate(20) }}</p>
<small>Post #{{ postIndex | default("unknown") }}</small>
{{#if postFirst}}
<span class="badge">First Post</span>
{{/if}}
{{#if postLast}}
<span class="badge">Latest Post</span>
{{/if}}
</article>
{{/if}}
{{/each}}
<h2>Available Items:</h2>
<ul>
{{#each item in items}}
<li>{{ item | capitalize }} ({{ itemIndex }})</li>
{{/each}}
</ul>
{{#if showFooter}}
<footer>
<p>© 2024 {{ title }}</p>
</footer>
{{else}}
<p>No footer to show.</p>
{{/if}}
</body>
</html>`;
const result = engine.render(template, data);
console.log('渲染结果:');
console.log(result);
}
}
FullFeaturedTemplateEngine.demo();
4. 性能优化和缓存版本:
// 高性能模板引擎(支持编译和缓存)
class OptimizedTemplateEngine extends FullFeaturedTemplateEngine {
constructor() {
super();
this.compiledCache = new Map();
this.compileMode = true;
}
// 编译模板为函数
compile(template) {
if (this.compiledCache.has(template)) {
return this.compiledCache.get(template);
}
const compiled = this.compileTemplate(template);
this.compiledCache.set(template, compiled);
return compiled;
}
// 模板编译器
compileTemplate(template) {
// 将模板转换为可执行的JavaScript代码
let code = 'let __output = "";\n';
// 预处理模板,提取所有的模板语法
const processed = this.preprocessTemplate(template);
code += processed;
code += 'return __output;';
// 创建编译后的函数
try {
return new Function('data', 'filters', 'engine', code);
} catch (error) {
console.error('模板编译失败:', error);
return () => '';
}
}
// 预处理模板
preprocessTemplate(template) {
let code = '';
let lastIndex = 0;
// 简化版:将插值表达式转换为代码
template.replace(this.patterns.interpolation, (match, expression, offset) => {
// 添加前面的静态文本
if (offset > lastIndex) {
const staticText = template.slice(lastIndex, offset);
code += `__output += ${JSON.stringify(staticText)};\n`;
}
// 添加动态表达式
code += `try {
const value = engine.processExpression(${JSON.stringify(expression.trim())}, data);
__output += String(value != null ? value : '');
} catch (error) {
__output += ${JSON.stringify(match)};
}\n`;
lastIndex = offset + match.length;
return match;
});
// 添加剩余的静态文本
if (lastIndex < template.length) {
const staticText = template.slice(lastIndex);
code += `__output += ${JSON.stringify(staticText)};\n`;
}
return code;
}
// 优化的渲染方法
render(template, data = {}) {
if (this.compileMode) {
const compiled = this.compile(template);
return compiled(data, this.filters, this);
} else {
return super.render(template, data);
}
}
// 性能测试
static performanceTest() {
console.log('=== 性能测试 ===');
const interpretedEngine = new FullFeaturedTemplateEngine();
const optimizedEngine = new OptimizedTemplateEngine();
const template = `
<h1>{{ title }}</h1>
{{#each item in items}}
<p>{{ item | upper }}</p>
{{/each}}
`;
const data = {
title: 'Performance Test',
items: Array.from({ length: 100 }, (_, i) => `Item ${i}`)
};
const iterations = 1000;
// 测试解释型引擎
console.time('解释型引擎');
for (let i = 0; i < iterations; i++) {
interpretedEngine.render(template, data);
}
console.timeEnd('解释型引擎');
// 测试编译型引擎(包含编译时间)
console.time('编译型引擎(含编译)');
for (let i = 0; i < iterations; i++) {
optimizedEngine.render(template, data);
}
console.timeEnd('编译型引擎(含编译)');
// 测试编译型引擎(预编译)
const compiled = optimizedEngine.compile(template);
console.time('编译型引擎(预编译)');
for (let i = 0; i < iterations; i++) {
compiled(data, optimizedEngine.filters, optimizedEngine);
}
console.timeEnd('编译型引擎(预编译)');
}
}
// OptimizedTemplateEngine.performanceTest();
5. 实际使用示例和最佳实践:
// 实用的模板引擎使用示例
class TemplateEngineUsage {
static blogExample() {
console.log('=== 博客系统示例 ===');
const engine = new FullFeaturedTemplateEngine();
// 添加自定义过滤器
engine.addFilter('markdown', (text) => {
// 简单的 markdown 处理
return text
.replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>')
.replace(/\*(.*?)\*/g, '<em>$1</em>')
.replace(/\n/g, '<br>');
});
engine.addFilter('timeAgo', (date) => {
const now = new Date();
const diff = now - new Date(date);
const minutes = Math.floor(diff / 60000);
const hours = Math.floor(minutes / 60);
const days = Math.floor(hours / 24);
if (days > 0) return `${days}天前`;
if (hours > 0) return `${hours}小时前`;
if (minutes > 0) return `${minutes}分钟前`;
return '刚刚';
});
const blogData = {
site: {
title: 'Tech Blog',
description: 'A blog about web development'
},
user: {
name: 'John Doe',
avatar: '/avatars/john.jpg',
isLoggedIn: true
},
posts: [
{
id: 1,
title: 'Getting Started with ES6',
content: '**ES6** brings many *exciting* features to JavaScript...',
author: 'John Doe',
publishedAt: new Date(Date.now() - 86400000), // 1天前
tags: ['javascript', 'es6', 'tutorial']
},
{
id: 2,
title: 'Understanding Template Engines',
content: 'Template engines help us separate *logic* from **presentation**...',
author: 'Jane Smith',
publishedAt: new Date(Date.now() - 3600000), // 1小时前
tags: ['templates', 'web-dev']
}
]
};
const blogTemplate = `
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>{{ site.title }} - {{ site.description }}</title>
<style>
body { font-family: Arial, sans-serif; max-width: 800px; margin: 0 auto; padding: 20px; }
.header { border-bottom: 1px solid #eee; padding-bottom: 20px; margin-bottom: 30px; }
.post { margin-bottom: 40px; padding: 20px; border: 1px solid #ddd; border-radius: 8px; }
.tags { margin-top: 10px; }
.tag { background: #007cba; color: white; padding: 3px 8px; border-radius: 12px; font-size: 12px; margin-right: 5px; }
.meta { color: #666; font-size: 14px; margin-top: 10px; }
.user-info { background: #f5f5f5; padding: 15px; border-radius: 5px; margin: 20px 0; }
</style>
</head>
<body>
<header class="header">
<h1>{{ site.title }}</h1>
<p>{{ site.description }}</p>
{{#if user.isLoggedIn}}
<div class="user-info">
<p>欢迎回来,<strong>{{ user.name }}</strong>!</p>
<a href="/logout">退出登录</a>
</div>
{{else}}
<div class="user-info">
<p><a href="/login">登录</a> 或 <a href="/register">注册</a></p>
</div>
{{/if}}
</header>
<main>
<h2>最新文章 (共{{ posts | length }}篇)</h2>
{{#each post in posts}}
<article class="post">
<h3>{{ post.title }}</h3>
<div class="content">{{ post.content | markdown }}</div>
<div class="tags">
标签:
{{#each tag in post.tags}}
<span class="tag">{{ tag }}</span>
{{/each}}
</div>
<div class="meta">
作者:{{ post.author }} |
发布时间:{{ post.publishedAt | timeAgo }} |
文章ID:{{ post.id }}
{{#if postFirst}}
| <strong>最新文章</strong>
{{/if}}
</div>
</article>
{{/each}}
{{#unless posts}}
<p>暂无文章,<a href="/write">立即写作</a></p>
{{/unless}}
</main>
<footer>
<p>© 2024 {{ site.title }}. All rights reserved.</p>
</footer>
</body>
</html>`;
const result = engine.render(blogTemplate, blogData);
console.log(result);
}
static emailTemplateExample() {
console.log('\n=== 邮件模板示例 ===');
const engine = new FullFeaturedTemplateEngine();
engine.addFilter('currency', (amount) =>
`¥${Number(amount).toFixed(2)}`);
const emailData = {
user: { name: 'Alice Wang' },
order: {
id: 'ORD-2024-001',
date: new Date().toLocaleDateString(),
items: [
{ name: 'MacBook Pro', price: 12999, quantity: 1 },
{ name: 'Magic Mouse', price: 649, quantity: 1 },
{ name: 'USB-C Cable', price: 149, quantity: 2 }
],
shipping: 29,
total: 13826
},
company: {
name: 'TechStore',
address: '北京市朝阳区科技园123号',
phone: '400-123-4567'
}
};
const emailTemplate = `
亲爱的 {{ user.name }},
感谢您在 {{ company.name }} 的购买!
订单详情:
订单号:{{ order.id }}
下单时间:{{ order.date }}
商品清单:
{{#each item in order.items}}
- {{ item.name }} × {{ item.quantity }} = {{ item.price | currency }}
{{/each}}
运费:{{ order.shipping | currency }}
总计:{{ order.total | currency }}
{{#if order.total > 10000}}
🎉 恭喜您!订单金额超过10,000元,享受免费配送服务!
{{/if}}
如有任何问题,请联系我们:
电话:{{ company.phone }}
地址:{{ company.address }}
祝您购物愉快!
{{ company.name }} 团队
`;
const result = engine.render(emailTemplate, emailData);
console.log(result);
}
}
// 运行示例
TemplateEngineUsage.blogExample();
TemplateEngineUsage.emailTemplateExample();
模板引擎核心特性总结:
基础功能:
{{ expression }}{{ user.name }}、{{ items[0] }}高级功能:
{{ name | upper }}{{#if}} {{else}} {{/if}}{{#each item in items}} {{/each}}itemIndex、itemFirst、itemLast性能优化:
实用特性:
这个模板引擎实现展示了如何使用ES6的新特性(如类、箭头函数、模板字符串、Map等)来构建一个功能完整且高性能的模板系统。
How does ES6 handle circular references? What are the solutions?
How does ES6 handle circular references? What are the solutions?
考察点:WeakMap、序列化算法。
答案:
循环引用是 JavaScript 中一个重要且复杂的问题,ES6 提供了多种工具和模式来处理这种情况。循环引用会影响对象的序列化、深拷贝、垃圾回收等多个方面。
1. 循环引用的基本概念和问题:
// 循环引用示例和问题演示
class CircularReferenceDemo {
// 创建循环引用的对象
static createCircularObject() {
console.log('=== 循环引用基本示例 ===');
const obj1 = { name: 'Object 1' };
const obj2 = { name: 'Object 2' };
// 创建循环引用
obj1.ref = obj2;
obj2.ref = obj1;
console.log('obj1.ref.name:', obj1.ref.name); // 'Object 2'
console.log('obj2.ref.name:', obj2.ref.name); // 'Object 1'
console.log('obj1.ref.ref === obj1:', obj1.ref.ref === obj1); // true
return { obj1, obj2 };
}
// 演示循环引用导致的问题
static demonstrateProblems() {
console.log('\n=== 循环引用导致的问题 ===');
const { obj1 } = this.createCircularObject();
// 1. JSON.stringify 问题
try {
JSON.stringify(obj1);
} catch (error) {
console.log('JSON.stringify 错误:', error.message);
}
// 2. 使用 WeakSet 检测循环引用
function safeTraverse(obj, visited = new WeakSet()) {
console.log('访问对象:', obj.name);
if (visited.has(obj)) {
console.log('检测到循环引用!');
return;
}
visited.add(obj);
if (obj.ref) {
safeTraverse(obj.ref, visited);
}
}
console.log('\n使用 WeakSet 检测循环引用:');
safeTraverse(obj1);
console.log();
}
}
CircularReferenceDemo.createCircularObject();
CircularReferenceDemo.demonstrateProblems();
2. 使用 WeakMap 实现深拷贝:
// 使用 WeakMap 解决循环引用的深拷贝
class DeepCloneWithWeakMap {
static deepClone(obj, cache = new WeakMap()) {
// 处理非对象类型
if (obj === null || typeof obj !== 'object') {
return obj;
}
// 检查是否已经克隆过(处理循环引用)
if (cache.has(obj)) {
return cache.get(obj);
}
// 处理特殊对象类型
if (obj instanceof Date) {
return new Date(obj.getTime());
}
if (obj instanceof RegExp) {
return new RegExp(obj.source, obj.flags);
}
// 创建新对象
const cloned = Array.isArray(obj) ? [] : {};
// 提前设置缓存,防止循环引用
cache.set(obj, cloned);
// 递归克隆属性
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
cloned[key] = this.deepClone(obj[key], cache);
}
}
return cloned;
}
// 演示
static demo() {
console.log('=== WeakMap 深拷贝演示 ===');
// 创建循环引用对象
const parent = { name: 'Parent', children: [] };
const child1 = { name: 'Child1', parent: parent };
const child2 = { name: 'Child2', parent: parent };
parent.children.push(child1, child2);
// 深拷贝
const clonedParent = this.deepClone(parent);
console.log('原对象:', parent.children[0].parent === parent);
console.log('克隆对象:', clonedParent.children[0].parent === clonedParent);
console.log('不是同一对象:', clonedParent !== parent);
console.log();
}
}
DeepCloneWithWeakMap.demo();
3. 循环引用安全序列化:
// 循环引用安全序列化
class SafeSerializer {
static stringify(obj, visited = new WeakSet()) {
if (obj === null) return 'null';
if (typeof obj !== 'object') return JSON.stringify(obj);
// 检查循环引用
if (visited.has(obj)) {
return '"[Circular Reference]"';
}
visited.add(obj);
try {
if (Array.isArray(obj)) {
const items = obj.map(item => this.stringify(item, visited));
return '[' + items.join(',') + ']';
} else {
const pairs = [];
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
const value = this.stringify(obj[key], visited);
pairs.push(`"${key}":${value}`);
}
}
return '{' + pairs.join(',') + '}';
}
} finally {
visited.delete(obj);
}
}
// 演示
static demo() {
console.log('=== 安全序列化演示 ===');
const obj1 = { name: 'A' };
const obj2 = { name: 'B' };
obj1.ref = obj2;
obj2.ref = obj1;
const serialized = this.stringify(obj1);
console.log('序列化结果:', serialized);
console.log();
}
}
SafeSerializer.demo();
How to implement class mixins? What should be noted?
How to implement class mixins? What should be noted?
考察点:对象合成与继承。
答案:
Mixin 是一种允许类从多个源继承行为的设计模式。ES6 虽然原生支持单继承,但我们可以通过多种技术实现 Mixin 模式来达到多继承的效果。
1. 基础 Mixin 实现:
// 基础 Mixin 模式实现
class BasicMixinExample {
// 简单的 mixin 函数
static mixin(BaseClass, ...mixins) {
// 创建一个新类继承自 BaseClass
class Mixed extends BaseClass {}
// 将所有 mixin 的方法复制到新类的原型上
mixins.forEach(mixin => {
Object.getOwnPropertyNames(mixin.prototype).forEach(name => {
if (name !== 'constructor') {
Mixed.prototype[name] = mixin.prototype[name];
}
});
});
return Mixed;
}
// 演示基础用法
static demo() {
console.log('=== 基础 Mixin 演示 ===');
// 定义基类
class Animal {
constructor(name) {
this.name = name;
}
speak() {
console.log(`${this.name} makes a sound`);
}
}
// 定义 mixin 类
class Flyable {
fly() {
console.log(`${this.name} is flying`);
}
land() {
console.log(`${this.name} has landed`);
}
}
class Swimmable {
swim() {
console.log(`${this.name} is swimming`);
}
dive() {
console.log(`${this.name} is diving`);
}
}
// 创建混合类
const FlyingSwimmingAnimal = this.mixin(Animal, Flyable, Swimmable);
// 测试
const duck = new FlyingSwimmingAnimal('Duck');
duck.speak(); // 继承自 Animal
duck.fly(); // 来自 Flyable mixin
duck.swim(); // 来自 Swimmable mixin
duck.land();
duck.dive();
console.log('duck instanceof Animal:', duck instanceof Animal);
console.log('duck instanceof FlyingSwimmingAnimal:',
duck instanceof FlyingSwimmingAnimal);
console.log();
}
}
BasicMixinExample.demo();
2. 工厂函数 Mixin 模式:
// 工厂函数 Mixin 模式
class FactoryMixinPattern {
// 可配置的 mixin 工厂
static createMixin(mixinDefinition) {
return (SuperClass) => {
class MixedClass extends SuperClass {
constructor(...args) {
super(...args);
// 调用 mixin 的初始化方法
if (mixinDefinition.initialize) {
mixinDefinition.initialize.call(this);
}
}
}
// 复制 mixin 的方法
Object.keys(mixinDefinition).forEach(key => {
if (key !== 'initialize' && typeof mixinDefinition[key] === 'function') {
MixedClass.prototype[key] = mixinDefinition[key];
}
});
// 复制静态方法
Object.getOwnPropertyNames(mixinDefinition).forEach(key => {
if (typeof mixinDefinition[key] === 'function' && key !== 'initialize') {
MixedClass[key] = mixinDefinition[key];
}
});
return MixedClass;
};
}
// 链式 mixin 组合
static compose(...mixins) {
return (BaseClass) => {
return mixins.reduce((currentClass, mixin) => {
return mixin(currentClass);
}, BaseClass);
};
}
// 演示工厂模式
static demo() {
console.log('=== 工厂函数 Mixin 演示 ===');
// 定义基类
class Vehicle {
constructor(brand) {
this.brand = brand;
this.speed = 0;
}
start() {
console.log(`${this.brand} vehicle started`);
}
}
// 定义 mixin
const Motorized = this.createMixin({
initialize() {
this.engine = 'V8';
this.fuel = 100;
},
accelerate() {
if (this.fuel > 0) {
this.speed += 10;
this.fuel -= 1;
console.log(`Accelerating to ${this.speed} km/h, fuel: ${this.fuel}%`);
} else {
console.log('No fuel left!');
}
},
refuel() {
this.fuel = 100;
console.log('Refueled to 100%');
}
});
const Trackable = this.createMixin({
initialize() {
this.position = { x: 0, y: 0 };
this.history = [];
},
moveTo(x, y) {
this.history.push({ ...this.position });
this.position = { x, y };
console.log(`Moved to position (${x}, ${y})`);
},
getHistory() {
return this.history;
}
});
const Communicable = this.createMixin({
initialize() {
this.messages = [];
},
sendMessage(message) {
this.messages.push({
message,
timestamp: new Date(),
type: 'sent'
});
console.log(`Message sent: ${message}`);
},
receiveMessage(message) {
this.messages.push({
message,
timestamp: new Date(),
type: 'received'
});
console.log(`Message received: ${message}`);
}
});
// 使用组合创建最终类
const SmartCar = this.compose(
Motorized,
Trackable,
Communicable
)(Vehicle);
// 测试
const tesla = new SmartCar('Tesla');
tesla.start();
tesla.accelerate();
tesla.moveTo(100, 200);
tesla.sendMessage('Arriving at destination');
tesla.refuel();
console.log('Position:', tesla.position);
console.log('Engine:', tesla.engine);
console.log('Messages:', tesla.messages.length);
console.log();
}
}
FactoryMixinPattern.demo();
3. Symbol-based Mixin(避免命名冲突):
// 使用 Symbol 避免命名冲突的 Mixin
class SymbolBasedMixin {
// 创建带命名空间的 mixin
static createNamespacedMixin(namespace, methods) {
const symbols = {};
const mixin = {};
// 为每个方法创建 Symbol
Object.keys(methods).forEach(methodName => {
const symbol = Symbol(`${namespace}.${methodName}`);
symbols[methodName] = symbol;
mixin[symbol] = methods[methodName];
});
// 返回 mixin 工厂
return {
symbols,
apply: (targetClass) => {
Object.keys(mixin).forEach(symbol => {
targetClass.prototype[symbol] = mixin[symbol];
});
return targetClass;
}
};
}
// 高级 Symbol mixin 实现
static createAdvancedMixin(mixinConfig) {
const mixinSymbols = new Map();
return (BaseClass) => {
class MixedClass extends BaseClass {
constructor(...args) {
super(...args);
// 初始化所有 mixin 的状态
Object.keys(mixinConfig).forEach(mixinName => {
const config = mixinConfig[mixinName];
if (config.initialize) {
config.initialize.call(this);
}
// 为每个 mixin 创建命名空间
const namespaceSymbol = Symbol(mixinName);
mixinSymbols.set(mixinName, namespaceSymbol);
this[namespaceSymbol] = {};
});
}
// 调用 mixin 方法的辅助函数
callMixin(mixinName, methodName, ...args) {
const config = mixinConfig[mixinName];
if (config && config[methodName]) {
return config[methodName].apply(this, args);
}
throw new Error(`Method ${methodName} not found in mixin ${mixinName}`);
}
// 获取 mixin 状态
getMixinState(mixinName) {
const symbol = mixinSymbols.get(mixinName);
return symbol ? this[symbol] : undefined;
}
}
// 注册所有 mixin 方法
Object.keys(mixinConfig).forEach(mixinName => {
const config = mixinConfig[mixinName];
Object.keys(config).forEach(methodName => {
if (methodName !== 'initialize' && typeof config[methodName] === 'function') {
const methodSymbol = Symbol(`${mixinName}.${methodName}`);
MixedClass.prototype[methodSymbol] = config[methodName];
// 创建便捷访问方法
const accessorName = `${mixinName}_${methodName}`;
MixedClass.prototype[accessorName] = function(...args) {
return this[methodSymbol](...args);
};
}
});
});
return MixedClass;
};
}
// 演示 Symbol-based Mixin
static demo() {
console.log('=== Symbol-based Mixin 演示 ===');
// 定义基类
class DataProcessor {
constructor(data) {
this.data = data;
}
}
// 定义 mixin 配置
const mixinConfig = {
validator: {
initialize() {
this.validationRules = [];
},
addRule(rule) {
this.validationRules.push(rule);
},
validate() {
return this.validationRules.every(rule => rule(this.data));
},
getErrors() {
return this.validationRules
.filter(rule => !rule(this.data))
.map(rule => rule.name || 'Validation error');
}
},
formatter: {
initialize() {
this.formatters = new Map();
},
addFormatter(name, formatter) {
this.formatters.set(name, formatter);
},
format(formatterName) {
const formatter = this.formatters.get(formatterName);
if (formatter) {
return formatter(this.data);
}
throw new Error(`Formatter ${formatterName} not found`);
}
},
serializer: {
initialize() {
this.serializers = new Map([
['json', JSON.stringify],
['string', String],
['csv', (data) => Array.isArray(data) ? data.join(',') : String(data)]
]);
},
serialize(format = 'json') {
const serializer = this.serializers.get(format);
if (serializer) {
return serializer(this.data);
}
throw new Error(`Serializer ${format} not found`);
},
addSerializer(name, serializer) {
this.serializers.set(name, serializer);
}
}
};
// 创建混合类
const EnhancedProcessor = this.createAdvancedMixin(mixinConfig)(DataProcessor);
// 测试
const processor = new EnhancedProcessor([1, 2, 3, 4, 5]);
// 使用 validator mixin
processor.validator_addRule(data => data.length > 0);
processor.validator_addRule(data => data.every(x => typeof x === 'number'));
console.log('数据验证通过:', processor.validator_validate());
// 使用 formatter mixin
processor.formatter_addFormatter('doubled', data => data.map(x => x * 2));
processor.formatter_addFormatter('sum', data => data.reduce((a, b) => a + b, 0));
console.log('格式化结果 (doubled):', processor.formatter_format('doubled'));
console.log('格式化结果 (sum):', processor.formatter_format('sum'));
// 使用 serializer mixin
console.log('JSON 序列化:', processor.serializer_serialize('json'));
console.log('CSV 序列化:', processor.serializer_serialize('csv'));
// 使用通用调用方法
console.log('通用调用:', processor.callMixin('serializer', 'serialize', 'string'));
console.log();
}
}
SymbolBasedMixin.demo();
4. 处理方法冲突的高级 Mixin:
// 处理方法冲突的高级 Mixin 实现
class ConflictResolutionMixin {
static createSmartMixin(...mixins) {
return (BaseClass) => {
class SmartMixed extends BaseClass {
constructor(...args) {
super(...args);
// 存储所有 mixin 的方法
this.__mixinMethods__ = new Map();
this.__mixinConflicts__ = new Map();
// 分析方法冲突
this.__analyzeMixins__(mixins);
}
__analyzeMixins__(mixins) {
const methodMap = new Map(); // methodName -> [mixin1, mixin2, ...]
mixins.forEach((mixin, index) => {
Object.getOwnPropertyNames(mixin.prototype).forEach(methodName => {
if (methodName !== 'constructor' &&
typeof mixin.prototype[methodName] === 'function') {
if (!methodMap.has(methodName)) {
methodMap.set(methodName, []);
}
methodMap.get(methodName).push({ mixin, index });
}
});
});
// 处理每个方法
methodMap.forEach((sources, methodName) => {
if (sources.length === 1) {
// 无冲突,直接使用
const { mixin } = sources[0];
this[methodName] = mixin.prototype[methodName];
this.__mixinMethods__.set(methodName, [mixin.name || 'Anonymous']);
} else {
// 有冲突,创建冲突解析方案
this.__handleConflict__(methodName, sources);
}
});
}
__handleConflict__(methodName, sources) {
// 记录冲突
const conflictInfo = sources.map(s => s.mixin.name || 'Anonymous');
this.__mixinConflicts__.set(methodName, conflictInfo);
// 创建组合方法(调用所有同名方法)
this[methodName] = (...args) => {
const results = sources.map(({ mixin }) => {
return mixin.prototype[methodName].apply(this, args);
});
console.log(`方法冲突 ${methodName}: 执行了 ${sources.length} 个实现`);
return results;
};
// 创建特定 mixin 的方法访问器
sources.forEach(({ mixin, index }) => {
const mixinName = mixin.name || `Mixin${index}`;
const specificMethodName = `${methodName}_from_${mixinName}`;
this[specificMethodName] = (...args) => {
return mixin.prototype[methodName].apply(this, args);
};
});
}
// 获取方法来源信息
getMethodSources(methodName) {
return this.__mixinMethods__.get(methodName) ||
this.__mixinConflicts__.get(methodName) ||
['BaseClass'];
}
// 获取所有冲突信息
getConflicts() {
return Array.from(this.__mixinConflicts__.entries());
}
// 列出所有可用方法
listMethods() {
const methods = [];
// 基类方法
Object.getOwnPropertyNames(BaseClass.prototype).forEach(name => {
if (name !== 'constructor' && typeof this[name] === 'function') {
methods.push({ name, source: 'BaseClass', conflict: false });
}
});
// Mixin 方法
this.__mixinMethods__.forEach((sources, name) => {
methods.push({ name, source: sources, conflict: false });
});
// 冲突方法
this.__mixinConflicts__.forEach((sources, name) => {
methods.push({ name, source: sources, conflict: true });
});
return methods;
}
}
return SmartMixed;
};
}
// 演示冲突解析
static demo() {
console.log('=== 冲突解析 Mixin 演示 ===');
// 基类
class Shape {
constructor(name) {
this.name = name;
}
describe() {
return `This is a ${this.name}`;
}
}
// Mixin 1
class Colorable {
static get name() { return 'Colorable'; }
setColor(color) {
this.color = color;
console.log(`${this.name} color set to ${color}`);
}
describe() { // 冲突方法
return `A ${this.color || 'colorless'} ${this.name}`;
}
}
// Mixin 2
class Sizeable {
static get name() { return 'Sizeable'; }
setSize(width, height) {
this.width = width;
this.height = height;
console.log(`${this.name} size set to ${width}x${height}`);
}
describe() { // 冲突方法
return `A ${this.width || 'unknown'}x${this.height || 'unknown'} ${this.name}`;
}
getArea() {
return (this.width || 0) * (this.height || 0);
}
}
// Mixin 3
class Drawable {
static get name() { return 'Drawable'; }
draw() {
console.log(`Drawing ${this.describe()}`);
}
setColor(color) { // 与 Colorable.setColor 冲突
this.strokeColor = color;
console.log(`${this.name} stroke color set to ${color}`);
}
}
// 创建智能混合类
const SmartShape = this.createSmartMixin(Colorable, Sizeable, Drawable)(Shape);
// 测试
const rectangle = new SmartShape('Rectangle');
console.log('=== 方法列表 ===');
rectangle.listMethods().forEach(method => {
console.log(`${method.name}: ${method.source} ${method.conflict ? '(冲突)' : ''}`);
});
console.log('\n=== 冲突信息 ===');
rectangle.getConflicts().forEach(([method, sources]) => {
console.log(`${method}: ${sources.join(', ')}`);
});
console.log('\n=== 方法调用测试 ===');
// 调用无冲突方法
rectangle.setSize(10, 20);
console.log('Area:', rectangle.getArea());
// 调用冲突方法(会执行所有实现)
console.log('\n调用冲突方法 setColor:');
rectangle.setColor('red');
console.log('\n调用冲突方法 describe:');
const descriptions = rectangle.describe();
console.log('所有描述:', descriptions);
// 调用特定 mixin 的方法
console.log('\n调用特定实现:');
console.log('Colorable describe:', rectangle.describe_from_Colorable());
console.log('Sizeable describe:', rectangle.describe_from_Sizeable());
rectangle.draw();
console.log();
}
}
ConflictResolutionMixin.demo();
5. Mixin 的最佳实践和注意事项:
// Mixin 最佳实践指南
class MixinBestPractices {
// 检查 Mixin 兼容性
static checkMixinCompatibility(BaseClass, ...mixins) {
const issues = [];
const allMethods = new Set();
// 检查基类方法
Object.getOwnPropertyNames(BaseClass.prototype).forEach(name => {
if (name !== 'constructor') {
allMethods.add(name);
}
});
// 检查 Mixin 之间的冲突
mixins.forEach((mixin, index) => {
const mixinName = mixin.name || `Mixin${index}`;
Object.getOwnPropertyNames(mixin.prototype).forEach(name => {
if (name !== 'constructor') {
if (allMethods.has(name)) {
issues.push({
type: 'method_conflict',
method: name,
mixin: mixinName,
message: `Method ${name} conflicts with existing implementation`
});
} else {
allMethods.add(name);
}
}
});
// 检查必要的依赖方法
if (mixin.requiredMethods) {
mixin.requiredMethods.forEach(requiredMethod => {
if (!allMethods.has(requiredMethod)) {
issues.push({
type: 'missing_dependency',
method: requiredMethod,
mixin: mixinName,
message: `Mixin ${mixinName} requires method ${requiredMethod}`
});
}
});
}
});
return {
compatible: issues.length === 0,
issues
};
}
// 创建文档化的 Mixin
static createDocumentedMixin(config) {
const mixin = (BaseClass) => {
class DocumentedMixin extends BaseClass {
constructor(...args) {
super(...args);
if (config.initialize) {
config.initialize.call(this);
}
// 存储 mixin 元信息
if (!this.__mixinInfo__) {
this.__mixinInfo__ = [];
}
this.__mixinInfo__.push({
name: config.name,
version: config.version,
description: config.description,
methods: Object.keys(config.methods || {}),
dependencies: config.dependencies || []
});
}
// 获取 mixin 信息
getMixinInfo() {
return this.__mixinInfo__ || [];
}
}
// 添加方法
if (config.methods) {
Object.keys(config.methods).forEach(methodName => {
DocumentedMixin.prototype[methodName] = config.methods[methodName];
});
}
// 添加静态方法
if (config.staticMethods) {
Object.keys(config.staticMethods).forEach(methodName => {
DocumentedMixin[methodName] = config.staticMethods[methodName];
});
}
// 存储依赖信息
if (config.dependencies) {
DocumentedMixin.requiredMethods = config.dependencies;
}
return DocumentedMixin;
};
// 添加 mixin 元信息
mixin.mixinName = config.name;
mixin.mixinVersion = config.version;
mixin.mixinDescription = config.description;
return mixin;
}
// 演示最佳实践
static demo() {
console.log('=== Mixin 最佳实践演示 ===');
// 定义基类
class EventEmitter {
constructor() {
this.listeners = new Map();
}
on(event, callback) {
if (!this.listeners.has(event)) {
this.listeners.set(event, []);
}
this.listeners.get(event).push(callback);
}
emit(event, ...args) {
if (this.listeners.has(event)) {
this.listeners.get(event).forEach(callback => {
callback(...args);
});
}
}
}
// 创建文档化的 Mixin
const LoggableMixin = this.createDocumentedMixin({
name: 'Loggable',
version: '1.0.0',
description: '为类添加日志记录功能',
dependencies: ['emit'], // 依赖 emit 方法
initialize() {
this.logs = [];
this.logLevel = 'info';
},
methods: {
log(level, message) {
const logEntry = {
level,
message,
timestamp: new Date()
};
this.logs.push(logEntry);
this.emit('log', logEntry); // 使用依赖的方法
console.log(`[${level.toUpperCase()}] ${message}`);
},
info(message) {
this.log('info', message);
},
warn(message) {
this.log('warn', message);
},
error(message) {
this.log('error', message);
},
getLogs(level = null) {
return level ?
this.logs.filter(log => log.level === level) :
this.logs;
}
}
});
const CacheableMixin = this.createDocumentedMixin({
name: 'Cacheable',
version: '1.1.0',
description: '为类添加缓存功能',
initialize() {
this.cache = new Map();
this.cacheStats = { hits: 0, misses: 0 };
},
methods: {
getCached(key) {
if (this.cache.has(key)) {
this.cacheStats.hits++;
return { found: true, value: this.cache.get(key) };
} else {
this.cacheStats.misses++;
return { found: false, value: null };
}
},
setCached(key, value, ttl = null) {
const entry = { value, timestamp: Date.now() };
if (ttl) {
entry.expires = Date.now() + ttl;
}
this.cache.set(key, entry);
},
clearCache() {
this.cache.clear();
this.cacheStats = { hits: 0, misses: 0 };
},
getCacheStats() {
return { ...this.cacheStats };
}
}
});
// 检查兼容性
const compatibility = this.checkMixinCompatibility(
EventEmitter,
LoggableMixin,
CacheableMixin
);
console.log('兼容性检查:', compatibility.compatible);
if (!compatibility.compatible) {
compatibility.issues.forEach(issue => {
console.log(`- ${issue.type}: ${issue.message}`);
});
}
// 创建混合类
const EnhancedEmitter = CacheableMixin(LoggableMixin(EventEmitter));
// 测试
const emitter = new EnhancedEmitter();
// 设置日志监听器
emitter.on('log', (logEntry) => {
console.log(`日志事件: ${logEntry.level} - ${logEntry.message}`);
});
// 测试功能
emitter.info('系统启动');
emitter.setCached('user:123', { name: 'Alice', age: 30 });
const cached = emitter.getCached('user:123');
if (cached.found) {
emitter.info(`从缓存获取用户: ${cached.value.value.name}`);
}
emitter.warn('内存使用率较高');
emitter.error('数据库连接失败');
// 显示统计信息
console.log('\n=== Mixin 信息 ===');
emitter.getMixinInfo().forEach(info => {
console.log(`${info.name} v${info.version}: ${info.description}`);
console.log(` 方法: ${info.methods.join(', ')}`);
if (info.dependencies.length > 0) {
console.log(` 依赖: ${info.dependencies.join(', ')}`);
}
});
console.log('\n=== 缓存统计 ===');
console.log(emitter.getCacheStats());
console.log('\n=== 日志记录 ===');
emitter.getLogs().forEach(log => {
console.log(`${log.timestamp.toISOString()} [${log.level}] ${log.message}`);
});
console.log();
}
}
MixinBestPractices.demo();
// Mixin 使用指南总结
console.log('=== Mixin 使用指南总结 ===');
console.log('1. 优先使用组合而非继承');
console.log('2. 避免方法名冲突,使用命名空间');
console.log('3. 明确声明 mixin 的依赖关系');
console.log('4. 提供清晰的文档和版本控制');
console.log('5. 实现冲突解析机制');
console.log('6. 使用 Symbol 来避免意外的属性覆盖');
console.log('7. 考虑性能影响,避免过度复杂的 mixin 链');
console.log('8. 提供运行时的 mixin 信息查询能力');
Mixin 模式核心要点:
实现方式:
最佳实践:
注意事项:
应用场景:
How does ES6 implement private methods and properties? What are the limitations?
How does ES6 implement private methods and properties? What are the limitations?
考察点:#语法、闭包、Symbol、WeakMap等多种实现方式。
答案:
ES6 本身并没有提供真正的私有成员语法,但可以通过多种技术手段来模拟私有属性和方法。ES2022 引入了真正的私有字段语法(#),让我们全面了解各种实现方式及其限制。
1. Symbol 方式实现私有成员:
// 使用 Symbol 模拟私有属性和方法
class SymbolPrivateExample {
constructor(name, balance) {
this[_name] = name;
this[_balance] = balance;
this[_transactions] = [];
}
// 公有方法
deposit(amount) {
if (this[_validateAmount](amount)) {
this[_balance] += amount;
this[_addTransaction]('deposit', amount);
return this[_balance];
}
throw new Error('Invalid amount');
}
withdraw(amount) {
if (this[_validateAmount](amount) && this[_balance] >= amount) {
this[_balance] -= amount;
this[_addTransaction]('withdraw', amount);
return this[_balance];
}
throw new Error('Invalid operation');
}
getBalance() {
return this[_balance];
}
getName() {
return this[_name];
}
// 私有方法
[_validateAmount](amount) {
return typeof amount === 'number' && amount > 0;
}
[_addTransaction](type, amount) {
this[_transactions].push({
type, amount, timestamp: new Date(), balance: this[_balance]
});
}
getTransactions() {
return [...this[_transactions]]; // 返回副本
}
}
// 私有 Symbol 定义
const _name = Symbol('name');
const _balance = Symbol('balance');
const _transactions = Symbol('transactions');
const _validateAmount = Symbol('validateAmount');
const _addTransaction = Symbol('addTransaction');
console.log('=== Symbol 私有成员演示 ===');
const account = new SymbolPrivateExample('Alice', 1000);
console.log('姓名:', account.getName());
console.log('余额:', account.getBalance());
account.deposit(500);
console.log('存款后:', account.getBalance());
console.log('交易记录:', account.getTransactions().length);
// Symbol 的局限性
console.log('对象键:', Object.keys(account)); // 不显示 Symbol 属性
console.log('Symbol 属性数量:', Object.getOwnPropertySymbols(account).length); // 但可以发现
console.log();
2. WeakMap 方式实现真正私有:
// 使用 WeakMap 实现真正的私有成员
const privateData = new WeakMap();
const privateMethods = new WeakMap();
class WeakMapPrivateExample {
constructor(username, email, role = 'user') {
// 私有数据
privateData.set(this, {
username,
email,
role,
loginAttempts: 0,
isLocked: false,
sessions: [],
permissions: this._getDefaultPermissions(role)
});
// 私有方法
privateMethods.set(this, {
validateEmail: (email) => {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
},
hashPassword: (password) => {
// 简化的哈希实现
return btoa(password + 'salt');
},
generateSessionId: () => {
return Math.random().toString(36).substr(2, 16);
},
checkPermission: (action) => {
const data = privateData.get(this);
return data.permissions.includes(action);
}
});
// 公有属性
this.id = Math.random().toString(36).substr(2, 9);
this.createdAt = new Date();
this.lastLogin = null;
}
// 辅助方法获取私有数据和方法
_getPrivateData() {
return privateData.get(this);
}
_getPrivateMethods() {
return privateMethods.get(this);
}
_getDefaultPermissions(role) {
const permissions = {
'user': ['read', 'comment'],
'editor': ['read', 'comment', 'write', 'edit'],
'admin': ['read', 'comment', 'write', 'edit', 'delete', 'manage']
};
return permissions[role] || permissions['user'];
}
// 公有方法
login(password) {
const data = this._getPrivateData();
const methods = this._getPrivateMethods();
if (data.isLocked) {
throw new Error('Account is locked');
}
// 简化的登录验证
const hashedPassword = methods.hashPassword(password);
if (hashedPassword) { // 简化验证
const sessionId = methods.generateSessionId();
data.sessions.push({
id: sessionId,
createdAt: new Date(),
isActive: true
});
this.lastLogin = new Date();
data.loginAttempts = 0;
return sessionId;
} else {
data.loginAttempts++;
if (data.loginAttempts >= 3) {
data.isLocked = true;
}
throw new Error('Invalid credentials');
}
}
logout(sessionId) {
const data = this._getPrivateData();
const session = data.sessions.find(s => s.id === sessionId);
if (session) {
session.isActive = false;
session.endedAt = new Date();
return true;
}
return false;
}
changeEmail(newEmail) {
const data = this._getPrivateData();
const methods = this._getPrivateMethods();
if (!methods.validateEmail(newEmail)) {
throw new Error('Invalid email format');
}
const oldEmail = data.email;
data.email = newEmail;
return { oldEmail, newEmail };
}
canPerform(action) {
const methods = this._getPrivateMethods();
return methods.checkPermission(action);
}
// 只读访问器
get username() {
return this._getPrivateData().username;
}
get email() {
const email = this._getPrivateData().email;
// 隐藏部分邮箱
const [user, domain] = email.split('@');
return user.slice(0, 2) + '*'.repeat(user.length - 2) + '@' + domain;
}
get role() {
return this._getPrivateData().role;
}
get isLocked() {
return this._getPrivateData().isLocked;
}
getActiveSessions() {
const data = this._getPrivateData();
return data.sessions.filter(s => s.isActive).length;
}
getPermissions() {
return [...this._getPrivateData().permissions]; // 返回副本
}
}
console.log('=== WeakMap 私有成员演示 ===');
const user = new WeakMapPrivateExample('john_doe', '[email protected]', 'editor');
console.log('用户信息:', user.username, user.email, user.role);
console.log('权限:', user.getPermissions());
console.log('可以写入:', user.canPerform('write'));
console.log('可以删除:', user.canPerform('delete'));
const sessionId = user.login('password123');
console.log('登录成功,会话ID:', sessionId);
console.log('活跃会话数:', user.getActiveSessions());
user.changeEmail('[email protected]');
console.log('邮箱已更改:', user.email);
console.log('尝试直接访问私有数据:', user.username); // 只能通过 getter
console.log();
3. 闭包方式实现私有成员:
// 使用闭包实现私有成员
function createSecureDataStore(initialData = {}) {
// 私有变量
let data = new Map(Object.entries(initialData));
let accessLog = [];
let isReadOnly = false;
let encryptionKey = Math.random().toString(36);
// 私有方法
function logAccess(operation, key, success) {
accessLog.push({
operation,
key,
success,
timestamp: new Date()
});
}
function encrypt(value) {
// 简化的加密
return btoa(JSON.stringify(value) + encryptionKey);
}
function decrypt(encryptedValue) {
try {
const decrypted = atob(encryptedValue);
return JSON.parse(decrypted.replace(encryptionKey, ''));
} catch {
return null;
}
}
function validateKey(key) {
return typeof key === 'string' && key.length > 0;
}
// 返回公有接口
return {
// 数据操作
set(key, value, encrypted = false) {
if (isReadOnly) {
logAccess('set', key, false);
throw new Error('Data store is read-only');
}
if (!validateKey(key)) {
logAccess('set', key, false);
throw new Error('Invalid key');
}
const finalValue = encrypted ? encrypt(value) : value;
data.set(key, finalValue);
logAccess('set', key, true);
return this;
},
get(key, encrypted = false) {
if (!validateKey(key)) {
logAccess('get', key, false);
return undefined;
}
const value = data.get(key);
logAccess('get', key, value !== undefined);
if (encrypted && value) {
return decrypt(value);
}
return value;
},
has(key) {
const exists = data.has(key);
logAccess('has', key, exists);
return exists;
},
delete(key) {
if (isReadOnly) {
logAccess('delete', key, false);
throw new Error('Data store is read-only');
}
const existed = data.has(key);
if (existed) {
data.delete(key);
}
logAccess('delete', key, existed);
return existed;
},
clear() {
if (isReadOnly) {
logAccess('clear', 'all', false);
throw new Error('Data store is read-only');
}
const size = data.size;
data.clear();
logAccess('clear', 'all', true);
return size;
},
// 状态管理
setReadOnly(readonly = true) {
isReadOnly = readonly;
logAccess('setReadOnly', 'state', true);
return this;
},
isReadOnly() {
return isReadOnly;
},
size() {
return data.size;
},
keys() {
return Array.from(data.keys());
},
// 批量操作
setBatch(entries, encrypted = false) {
if (isReadOnly) {
throw new Error('Data store is read-only');
}
const results = [];
for (const [key, value] of entries) {
try {
this.set(key, value, encrypted);
results.push({ key, success: true });
} catch (error) {
results.push({ key, success: false, error: error.message });
}
}
return results;
},
// 安全操作
backup() {
const backup = {};
for (const [key, value] of data) {
backup[key] = value;
}
logAccess('backup', 'all', true);
return backup;
},
restore(backupData) {
if (isReadOnly) {
throw new Error('Data store is read-only');
}
data.clear();
for (const [key, value] of Object.entries(backupData)) {
data.set(key, value);
}
logAccess('restore', 'all', true);
return this;
},
// 日志和统计
getAccessLog() {
return [...accessLog]; // 返回副本
},
clearLog() {
accessLog = [];
return this;
},
getStats() {
const stats = {
totalOperations: accessLog.length,
successfulOperations: accessLog.filter(log => log.success).length,
dataSize: data.size,
isReadOnly: isReadOnly,
operationTypes: {}
};
accessLog.forEach(log => {
stats.operationTypes[log.operation] =
(stats.operationTypes[log.operation] || 0) + 1;
});
return stats;
}
};
}
console.log('=== 闭包私有成员演示 ===');
const dataStore = createSecureDataStore({ initial: 'value' });
console.log('初始数据大小:', dataStore.size());
dataStore.set('name', 'Alice');
dataStore.set('secret', 'confidential data', true); // 加密存储
console.log('普通数据:', dataStore.get('name'));
console.log('加密数据:', dataStore.get('secret', true));
console.log('所有键:', dataStore.keys());
// 设置只读模式
dataStore.setReadOnly(true);
try {
dataStore.set('new', 'value'); // 会失败
} catch (error) {
console.log('只读模式错误:', error.message);
}
console.log('操作统计:', dataStore.getStats());
console.log();
4. ES2022 原生私有字段(如果支持):
// ES2022 私有字段和方法
class ModernPrivateExample {
// 私有字段声明
#userId;
#password;
#permissions = new Set();
#loginHistory = [];
#maxLoginAttempts = 3;
#currentAttempts = 0;
// 私有静态字段
static #instanceCounter = 0;
static #activeUsers = new Map();
constructor(username, password, role = 'user') {
this.#userId = username;
this.#password = this.#hashPassword(password);
this.#setupPermissions(role);
// 公有属性
this.id = `user_${++ModernPrivateExample.#instanceCounter}`;
this.createdAt = new Date();
this.isActive = true;
ModernPrivateExample.#activeUsers.set(this.id, this);
}
// 私有方法
#hashPassword(password) {
return btoa(password + 'salt' + this.#userId);
}
#setupPermissions(role) {
const rolePermissions = {
'user': ['read'],
'editor': ['read', 'write'],
'admin': ['read', 'write', 'delete']
};
const permissions = rolePermissions[role] || rolePermissions['user'];
permissions.forEach(p => this.#permissions.add(p));
}
#validatePassword(password) {
return this.#hashPassword(password) === this.#password;
}
#logLogin(success, ip = 'unknown') {
this.#loginHistory.push({
success,
ip,
timestamp: new Date(),
attempts: this.#currentAttempts
});
}
#resetLoginAttempts() {
this.#currentAttempts = 0;
}
#incrementLoginAttempts() {
this.#currentAttempts++;
return this.#currentAttempts >= this.#maxLoginAttempts;
}
// 公有方法
authenticate(password, ip) {
if (this.#currentAttempts >= this.#maxLoginAttempts) {
throw new Error('Account temporarily locked');
}
if (this.#validatePassword(password)) {
this.#resetLoginAttempts();
this.#logLogin(true, ip);
this.lastLogin = new Date();
return true;
} else {
const isLocked = this.#incrementLoginAttempts();
this.#logLogin(false, ip);
if (isLocked) {
throw new Error('Too many failed attempts. Account locked.');
} else {
throw new Error('Invalid password');
}
}
}
hasPermission(permission) {
return this.#permissions.has(permission);
}
addPermission(permission) {
this.#permissions.add(permission);
return this;
}
removePermission(permission) {
this.#permissions.delete(permission);
return this;
}
getPermissions() {
return Array.from(this.#permissions);
}
changePassword(oldPassword, newPassword) {
if (!this.#validatePassword(oldPassword)) {
throw new Error('Current password is incorrect');
}
this.#password = this.#hashPassword(newPassword);
return true;
}
getLoginHistory() {
return this.#loginHistory.map(entry => ({
...entry,
// 不暴露敏感信息
password: undefined
}));
}
unlock() {
this.#resetLoginAttempts();
return this;
}
// 静态方法
static getActiveUserCount() {
return ModernPrivateExample.#activeUsers.size;
}
static findUser(id) {
return ModernPrivateExample.#activeUsers.get(id);
}
// 私有字段检查
static hasPrivateFields(obj) {
try {
return #userId in obj && #password in obj;
} catch {
return false;
}
}
}
// 演示现代私有字段(注意:需要支持 ES2022 的环境)
console.log('=== ES2022 私有字段演示 ===');
try {
const user = new ModernPrivateExample('alice', 'secret123', 'editor');
console.log('用户ID:', user.id);
console.log('权限:', user.getPermissions());
// 认证测试
user.authenticate('secret123', '192.168.1.1');
console.log('认证成功');
// 权限测试
console.log('可以读取:', user.hasPermission('read'));
console.log('可以写入:', user.hasPermission('write'));
console.log('可以删除:', user.hasPermission('delete'));
// 修改密码
user.changePassword('secret123', 'newPassword456');
console.log('密码已更改');
// 尝试访问私有字段(语法错误)
try {
console.log(user.#userId); // 这会导致语法错误
} catch {
console.log('私有字段无法直接访问');
}
} catch (error) {
console.log('ES2022 私有字段可能不被支持:', error.message);
}
console.log();
5. 不同实现方式的对比和最佳实践:
// 私有成员实现方式对比
class PrivateImplementationComparison {
static analyze() {
console.log('=== 私有成员实现方式对比 ===\n');
const comparisons = [
{
method: 'Symbol',
trulyPrivate: false,
performance: 'high',
memoryEfficient: true,
debuggable: 'partial',
compatibility: 'ES6+',
syntax: 'complex',
pros: ['兼容性好', '性能高', '支持元编程'],
cons: ['不是真正私有', '语法复杂', 'Symbol 可被发现']
},
{
method: 'WeakMap',
trulyPrivate: true,
performance: 'medium',
memoryEfficient: true,
debuggable: 'difficult',
compatibility: 'ES6+',
syntax: 'complex',
pros: ['真正私有', '内存安全', '垃圾回收友好'],
cons: ['语法复杂', '性能较低', '调试困难']
},
{
method: 'Closure',
trulyPrivate: true,
performance: 'low',
memoryEfficient: false,
debuggable: 'good',
compatibility: 'ES5+',
syntax: 'natural',
pros: ['真正私有', '语法自然', '兼容性极好'],
cons: ['内存消耗高', '性能较低', '每实例都创建函数']
},
{
method: 'ES2022 Private Fields',
trulyPrivate: true,
performance: 'high',
memoryEfficient: true,
debuggable: 'good',
compatibility: 'ES2022+',
syntax: 'simple',
pros: ['原生支持', '真正私有', '性能最佳', '语法简洁'],
cons: ['兼容性限制', '需要转译器支持']
}
];
comparisons.forEach((comp, index) => {
console.log(`${index + 1}. ${comp.method}`);
console.log(` 真正私有: ${comp.trulyPrivate ? '✅' : '❌'}`);
console.log(` 性能: ${comp.performance}`);
console.log(` 内存效率: ${comp.memoryEfficient ? '✅' : '❌'}`);
console.log(` 可调试性: ${comp.debuggable}`);
console.log(` 兼容性: ${comp.compatibility}`);
console.log(` 语法复杂度: ${comp.syntax}`);
console.log(' 优点:', comp.pros.join(', '));
console.log(' 缺点:', comp.cons.join(', '));
console.log('');
});
console.log('=== 选择建议 ===');
console.log('🎯 现代项目 → ES2022 私有字段');
console.log('🔄 需要兼容性 → WeakMap 方式');
console.log('⚡ 性能优先 → Symbol 方式');
console.log('🛠️ 特殊需求 → 闭包方式');
console.log('\n=== 使用场景 ===');
console.log('• 敏感数据保护 → WeakMap 或 ES2022');
console.log('• 内部状态管理 → Symbol 或 ES2022');
console.log('• 库/框架开发 → 考虑兼容性,使用 WeakMap');
console.log('• 现代应用开发 → 优先使用 ES2022 私有字段');
}
}
PrivateImplementationComparison.analyze();
私有成员实现总结:
实现方式对比:
#:原生私有,最佳性能,现代语法选择原则:
限制和注意事项:
How to implement an async queue? What are the application scenarios?
How to implement an async queue? What are the application scenarios?
考察点:Promise、Generator、事件循环。
答案:
异步队列是一种重要的并发控制机制,用于管理异步任务的执行顺序、限制并发数量、处理任务依赖等。让我们实现几种不同类型的异步队列并探讨其应用场景。
1. 基础异步队列实现:
// 基础异步队列类
class AsyncQueue {
constructor(options = {}) {
this.queue = [];
this.running = false;
this.maxConcurrency = options.maxConcurrency || 1;
this.currentConcurrency = 0;
this.results = [];
this.errors = [];
this.onProgress = options.onProgress || null;
this.onComplete = options.onComplete || null;
this.onError = options.onError || null;
this.retryCount = options.retryCount || 0;
this.retryDelay = options.retryDelay || 1000;
}
// 添加任务到队列
add(task, priority = 0) {
return new Promise((resolve, reject) => {
const taskWrapper = {
id: Math.random().toString(36).substr(2, 9),
task,
priority,
resolve,
reject,
attempts: 0,
createdAt: new Date()
};
// 按优先级插入
const insertIndex = this.queue.findIndex(item => item.priority < priority);
if (insertIndex === -1) {
this.queue.push(taskWrapper);
} else {
this.queue.splice(insertIndex, 0, taskWrapper);
}
// 自动开始处理
this.process();
});
}
// 批量添加任务
addBatch(tasks, priority = 0) {
const promises = tasks.map(task => this.add(task, priority));
return Promise.all(promises);
}
// 处理队列
async process() {
if (this.running && this.currentConcurrency >= this.maxConcurrency) {
return;
}
this.running = true;
while (this.queue.length > 0 && this.currentConcurrency < this.maxConcurrency) {
const taskWrapper = this.queue.shift();
this.currentConcurrency++;
// 异步执行任务
this.executeTask(taskWrapper);
}
// 检查是否所有任务完成
if (this.queue.length === 0 && this.currentConcurrency === 0) {
this.running = false;
if (this.onComplete) {
this.onComplete({
results: this.results,
errors: this.errors,
totalTasks: this.results.length + this.errors.length
});
}
}
}
// 执行单个任务
async executeTask(taskWrapper) {
try {
taskWrapper.startedAt = new Date();
taskWrapper.attempts++;
const result = await taskWrapper.task();
taskWrapper.completedAt = new Date();
taskWrapper.duration = taskWrapper.completedAt - taskWrapper.startedAt;
this.results.push({
id: taskWrapper.id,
result,
duration: taskWrapper.duration,
attempts: taskWrapper.attempts
});
taskWrapper.resolve(result);
// 触发进度回调
if (this.onProgress) {
this.onProgress({
completed: this.results.length,
errors: this.errors.length,
remaining: this.queue.length,
current: this.currentConcurrency
});
}
} catch (error) {
// 重试逻辑
if (taskWrapper.attempts <= this.retryCount) {
console.log(`Task ${taskWrapper.id} failed, retrying... (${taskWrapper.attempts}/${this.retryCount + 1})`);
// 延迟重试
await new Promise(resolve => setTimeout(resolve, this.retryDelay));
// 重新加入队列
this.queue.unshift(taskWrapper);
} else {
// 重试次数用完,记录错误
this.errors.push({
id: taskWrapper.id,
error: error.message,
attempts: taskWrapper.attempts,
task: taskWrapper.task.toString()
});
taskWrapper.reject(error);
if (this.onError) {
this.onError(error, taskWrapper);
}
}
} finally {
this.currentConcurrency--;
// 继续处理队列
setTimeout(() => this.process(), 0);
}
}
// 清空队列
clear() {
this.queue.forEach(taskWrapper => {
taskWrapper.reject(new Error('Queue cleared'));
});
this.queue = [];
return this;
}
// 暂停队列
pause() {
this.running = false;
return this;
}
// 恢复队列
resume() {
this.running = true;
this.process();
return this;
}
// 获取队列状态
getStatus() {
return {
queueLength: this.queue.length,
running: this.running,
currentConcurrency: this.currentConcurrency,
maxConcurrency: this.maxConcurrency,
completed: this.results.length,
failed: this.errors.length
};
}
// 等待所有任务完成
async waitForCompletion() {
return new Promise(resolve => {
if (this.queue.length === 0 && this.currentConcurrency === 0) {
resolve({
results: this.results,
errors: this.errors
});
return;
}
const originalOnComplete = this.onComplete;
this.onComplete = (result) => {
if (originalOnComplete) {
originalOnComplete(result);
}
resolve(result);
};
});
}
}
// 使用示例
console.log('=== 基础异步队列演示 ===');
async function demoBasicQueue() {
const queue = new AsyncQueue({
maxConcurrency: 3,
retryCount: 2,
retryDelay: 500,
onProgress: (progress) => {
console.log(`进度: ${progress.completed}/${progress.completed + progress.remaining + progress.current} 完成`);
}
});
// 创建测试任务
const createTask = (id, duration, shouldFail = false) => {
return () => new Promise((resolve, reject) => {
setTimeout(() => {
if (shouldFail && Math.random() < 0.3) {
reject(new Error(`Task ${id} failed`));
} else {
resolve(`Task ${id} completed in ${duration}ms`);
}
}, duration);
});
};
// 添加任务
const tasks = [
queue.add(createTask(1, 1000), 1), // 高优先级
queue.add(createTask(2, 500)),
queue.add(createTask(3, 800)),
queue.add(createTask(4, 300), 2), // 最高优先级
queue.add(createTask(5, 600))
];
try {
const results = await Promise.all(tasks);
console.log('所有任务完成:', results.length);
console.log('队列状态:', queue.getStatus());
} catch (error) {
console.error('任务执行错误:', error.message);
}
}
await demoBasicQueue();
console.log();
2. 支持任务依赖的异步队列:
// 支持任务依赖的异步队列
class DependencyAsyncQueue {
constructor(options = {}) {
this.tasks = new Map();
this.dependencies = new Map();
this.completed = new Set();
this.running = new Set();
this.failed = new Set();
this.maxConcurrency = options.maxConcurrency || 5;
this.currentConcurrency = 0;
this.results = new Map();
this.errors = new Map();
this.onTaskComplete = options.onTaskComplete || null;
this.onTaskError = options.onTaskError || null;
this.onAllComplete = options.onAllComplete || null;
}
// 添加任务及其依赖
addTask(id, taskFn, dependencies = []) {
if (this.tasks.has(id)) {
throw new Error(`Task ${id} already exists`);
}
this.tasks.set(id, taskFn);
this.dependencies.set(id, new Set(dependencies));
// 验证依赖是否存在
for (const dep of dependencies) {
if (!this.tasks.has(dep) && !this.completed.has(dep)) {
console.warn(`Dependency ${dep} for task ${id} not found`);
}
}
return this;
}
// 批量添加任务
addTasks(taskMap) {
for (const [id, config] of Object.entries(taskMap)) {
this.addTask(id, config.task, config.dependencies || []);
}
return this;
}
// 检查任务是否可以执行
canExecute(taskId) {
if (this.running.has(taskId) || this.completed.has(taskId) || this.failed.has(taskId)) {
return false;
}
const dependencies = this.dependencies.get(taskId);
if (!dependencies) return false;
// 检查所有依赖是否已完成
for (const dep of dependencies) {
if (!this.completed.has(dep)) {
return false;
}
}
return true;
}
// 获取可执行的任务
getExecutableTasks() {
const executable = [];
for (const taskId of this.tasks.keys()) {
if (this.canExecute(taskId)) {
executable.push(taskId);
}
}
return executable;
}
// 执行任务
async executeTask(taskId) {
if (!this.canExecute(taskId)) {
throw new Error(`Task ${taskId} cannot be executed`);
}
this.running.add(taskId);
this.currentConcurrency++;
try {
const taskFn = this.tasks.get(taskId);
const dependencies = this.dependencies.get(taskId);
// 获取依赖的结果
const dependencyResults = {};
for (const dep of dependencies) {
dependencyResults[dep] = this.results.get(dep);
}
console.log(`开始执行任务: ${taskId}`);
const startTime = Date.now();
// 执行任务,传入依赖结果
const result = await taskFn(dependencyResults);
const duration = Date.now() - startTime;
console.log(`任务 ${taskId} 完成,耗时 ${duration}ms`);
// 记录结果
this.results.set(taskId, result);
this.completed.add(taskId);
this.running.delete(taskId);
if (this.onTaskComplete) {
this.onTaskComplete(taskId, result, duration);
}
return result;
} catch (error) {
console.error(`任务 ${taskId} 失败:`, error.message);
this.errors.set(taskId, error);
this.failed.add(taskId);
this.running.delete(taskId);
if (this.onTaskError) {
this.onTaskError(taskId, error);
}
throw error;
} finally {
this.currentConcurrency--;
// 尝试执行更多任务
setImmediate(() => this.processQueue());
}
}
// 处理队列
async processQueue() {
const executableTasks = this.getExecutableTasks();
const availableSlots = this.maxConcurrency - this.currentConcurrency;
const tasksToRun = executableTasks.slice(0, availableSlots);
// 并行执行可用任务
const promises = tasksToRun.map(taskId =>
this.executeTask(taskId).catch(error => {
// 错误处理已在 executeTask 中完成
return null;
})
);
if (promises.length > 0) {
await Promise.all(promises);
// 检查是否还有更多任务可以执行
const remainingExecutable = this.getExecutableTasks();
if (remainingExecutable.length > 0 && this.currentConcurrency < this.maxConcurrency) {
await this.processQueue();
}
}
// 检查是否所有任务都已完成或失败
const totalTasks = this.tasks.size;
const finishedTasks = this.completed.size + this.failed.size;
if (finishedTasks === totalTasks && this.currentConcurrency === 0) {
if (this.onAllComplete) {
this.onAllComplete({
completed: this.completed.size,
failed: this.failed.size,
results: Object.fromEntries(this.results),
errors: Object.fromEntries(this.errors)
});
}
}
}
// 开始执行所有任务
async execute() {
console.log('开始执行依赖队列...');
console.log(`总任务数: ${this.tasks.size}`);
const startTime = Date.now();
await this.processQueue();
// 等待所有任务完成
return new Promise((resolve) => {
const checkCompletion = () => {
const totalTasks = this.tasks.size;
const finishedTasks = this.completed.size + this.failed.size;
if (finishedTasks === totalTasks && this.currentConcurrency === 0) {
const duration = Date.now() - startTime;
resolve({
duration,
completed: this.completed.size,
failed: this.failed.size,
results: Object.fromEntries(this.results),
errors: Object.fromEntries(this.errors)
});
} else {
setTimeout(checkCompletion, 100);
}
};
checkCompletion();
});
}
// 获取任务状态
getStatus() {
return {
total: this.tasks.size,
completed: this.completed.size,
running: this.running.size,
failed: this.failed.size,
pending: this.tasks.size - this.completed.size - this.running.size - this.failed.size
};
}
// 可视化依赖图(简单版本)
printDependencyGraph() {
console.log('\n=== 任务依赖图 ===');
for (const [taskId, dependencies] of this.dependencies) {
const deps = Array.from(dependencies);
const status = this.completed.has(taskId) ? '✅' :
this.running.has(taskId) ? '🔄' :
this.failed.has(taskId) ? '❌' : '⏳';
console.log(`${status} ${taskId} ${deps.length > 0 ? `← [${deps.join(', ')}]` : '(no deps)'}`);
}
console.log('');
}
}
// 使用示例
console.log('=== 依赖异步队列演示 ===');
async function demoDependencyQueue() {
const queue = new DependencyAsyncQueue({
maxConcurrency: 3,
onTaskComplete: (id, result, duration) => {
console.log(`✅ 任务 ${id} 完成: ${result}`);
},
onTaskError: (id, error) => {
console.log(`❌ 任务 ${id} 失败: ${error.message}`);
}
});
// 模拟复杂的任务依赖关系
const tasks = {
'init': {
task: async () => {
await new Promise(resolve => setTimeout(resolve, 500));
return '初始化完成';
},
dependencies: []
},
'load-config': {
task: async (deps) => {
await new Promise(resolve => setTimeout(resolve, 300));
return { config: 'loaded', init: deps.init };
},
dependencies: ['init']
},
'load-data': {
task: async (deps) => {
await new Promise(resolve => setTimeout(resolve, 800));
return { data: 'user-data', config: deps['load-config'] };
},
dependencies: ['load-config']
},
'setup-auth': {
task: async (deps) => {
await new Promise(resolve => setTimeout(resolve, 400));
return { auth: 'setup', config: deps['load-config'] };
},
dependencies: ['load-config']
},
'process-data': {
task: async (deps) => {
await new Promise(resolve => setTimeout(resolve, 600));
return { processed: true, data: deps['load-data'], auth: deps['setup-auth'] };
},
dependencies: ['load-data', 'setup-auth']
},
'generate-report': {
task: async (deps) => {
await new Promise(resolve => setTimeout(resolve, 400));
return { report: 'generated', processedData: deps['process-data'] };
},
dependencies: ['process-data']
},
'send-notification': {
task: async (deps) => {
await new Promise(resolve => setTimeout(resolve, 200));
return { notification: 'sent', report: deps['generate-report'] };
},
dependencies: ['generate-report']
}
};
// 添加所有任务
queue.addTasks(tasks);
// 显示依赖图
queue.printDependencyGraph();
// 执行队列
const result = await queue.execute();
console.log('\n=== 执行结果 ===');
console.log(`总耗时: ${result.duration}ms`);
console.log(`成功: ${result.completed}, 失败: ${result.failed}`);
console.log('最终结果:', result.results['send-notification']);
}
await demoDependencyQueue();
console.log();
3. 支持优先级和延迟执行的异步队列:
// 高级异步队列 - 支持优先级、延迟、取消等功能
class AdvancedAsyncQueue {
constructor(options = {}) {
this.queues = {
high: [],
normal: [],
low: []
};
this.running = new Map();
this.completed = new Map();
this.cancelled = new Set();
this.delayed = new Map();
this.maxConcurrency = options.maxConcurrency || 3;
this.currentConcurrency = 0;
this.taskTimeout = options.taskTimeout || 30000;
this.retryAttempts = options.retryAttempts || 3;
this.retryDelay = options.retryDelay || 1000;
this.metrics = {
totalAdded: 0,
totalCompleted: 0,
totalFailed: 0,
totalCancelled: 0,
averageExecutionTime: 0
};
// 事件处理
this.listeners = new Map();
// 定时检查延迟任务
this.delayCheckInterval = setInterval(() => {
this.processDelayedTasks();
}, 100);
}
// 事件系统
on(event, callback) {
if (!this.listeners.has(event)) {
this.listeners.set(event, []);
}
this.listeners.get(event).push(callback);
return this;
}
emit(event, data) {
const callbacks = this.listeners.get(event);
if (callbacks) {
callbacks.forEach(callback => {
try {
callback(data);
} catch (error) {
console.error(`Event callback error for ${event}:`, error);
}
});
}
}
// 生成唯一任务ID
generateTaskId() {
return `task_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
}
// 添加任务
add(taskFn, options = {}) {
const {
priority = 'normal',
delay = 0,
timeout = this.taskTimeout,
retries = this.retryAttempts,
context = {},
onProgress = null
} = options;
const taskId = this.generateTaskId();
const task = {
id: taskId,
fn: taskFn,
priority,
timeout,
retries,
attempts: 0,
context,
onProgress,
createdAt: Date.now(),
scheduledAt: Date.now() + delay
};
if (delay > 0) {
// 延迟任务
this.delayed.set(taskId, task);
} else {
// 立即加入队列
this.enqueueTask(task);
}
this.metrics.totalAdded++;
this.emit('task-added', { taskId, priority, delay });
// 返回可以取消的 Promise
return {
taskId,
promise: new Promise((resolve, reject) => {
task.resolve = resolve;
task.reject = reject;
}),
cancel: () => this.cancelTask(taskId)
};
}
// 将任务加入优先级队列
enqueueTask(task) {
const queue = this.queues[task.priority] || this.queues.normal;
// 按创建时间排序(FIFO)
const insertIndex = queue.findIndex(t => t.createdAt > task.createdAt);
if (insertIndex === -1) {
queue.push(task);
} else {
queue.splice(insertIndex, 0, task);
}
// 尝试开始处理
this.processQueue();
}
// 处理延迟任务
processDelayedTasks() {
const now = Date.now();
const readyTasks = [];
for (const [taskId, task] of this.delayed.entries()) {
if (now >= task.scheduledAt) {
readyTasks.push(taskId);
}
}
readyTasks.forEach(taskId => {
const task = this.delayed.get(taskId);
this.delayed.delete(taskId);
if (!this.cancelled.has(taskId)) {
this.enqueueTask(task);
}
});
}
// 获取下一个要执行的任务
getNextTask() {
// 优先级顺序:high -> normal -> low
for (const priority of ['high', 'normal', 'low']) {
const queue = this.queues[priority];
if (queue.length > 0) {
return queue.shift();
}
}
return null;
}
// 处理队列
async processQueue() {
while (this.currentConcurrency < this.maxConcurrency) {
const task = this.getNextTask();
if (!task) break;
if (this.cancelled.has(task.id)) {
this.handleTaskCancellation(task);
continue;
}
// 异步执行任务
this.executeTask(task);
}
}
// 执行单个任务
async executeTask(task) {
this.currentConcurrency++;
this.running.set(task.id, task);
const startTime = Date.now();
task.startedAt = startTime;
this.emit('task-started', {
taskId: task.id,
priority: task.priority,
attempt: task.attempts + 1
});
try {
task.attempts++;
// 创建任务执行 Promise
const taskPromise = this.createTaskPromise(task);
// 创建超时 Promise
const timeoutPromise = new Promise((_, reject) => {
setTimeout(() => {
reject(new Error(`Task ${task.id} timed out after ${task.timeout}ms`));
}, task.timeout);
});
// 竞争执行
const result = await Promise.race([taskPromise, timeoutPromise]);
// 任务成功
const executionTime = Date.now() - startTime;
task.completedAt = Date.now();
task.executionTime = executionTime;
this.completed.set(task.id, { task, result, executionTime });
this.running.delete(task.id);
// 更新统计
this.metrics.totalCompleted++;
this.updateAverageExecutionTime(executionTime);
task.resolve(result);
this.emit('task-completed', {
taskId: task.id,
result,
executionTime,
attempts: task.attempts
});
} catch (error) {
await this.handleTaskError(task, error, startTime);
} finally {
this.currentConcurrency--;
// 继续处理队列
setTimeout(() => this.processQueue(), 0);
}
}
// 创建任务执行 Promise
createTaskPromise(task) {
return new Promise(async (resolve, reject) => {
try {
// 创建任务上下文
const taskContext = {
...task.context,
taskId: task.id,
attempt: task.attempts,
progress: (data) => {
if (task.onProgress) {
task.onProgress(data);
}
this.emit('task-progress', { taskId: task.id, ...data });
}
};
const result = await task.fn(taskContext);
resolve(result);
} catch (error) {
reject(error);
}
});
}
// 处理任务错误
async handleTaskError(task, error, startTime) {
const executionTime = Date.now() - startTime;
// 检查是否应该重试
if (task.attempts < task.retries && !this.cancelled.has(task.id)) {
console.log(`Task ${task.id} failed (attempt ${task.attempts}/${task.retries}), retrying...`);
this.emit('task-retry', {
taskId: task.id,
error: error.message,
attempt: task.attempts,
maxRetries: task.retries
});
// 延迟重试
await new Promise(resolve => setTimeout(resolve, this.retryDelay * task.attempts));
// 重新加入队列
this.running.delete(task.id);
this.enqueueTask(task);
} else {
// 重试次数用完或任务被取消
this.running.delete(task.id);
this.metrics.totalFailed++;
task.reject(error);
this.emit('task-failed', {
taskId: task.id,
error: error.message,
attempts: task.attempts,
executionTime
});
}
}
// 取消任务
cancelTask(taskId) {
this.cancelled.add(taskId);
// 从延迟队列移除
if (this.delayed.has(taskId)) {
const task = this.delayed.get(taskId);
this.delayed.delete(taskId);
task.reject(new Error('Task cancelled'));
}
// 从普通队列移除
for (const queue of Object.values(this.queues)) {
const index = queue.findIndex(task => task.id === taskId);
if (index !== -1) {
const task = queue.splice(index, 1)[0];
task.reject(new Error('Task cancelled'));
break;
}
}
// 如果任务正在运行,标记为取消(实际取消需要任务配合)
if (this.running.has(taskId)) {
const task = this.running.get(taskId);
task.cancelled = true;
}
this.metrics.totalCancelled++;
this.emit('task-cancelled', { taskId });
return true;
}
// 处理任务取消
handleTaskCancellation(task) {
task.reject(new Error('Task cancelled'));
this.emit('task-cancelled', { taskId: task.id });
}
// 更新平均执行时间
updateAverageExecutionTime(newTime) {
const total = this.metrics.totalCompleted;
const currentAvg = this.metrics.averageExecutionTime;
this.metrics.averageExecutionTime = ((currentAvg * (total - 1)) + newTime) / total;
}
// 获取队列状态
getStatus() {
const totalQueued = Object.values(this.queues).reduce((sum, queue) => sum + queue.length, 0);
return {
queued: {
total: totalQueued,
high: this.queues.high.length,
normal: this.queues.normal.length,
low: this.queues.low.length
},
delayed: this.delayed.size,
running: this.running.size,
completed: this.completed.size,
cancelled: this.cancelled.size,
concurrency: {
current: this.currentConcurrency,
max: this.maxConcurrency
},
metrics: { ...this.metrics }
};
}
// 暂停队列
pause() {
this.paused = true;
return this;
}
// 恢复队列
resume() {
this.paused = false;
this.processQueue();
return this;
}
// 清空队列
clear() {
// 取消所有待处理任务
for (const queue of Object.values(this.queues)) {
queue.forEach(task => {
task.reject(new Error('Queue cleared'));
});
queue.length = 0;
}
// 取消所有延迟任务
for (const [taskId, task] of this.delayed) {
task.reject(new Error('Queue cleared'));
}
this.delayed.clear();
this.emit('queue-cleared', {});
return this;
}
// 销毁队列
destroy() {
this.clear();
clearInterval(this.delayCheckInterval);
this.listeners.clear();
this.emit('queue-destroyed', {});
}
// 等待所有任务完成
async waitForCompletion() {
return new Promise(resolve => {
const checkCompletion = () => {
const status = this.getStatus();
const totalPending = status.queued.total + status.delayed + status.running;
if (totalPending === 0) {
resolve(this.getStatus());
} else {
setTimeout(checkCompletion, 100);
}
};
checkCompletion();
});
}
}
// 使用示例
console.log('=== 高级异步队列演示 ===');
async function demoAdvancedQueue() {
const queue = new AdvancedAsyncQueue({
maxConcurrency: 2,
taskTimeout: 5000,
retryAttempts: 2
});
// 监听事件
queue.on('task-started', ({ taskId, priority, attempt }) => {
console.log(`🚀 任务 ${taskId} 开始执行 (优先级: ${priority}, 尝试: ${attempt})`);
});
queue.on('task-completed', ({ taskId, executionTime }) => {
console.log(`✅ 任务 ${taskId} 完成,耗时 ${executionTime}ms`);
});
queue.on('task-failed', ({ taskId, error, attempts }) => {
console.log(`❌ 任务 ${taskId} 失败: ${error} (尝试了 ${attempts} 次)`);
});
queue.on('task-retry', ({ taskId, attempt, maxRetries }) => {
console.log(`🔄 任务 ${taskId} 重试 ${attempt}/${maxRetries}`);
});
// 创建不同类型的任务
const tasks = [];
// 高优先级任务
tasks.push(queue.add(async (ctx) => {
ctx.progress({ step: 1, message: '开始高优先级任务' });
await new Promise(resolve => setTimeout(resolve, 1000));
ctx.progress({ step: 2, message: '高优先级任务完成' });
return '高优先级任务结果';
}, { priority: 'high' }));
// 普通任务带进度
tasks.push(queue.add(async (ctx) => {
for (let i = 1; i <= 5; i++) {
await new Promise(resolve => setTimeout(resolve, 200));
ctx.progress({ step: i, total: 5, message: `处理步骤 ${i}/5` });
}
return '普通任务结果';
}, { priority: 'normal' }));
// 低优先级延迟任务
tasks.push(queue.add(async () => {
await new Promise(resolve => setTimeout(resolve, 500));
return '低优先级延迟任务结果';
}, { priority: 'low', delay: 2000 }));
// 可能失败的任务
tasks.push(queue.add(async () => {
if (Math.random() < 0.7) {
throw new Error('模拟任务失败');
}
return '幸运任务成功';
}, { retries: 3 }));
// 可取消的任务
const cancellableTask = queue.add(async () => {
await new Promise(resolve => setTimeout(resolve, 3000));
return '长时间运行的任务';
}, { priority: 'normal' });
// 2秒后取消任务
setTimeout(() => {
console.log('取消长时间运行的任务...');
cancellableTask.cancel();
}, 2000);
tasks.push(cancellableTask);
// 显示初始状态
console.log('初始队列状态:', queue.getStatus());
// 等待一些任务完成
try {
const results = await Promise.allSettled(tasks.map(t => t.promise));
console.log('\n=== 任务执行结果 ===');
results.forEach((result, index) => {
if (result.status === 'fulfilled') {
console.log(`任务 ${index + 1}: ✅ ${result.value}`);
} else {
console.log(`任务 ${index + 1}: ❌ ${result.reason.message}`);
}
});
} catch (error) {
console.error('任务执行出错:', error);
}
// 最终状态
const finalStatus = queue.getStatus();
console.log('\n=== 最终队列状态 ===');
console.log(`已完成: ${finalStatus.completed}`);
console.log(`已取消: ${finalStatus.cancelled}`);
console.log(`平均执行时间: ${finalStatus.metrics.averageExecutionTime.toFixed(2)}ms`);
// 清理
queue.destroy();
}
await demoAdvancedQueue();
console.log();
4. 异步队列的实际应用场景:
// 应用场景演示
class RealWorldAsyncQueue {
// 场景1:文件上传队列
static createFileUploadQueue() {
console.log('=== 文件上传队列场景 ===');
const uploadQueue = new AsyncQueue({
maxConcurrency: 3, // 限制并发上传数
onProgress: (progress) => {
console.log(`上传进度: ${progress.completed}/${progress.completed + progress.remaining} 文件`);
}
});
// 模拟文件上传任务
const createUploadTask = (fileName, fileSize) => {
return () => new Promise((resolve, reject) => {
console.log(`开始上传: ${fileName} (${fileSize}MB)`);
// 模拟上传时间(文件越大耗时越长)
const uploadTime = fileSize * 100 + Math.random() * 500;
setTimeout(() => {
if (Math.random() < 0.1) { // 10% 失败率
reject(new Error(`${fileName} 上传失败`));
} else {
resolve({
fileName,
fileSize,
uploadTime,
url: `https://cdn.example.com/${fileName}`
});
}
}, uploadTime);
});
};
// 添加上传任务
const files = [
{ name: 'avatar.jpg', size: 2 },
{ name: 'document.pdf', size: 15 },
{ name: 'video.mp4', size: 50 },
{ name: 'image1.png', size: 5 },
{ name: 'image2.png', size: 3 }
];
const uploadPromises = files.map(file =>
uploadQueue.add(createUploadTask(file.name, file.size), 1) // 高优先级
);
return Promise.all(uploadPromises);
}
// 场景2:API请求限流
static createAPIRateLimitQueue() {
console.log('\n=== API请求限流场景 ===');
class RateLimitedAPIQueue extends AsyncQueue {
constructor(options) {
super(options);
this.rateLimit = options.rateLimit || 10; // 每秒请求数
this.requestTimes = [];
}
async add(apiCall, priority = 0) {
// 检查速率限制
await this.checkRateLimit();
return super.add(apiCall, priority);
}
async checkRateLimit() {
const now = Date.now();
const oneSecondAgo = now - 1000;
// 清理超过1秒的请求记录
this.requestTimes = this.requestTimes.filter(time => time > oneSecondAgo);
// 如果达到速率限制,等待
if (this.requestTimes.length >= this.rateLimit) {
const oldestRequest = Math.min(...this.requestTimes);
const waitTime = 1000 - (now - oldestRequest);
if (waitTime > 0) {
console.log(`速率限制,等待 ${waitTime}ms...`);
await new Promise(resolve => setTimeout(resolve, waitTime));
}
}
this.requestTimes.push(now);
}
}
const apiQueue = new RateLimitedAPIQueue({
maxConcurrency: 5,
rateLimit: 3 // 每秒最多3个请求
});
// 模拟API请求
const createAPIRequest = (endpoint, params) => {
return () => new Promise((resolve, reject) => {
console.log(`API请求: ${endpoint}`);
setTimeout(() => {
if (Math.random() < 0.05) { // 5% 失败率
reject(new Error(`API ${endpoint} 请求失败`));
} else {
resolve({
endpoint,
params,
data: `${endpoint} 的响应数据`,
timestamp: new Date().toISOString()
});
}
}, 200 + Math.random() * 300);
});
};
// 添加多个API请求
const requests = [
'/api/users',
'/api/posts',
'/api/comments',
'/api/notifications',
'/api/settings',
'/api/analytics',
'/api/reports'
].map(endpoint =>
apiQueue.add(createAPIRequest(endpoint, {}))
);
return Promise.all(requests);
}
// 场景3:数据处理管道
static createDataProcessingQueue() {
console.log('\n=== 数据处理管道场景 ===');
const processingQueue = new DependencyAsyncQueue({
maxConcurrency: 4
});
// 数据处理任务
const tasks = {
'fetch-raw-data': {
task: async () => {
console.log('获取原始数据...');
await new Promise(resolve => setTimeout(resolve, 500));
return Array.from({ length: 1000 }, (_, i) => ({
id: i + 1,
value: Math.random() * 100,
timestamp: Date.now() + i
}));
},
dependencies: []
},
'validate-data': {
task: async (deps) => {
console.log('验证数据...');
const rawData = deps['fetch-raw-data'];
await new Promise(resolve => setTimeout(resolve, 300));
return rawData.filter(item =>
item.value > 0 && item.value < 100
);
},
dependencies: ['fetch-raw-data']
},
'transform-data': {
task: async (deps) => {
console.log('转换数据格式...');
const validData = deps['validate-data'];
await new Promise(resolve => setTimeout(resolve, 400));
return validData.map(item => ({
...item,
normalized: item.value / 100,
category: item.value > 50 ? 'high' : 'low'
}));
},
dependencies: ['validate-data']
},
'aggregate-data': {
task: async (deps) => {
console.log('聚合数据...');
const transformedData = deps['transform-data'];
await new Promise(resolve => setTimeout(resolve, 200));
const stats = {
total: transformedData.length,
high: transformedData.filter(item => item.category === 'high').length,
low: transformedData.filter(item => item.category === 'low').length,
average: transformedData.reduce((sum, item) => sum + item.value, 0) / transformedData.length
};
return { data: transformedData, stats };
},
dependencies: ['transform-data']
},
'save-to-database': {
task: async (deps) => {
console.log('保存到数据库...');
const { data, stats } = deps['aggregate-data'];
await new Promise(resolve => setTimeout(resolve, 600));
return {
saved: true,
recordCount: data.length,
stats
};
},
dependencies: ['aggregate-data']
},
'generate-report': {
task: async (deps) => {
console.log('生成报告...');
const saveResult = deps['save-to-database'];
await new Promise(resolve => setTimeout(resolve, 300));
return {
reportId: `RPT_${Date.now()}`,
summary: saveResult.stats,
generatedAt: new Date().toISOString()
};
},
dependencies: ['save-to-database']
}
};
processingQueue.addTasks(tasks);
return processingQueue.execute();
}
// 场景4:微服务调用编排
static createMicroserviceOrchestration() {
console.log('\n=== 微服务调用编排场景 ===');
const orchestrationQueue = new AdvancedAsyncQueue({
maxConcurrency: 6,
taskTimeout: 10000
});
// 微服务调用模拟
const callMicroservice = (serviceName, method, data, options = {}) => {
return async (ctx) => {
const { timeout = 2000, shouldFail = false } = options;
ctx.progress({ service: serviceName, status: 'calling', method });
console.log(`调用微服务: ${serviceName}.${method}`);
return new Promise((resolve, reject) => {
setTimeout(() => {
if (shouldFail && Math.random() < 0.2) {
reject(new Error(`${serviceName} 服务调用失败`));
} else {
ctx.progress({ service: serviceName, status: 'completed', method });
resolve({
service: serviceName,
method,
data: `${serviceName} 的响应`,
timestamp: Date.now()
});
}
}, timeout);
});
};
};
// 编排微服务调用
const serviceCallsHigh = [
orchestrationQueue.add(
callMicroservice('user-service', 'getProfile', { userId: 123 }),
{ priority: 'high', retries: 3 }
),
orchestrationQueue.add(
callMicroservice('auth-service', 'validateToken', { token: 'xxx' }),
{ priority: 'high', retries: 3 }
)
];
const serviceCallsNormal = [
orchestrationQueue.add(
callMicroservice('product-service', 'getRecommendations', {}),
{ priority: 'normal', retries: 2 }
),
orchestrationQueue.add(
callMicroservice('inventory-service', 'checkStock', { productIds: [1, 2, 3] }),
{ priority: 'normal', retries: 2 }
),
orchestrationQueue.add(
callMicroservice('pricing-service', 'calculatePrices', {}),
{ priority: 'normal', retries: 2 }
)
];
const serviceCallsLow = [
orchestrationQueue.add(
callMicroservice('analytics-service', 'trackEvent', { event: 'page_view' }),
{ priority: 'low', retries: 1 }
),
orchestrationQueue.add(
callMicroservice('notification-service', 'sendEmail', { template: 'welcome' }),
{ priority: 'low', delay: 1000, retries: 1 }
)
];
return Promise.allSettled([
...serviceCallsHigh.map(call => call.promise),
...serviceCallsNormal.map(call => call.promise),
...serviceCallsLow.map(call => call.promise)
]);
}
}
// 运行所有应用场景演示
async function runRealWorldExamples() {
try {
// 文件上传队列
const uploadResults = await RealWorldAsyncQueue.createFileUploadQueue();
console.log('上传完成,成功文件数:', uploadResults.filter(r => r).length);
// API限流队列
const apiResults = await RealWorldAsyncQueue.createAPIRateLimitQueue();
console.log('API请求完成,成功请求数:', apiResults.filter(r => r).length);
// 数据处理管道
const processingResult = await RealWorldAsyncQueue.createDataProcessingQueue();
console.log('数据处理完成:', processingResult.completed, '个任务');
// 微服务编排
const orchestrationResults = await RealWorldAsyncQueue.createMicroserviceOrchestration();
const successfulCalls = orchestrationResults.filter(r => r.status === 'fulfilled').length;
console.log('微服务调用完成,成功:', successfulCalls, '个调用');
} catch (error) {
console.error('演示过程中出错:', error);
}
}
await runRealWorldExamples();
异步队列应用场景总结:
并发控制场景:
任务调度场景:
资源管理场景:
用户体验场景:
系统集成场景:
How to use Proxy to implement data validation and automatic formatting?
How to use Proxy to implement data validation and automatic formatting?
考察点:拦截器与数据处理。
答案:
Proxy 是 ES6 提供的强大元编程工具,可以拦截和自定义对象操作。通过 Proxy 的各种 trap(拦截器),我们可以实现数据校验、自动格式化、类型转换等功能。让我们通过具体实现来探索 Proxy 在数据处理中的应用。
1. 基础数据校验 Proxy:
// 基础数据校验系统
class DataValidator {
constructor(schema = {}) {
this.schema = schema;
this.errors = new Map();
this.formatters = new Map();
this.validators = new Map();
// 内置验证器
this.setupBuiltInValidators();
// 内置格式化器
this.setupBuiltInFormatters();
}
setupBuiltInValidators() {
this.validators.set('required', (value) => {
return value !== undefined && value !== null && value !== '';
});
this.validators.set('string', (value) => {
return typeof value === 'string';
});
this.validators.set('number', (value) => {
return typeof value === 'number' && !isNaN(value);
});
this.validators.set('email', (value) => {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value);
});
this.validators.set('phone', (value) => {
return /^[\d\s\-\+\(\)]+$/.test(value);
});
this.validators.set('minLength', (value, min) => {
return value && value.length >= min;
});
this.validators.set('maxLength', (value, max) => {
return value && value.length <= max;
});
this.validators.set('range', (value, min, max) => {
return value >= min && value <= max;
});
}
setupBuiltInFormatters() {
this.formatters.set('trim', (value) => {
return typeof value === 'string' ? value.trim() : value;
});
this.formatters.set('lowercase', (value) => {
return typeof value === 'string' ? value.toLowerCase() : value;
});
this.formatters.set('uppercase', (value) => {
return typeof value === 'string' ? value.toUpperCase() : value;
});
this.formatters.set('capitalize', (value) => {
if (typeof value !== 'string') return value;
return value.charAt(0).toUpperCase() + value.slice(1).toLowerCase();
});
this.formatters.set('phone', (value) => {
if (typeof value !== 'string') return value;
// 格式化电话号码:去除非数字字符,添加分隔符
const digits = value.replace(/\D/g, '');
if (digits.length === 11) {
return digits.replace(/(\d{3})(\d{4})(\d{4})/, '$1-$2-$3');
}
return value;
});
this.formatters.set('currency', (value) => {
if (typeof value !== 'number') return value;
return `¥${value.toFixed(2)}`;
});
}
// 验证单个值
validateField(key, value, fieldSchema) {
const errors = [];
// 应用格式化器
if (fieldSchema.format) {
const formatters = Array.isArray(fieldSchema.format) ?
fieldSchema.format : [fieldSchema.format];
for (const formatterName of formatters) {
const formatter = this.formatters.get(formatterName);
if (formatter) {
value = formatter(value);
} else if (typeof formatterName === 'function') {
value = formatterName(value);
}
}
}
// 应用验证规则
if (fieldSchema.rules) {
for (const rule of fieldSchema.rules) {
const { validator, params = [], message } = rule;
const validatorFn = this.validators.get(validator) || validator;
if (typeof validatorFn === 'function') {
const isValid = validatorFn(value, ...params);
if (!isValid) {
errors.push(message || `${key} validation failed for ${validator}`);
}
} else {
console.warn(`Validator ${validator} not found`);
}
}
}
return { value, errors };
}
// 创建代理对象
createProxy(target = {}) {
const validator = this;
return new Proxy(target, {
set(obj, key, value) {
const fieldSchema = validator.schema[key];
if (fieldSchema) {
const result = validator.validateField(key, value, fieldSchema);
if (result.errors.length > 0) {
validator.errors.set(key, result.errors);
// 根据配置决定是否阻止设置
if (fieldSchema.strict === true) {
throw new Error(`Validation failed for ${key}: ${result.errors.join(', ')}`);
}
} else {
validator.errors.delete(key);
}
// 使用格式化后的值
obj[key] = result.value;
} else {
// 没有定义schema的字段直接设置
obj[key] = value;
}
return true;
},
get(obj, key) {
// 特殊方法
if (key === '$validate') {
return () => validator.validateAll(obj);
}
if (key === '$errors') {
return Object.fromEntries(validator.errors);
}
if (key === '$isValid') {
return validator.errors.size === 0;
}
if (key === '$reset') {
return () => {
validator.errors.clear();
return true;
};
}
return obj[key];
},
has(obj, key) {
return key in obj || ['$validate', '$errors', '$isValid', '$reset'].includes(key);
},
ownKeys(obj) {
return [...Reflect.ownKeys(obj), '$validate', '$errors', '$isValid', '$reset'];
},
deleteProperty(obj, key) {
if (validator.schema[key] && validator.schema[key].required) {
throw new Error(`Cannot delete required field: ${key}`);
}
validator.errors.delete(key);
delete obj[key];
return true;
}
});
}
// 验证整个对象
validateAll(obj) {
const results = {};
for (const [key, fieldSchema] of Object.entries(this.schema)) {
const value = obj[key];
const result = this.validateField(key, value, fieldSchema);
results[key] = result;
if (result.errors.length > 0) {
this.errors.set(key, result.errors);
} else {
this.errors.delete(key);
}
}
return {
isValid: this.errors.size === 0,
errors: Object.fromEntries(this.errors),
results
};
}
}
// 使用示例
console.log('=== 基础数据校验演示 ===');
const userSchema = {
name: {
rules: [
{ validator: 'required', message: '姓名是必填的' },
{ validator: 'string', message: '姓名必须是字符串' },
{ validator: 'minLength', params: [2], message: '姓名至少2个字符' }
],
format: ['trim', 'capitalize']
},
email: {
rules: [
{ validator: 'required', message: '邮箱是必填的' },
{ validator: 'email', message: '邮箱格式不正确' }
],
format: ['trim', 'lowercase']
},
phone: {
rules: [
{ validator: 'phone', message: '电话号码格式不正确' }
],
format: 'phone'
},
age: {
rules: [
{ validator: 'number', message: '年龄必须是数字' },
{ validator: 'range', params: [0, 150], message: '年龄必须在0-150之间' }
]
},
bio: {
rules: [
{ validator: 'maxLength', params: [200], message: '简介不能超过200字符' }
],
format: 'trim'
}
};
const validator = new DataValidator(userSchema);
const user = validator.createProxy();
try {
// 设置用户数据
user.name = ' john doe '; // 会被格式化为 "John doe"
user.email = ' [email protected] '; // 会被格式化为 "[email protected]"
user.phone = '13812345678'; // 会被格式化为 "138-1234-5678"
user.age = 25;
user.bio = ' 一个热爱编程的开发者 '; // 会被trim
console.log('用户数据:', user);
console.log('验证状态:', user.$isValid);
console.log('错误信息:', user.$errors);
// 尝试设置无效数据
try {
user.email = 'invalid-email';
} catch (error) {
console.log('设置无效邮箱被阻止');
}
console.log('最终错误:', user.$errors);
} catch (error) {
console.error('数据校验错误:', error.message);
}
console.log();
2. 高级数据模型 Proxy:
// 高级数据模型系统
class SmartModel {
constructor(schema = {}, options = {}) {
this.schema = schema;
this.options = {
strict: false,
autoSave: false,
history: false,
computed: true,
...options
};
this.data = {};
this.computed = new Map();
this.watchers = new Map();
this.history = [];
this.changeListeners = [];
this.validationErrors = new Map();
// 设置计算属性
this.setupComputedProperties();
}
setupComputedProperties() {
for (const [key, config] of Object.entries(this.schema)) {
if (config.computed) {
this.computed.set(key, config.computed);
}
if (config.watch) {
this.watchers.set(key, config.watch);
}
}
}
// 类型转换和格式化
transformValue(key, value, config) {
let result = value;
// 类型转换
if (config.type) {
switch (config.type) {
case 'string':
result = String(result);
break;
case 'number':
result = Number(result);
break;
case 'boolean':
result = Boolean(result);
break;
case 'date':
result = new Date(result);
break;
case 'array':
result = Array.isArray(result) ? result : [result];
break;
case 'object':
result = typeof result === 'object' && result !== null ? result : {};
break;
}
}
// 自定义转换器
if (config.transform && typeof config.transform === 'function') {
result = config.transform(result, key);
}
// 格式化器
if (config.formatters) {
const formatters = Array.isArray(config.formatters) ?
config.formatters : [config.formatters];
for (const formatter of formatters) {
if (typeof formatter === 'function') {
result = formatter(result);
} else if (typeof formatter === 'string') {
result = this.applyBuiltInFormatter(formatter, result);
}
}
}
return result;
}
applyBuiltInFormatter(formatter, value) {
const formatters = {
trim: (val) => typeof val === 'string' ? val.trim() : val,
uppercase: (val) => typeof val === 'string' ? val.toUpperCase() : val,
lowercase: (val) => typeof val === 'string' ? val.toLowerCase() : val,
capitalize: (val) => {
if (typeof val !== 'string') return val;
return val.charAt(0).toUpperCase() + val.slice(1);
},
currency: (val) => {
if (typeof val !== 'number') return val;
return new Intl.NumberFormat('zh-CN', {
style: 'currency',
currency: 'CNY'
}).format(val);
},
percentage: (val) => {
if (typeof val !== 'number') return val;
return (val * 100).toFixed(2) + '%';
},
dateFormat: (val) => {
if (!(val instanceof Date)) return val;
return val.toLocaleDateString('zh-CN');
}
};
return formatters[formatter] ? formatters[formatter](value) : value;
}
// 数据验证
validate(key, value, config) {
const errors = [];
// 必填验证
if (config.required && (value === undefined || value === null || value === '')) {
errors.push(`${key} is required`);
}
// 类型验证
if (value !== undefined && value !== null && config.type) {
const expectedType = config.type;
const actualType = Array.isArray(value) ? 'array' : typeof value;
if (expectedType === 'date' && !(value instanceof Date)) {
errors.push(`${key} must be a valid date`);
} else if (expectedType !== 'date' && expectedType !== actualType) {
errors.push(`${key} must be of type ${expectedType}`);
}
}
// 自定义验证器
if (config.validators) {
for (const validator of config.validators) {
const result = validator(value, key);
if (result !== true) {
errors.push(typeof result === 'string' ? result : `${key} validation failed`);
}
}
}
// 长度验证
if (config.minLength && value && value.length < config.minLength) {
errors.push(`${key} must be at least ${config.minLength} characters`);
}
if (config.maxLength && value && value.length > config.maxLength) {
errors.push(`${key} cannot exceed ${config.maxLength} characters`);
}
// 数值范围验证
if (typeof value === 'number') {
if (config.min !== undefined && value < config.min) {
errors.push(`${key} must be at least ${config.min}`);
}
if (config.max !== undefined && value > config.max) {
errors.push(`${key} cannot exceed ${config.max}`);
}
}
return errors;
}
// 计算属性值
getComputedValue(key) {
const computedFn = this.computed.get(key);
if (computedFn) {
return computedFn.call(this, this.data);
}
return undefined;
}
// 触发变更事件
triggerChange(key, oldValue, newValue) {
// 记录历史
if (this.options.history) {
this.history.push({
key,
oldValue,
newValue,
timestamp: new Date()
});
}
// 触发监听器
const watcher = this.watchers.get(key);
if (watcher) {
watcher.call(this, newValue, oldValue, key);
}
// 触发全局变更监听器
this.changeListeners.forEach(listener => {
try {
listener({ key, oldValue, newValue, model: this });
} catch (error) {
console.error('Change listener error:', error);
}
});
// 自动保存
if (this.options.autoSave && typeof this.save === 'function') {
this.save();
}
}
// 创建代理
createProxy() {
const model = this;
return new Proxy(this.data, {
set(target, key, value) {
const config = model.schema[key] || {};
const oldValue = target[key];
try {
// 转换和格式化值
let transformedValue = model.transformValue(key, value, config);
// 验证数据
const errors = model.validate(key, transformedValue, config);
if (errors.length > 0) {
model.validationErrors.set(key, errors);
if (model.options.strict) {
throw new Error(`Validation failed for ${key}: ${errors.join(', ')}`);
}
} else {
model.validationErrors.delete(key);
}
// 设置值
target[key] = transformedValue;
// 触发变更事件
if (oldValue !== transformedValue) {
model.triggerChange(key, oldValue, transformedValue);
}
return true;
} catch (error) {
console.error(`Error setting ${key}:`, error);
if (model.options.strict) {
throw error;
}
return false;
}
},
get(target, key) {
// 特殊属性
if (key === '$model') return model;
if (key === '$errors') return Object.fromEntries(model.validationErrors);
if (key === '$isValid') return model.validationErrors.size === 0;
if (key === '$history') return [...model.history];
if (key === '$schema') return model.schema;
// 特殊方法
if (key === '$validate') {
return () => model.validateAll();
}
if (key === '$reset') {
return () => model.reset();
}
if (key === '$toJSON') {
return () => model.toJSON();
}
if (key === '$watch') {
return (callback) => model.addChangeListener(callback);
}
// 计算属性
if (model.computed.has(key)) {
return model.getComputedValue(key);
}
return target[key];
},
has(target, key) {
return key in target ||
model.computed.has(key) ||
['$model', '$errors', '$isValid', '$history', '$schema',
'$validate', '$reset', '$toJSON', '$watch'].includes(key);
},
ownKeys(target) {
return [
...Reflect.ownKeys(target),
...model.computed.keys(),
'$model', '$errors', '$isValid', '$history', '$schema'
];
},
deleteProperty(target, key) {
const config = model.schema[key];
if (config && config.required) {
throw new Error(`Cannot delete required field: ${key}`);
}
const oldValue = target[key];
delete target[key];
model.validationErrors.delete(key);
model.triggerChange(key, oldValue, undefined);
return true;
}
});
}
// 验证所有字段
validateAll() {
const errors = {};
let isValid = true;
for (const [key, config] of Object.entries(this.schema)) {
const value = this.data[key];
const fieldErrors = this.validate(key, value, config);
if (fieldErrors.length > 0) {
errors[key] = fieldErrors;
this.validationErrors.set(key, fieldErrors);
isValid = false;
} else {
this.validationErrors.delete(key);
}
}
return { isValid, errors };
}
// 重置模型
reset() {
for (const key of Object.keys(this.data)) {
delete this.data[key];
}
this.validationErrors.clear();
this.history = [];
}
// 添加变更监听器
addChangeListener(callback) {
this.changeListeners.push(callback);
// 返回取消函数
return () => {
const index = this.changeListeners.indexOf(callback);
if (index > -1) {
this.changeListeners.splice(index, 1);
}
};
}
// 序列化为JSON
toJSON() {
const result = { ...this.data };
// 包含计算属性
for (const key of this.computed.keys()) {
result[key] = this.getComputedValue(key);
}
return result;
}
}
// 使用示例
console.log('=== 高级数据模型演示 ===');
const productSchema = {
name: {
type: 'string',
required: true,
minLength: 2,
maxLength: 50,
formatters: ['trim', 'capitalize'],
validators: [
(value) => value.length > 0 || 'Product name cannot be empty'
]
},
price: {
type: 'number',
required: true,
min: 0,
transform: (value) => parseFloat(value) || 0,
watch: function(newPrice, oldPrice) {
console.log(`价格变更: ${oldPrice} -> ${newPrice}`);
}
},
category: {
type: 'string',
formatters: ['lowercase'],
validators: [
(value) => ['electronics', 'clothing', 'books', 'home'].includes(value) ||
'Invalid category'
]
},
description: {
type: 'string',
formatters: ['trim'],
maxLength: 500
},
inStock: {
type: 'boolean',
transform: (value) => Boolean(value)
},
createdAt: {
type: 'date',
transform: (value) => value instanceof Date ? value : new Date()
},
// 计算属性
displayPrice: {
computed: function(data) {
return data.price ? `¥${data.price.toFixed(2)}` : '价格待定';
}
},
status: {
computed: function(data) {
if (!data.inStock) return '缺货';
if (data.price > 100) return '高价商品';
return '普通商品';
}
},
summary: {
computed: function(data) {
return `${data.name || '未命名'} - ${this.displayPrice} (${this.status})`;
}
}
};
const productModel = new SmartModel(productSchema, {
strict: false,
history: true,
computed: true
});
const product = productModel.createProxy();
// 监听变更
const unwatch = product.$watch((change) => {
console.log(`字段变更: ${change.key} = ${change.newValue}`);
});
try {
// 设置商品数据
product.name = ' iPhone 15 Pro '; // 会被格式化
product.price = '999.99'; // 会被转换为数字
product.category = 'ELECTRONICS'; // 会被转换为小写
product.description = ' 最新款iPhone,性能强劲 '; // 会被trim
product.inStock = 'true'; // 会被转换为boolean
console.log('商品信息:');
console.log('- 名称:', product.name);
console.log('- 价格:', product.price);
console.log('- 分类:', product.category);
console.log('- 库存:', product.inStock);
console.log('- 显示价格:', product.displayPrice);
console.log('- 状态:', product.status);
console.log('- 摘要:', product.summary);
console.log('\n验证状态:', product.$isValid);
console.log('错误信息:', product.$errors);
// 尝试设置无效数据
product.category = 'invalid-category';
console.log('设置无效分类后的错误:', product.$errors);
// 查看历史记录
console.log('\n变更历史:');
product.$history.forEach((change, index) => {
console.log(`${index + 1}. ${change.key}: ${change.oldValue} -> ${change.newValue}`);
});
} catch (error) {
console.error('模型操作错误:', error);
} finally {
unwatch(); // 取消监听
}
console.log();
3. 复杂表单验证 Proxy:
// 复杂表单验证系统
class FormValidator {
constructor() {
this.forms = new Map();
this.globalValidators = new Map();
this.setupGlobalValidators();
}
setupGlobalValidators() {
// 全局验证器
this.globalValidators.set('required', {
validate: (value) => value !== undefined && value !== null && value !== '',
message: '此字段是必填的'
});
this.globalValidators.set('email', {
validate: (value) => !value || /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value),
message: '请输入有效的邮箱地址'
});
this.globalValidators.set('phone', {
validate: (value) => !value || /^1[3-9]\d{9}$/.test(value),
message: '请输入有效的手机号码'
});
this.globalValidators.set('idCard', {
validate: (value) => {
if (!value) return true;
return /^[1-9]\d{5}(18|19|20)\d{2}((0[1-9])|(1[0-2]))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$/.test(value);
},
message: '请输入有效的身份证号码'
});
this.globalValidators.set('url', {
validate: (value) => !value || /^https?:\/\/[^\s/$.?#].[^\s]*$/.test(value),
message: '请输入有效的URL地址'
});
this.globalValidators.set('minLength', {
validate: (value, min) => !value || value.length >= min,
message: (min) => `最少需要${min}个字符`
});
this.globalValidators.set('maxLength', {
validate: (value, max) => !value || value.length <= max,
message: (max) => `最多允许${max}个字符`
});
this.globalValidators.set('range', {
validate: (value, min, max) => {
const num = Number(value);
return isNaN(num) || (num >= min && num <= max);
},
message: (min, max) => `值必须在${min}到${max}之间`
});
this.globalValidators.set('match', {
validate: (value, targetField, formData) => {
return !value || value === formData[targetField];
},
message: (targetField) => `必须与${targetField}字段一致`
});
}
// 创建表单验证器
createFormValidator(formSchema, options = {}) {
const formId = options.formId || Math.random().toString(36).substr(2, 9);
const formValidator = {
id: formId,
schema: formSchema,
data: {},
errors: new Map(),
touched: new Set(),
submitted: false,
options: {
validateOnChange: true,
validateOnBlur: true,
showErrorsImmediately: false,
...options
},
// 字段级验证
validateField: (key, value, formData) => {
const fieldSchema = formSchema[key];
if (!fieldSchema) return [];
const errors = [];
const validators = fieldSchema.validators || [];
for (const validatorConfig of validators) {
const { name, params = [], message } = validatorConfig;
const validator = this.globalValidators.get(name);
if (!validator) {
console.warn(`Validator ${name} not found`);
continue;
}
const isValid = validator.validate(value, ...params, formData);
if (!isValid) {
const errorMessage = message ||
(typeof validator.message === 'function' ?
validator.message(...params) : validator.message);
errors.push(errorMessage);
}
}
return errors;
},
// 表单级验证
validateForm: () => {
const allErrors = new Map();
let isValid = true;
for (const key of Object.keys(formSchema)) {
const value = formValidator.data[key];
const fieldErrors = formValidator.validateField(key, value, formValidator.data);
if (fieldErrors.length > 0) {
allErrors.set(key, fieldErrors);
isValid = false;
}
}
formValidator.errors = allErrors;
return { isValid, errors: Object.fromEntries(allErrors) };
},
// 重置表单
reset: () => {
for (const key of Object.keys(formValidator.data)) {
delete formValidator.data[key];
}
formValidator.errors.clear();
formValidator.touched.clear();
formValidator.submitted = false;
},
// 设置字段为已触碰
touch: (key) => {
formValidator.touched.add(key);
},
// 获取表单状态
getState: () => ({
isValid: formValidator.errors.size === 0,
errors: Object.fromEntries(formValidator.errors),
touched: Array.from(formValidator.touched),
submitted: formValidator.submitted,
data: { ...formValidator.data }
})
};
this.forms.set(formId, formValidator);
// 创建代理
const proxy = new Proxy(formValidator.data, {
set(target, key, value) {
const oldValue = target[key];
// 应用格式化器
const fieldSchema = formSchema[key];
let processedValue = value;
if (fieldSchema && fieldSchema.formatters) {
for (const formatter of fieldSchema.formatters) {
if (typeof formatter === 'function') {
processedValue = formatter(processedValue);
} else {
processedValue = applyBuiltInFormatter(formatter, processedValue);
}
}
}
target[key] = processedValue;
// 实时验证
if (formValidator.options.validateOnChange) {
const fieldErrors = formValidator.validateField(key, processedValue, target);
if (fieldErrors.length > 0) {
formValidator.errors.set(key, fieldErrors);
} else {
formValidator.errors.delete(key);
}
}
// 触发变更事件
if (formValidator.options.onChange) {
formValidator.options.onChange(key, processedValue, oldValue);
}
return true;
},
get(target, key) {
// 特殊方法和属性
if (key === '$validate') return formValidator.validateForm;
if (key === '$reset') return formValidator.reset;
if (key === '$touch') return formValidator.touch;
if (key === '$state') return formValidator.getState();
if (key === '$errors') return Object.fromEntries(formValidator.errors);
if (key === '$isValid') return formValidator.errors.size === 0;
if (key === '$touched') return Array.from(formValidator.touched);
if (key === '$submit') {
return (onSubmit) => {
formValidator.submitted = true;
const validation = formValidator.validateForm();
if (validation.isValid && onSubmit) {
return onSubmit(formValidator.data);
} else if (!validation.isValid && formValidator.options.onError) {
formValidator.options.onError(validation.errors);
}
return validation;
};
}
return target[key];
},
has(target, key) {
return key in target ||
['$validate', '$reset', '$touch', '$state', '$errors',
'$isValid', '$touched', '$submit'].includes(key);
}
});
return proxy;
}
}
// 内置格式化器
function applyBuiltInFormatter(formatter, value) {
const formatters = {
trim: (val) => typeof val === 'string' ? val.trim() : val,
uppercase: (val) => typeof val === 'string' ? val.toUpperCase() : val,
lowercase: (val) => typeof val === 'string' ? val.toLowerCase() : val,
removeSpaces: (val) => typeof val === 'string' ? val.replace(/\s+/g, '') : val,
phone: (val) => {
if (typeof val !== 'string') return val;
const digits = val.replace(/\D/g, '');
return digits.replace(/(\d{3})(\d{4})(\d{4})/, '$1 $2 $3');
},
currency: (val) => {
const num = parseFloat(val);
return isNaN(num) ? val : num.toFixed(2);
}
};
return formatters[formatter] ? formatters[formatter](value) : value;
}
// 使用示例
console.log('=== 复杂表单验证演示 ===');
const formValidator = new FormValidator();
// 注册表单模式
const registrationSchema = {
username: {
validators: [
{ name: 'required' },
{ name: 'minLength', params: [3] },
{ name: 'maxLength', params: [20] }
],
formatters: ['trim', 'lowercase']
},
email: {
validators: [
{ name: 'required' },
{ name: 'email' }
],
formatters: ['trim', 'lowercase']
},
password: {
validators: [
{ name: 'required' },
{ name: 'minLength', params: [8], message: '密码至少需要8位字符' }
]
},
confirmPassword: {
validators: [
{ name: 'required' },
{ name: 'match', params: ['password'], message: '两次输入的密码不一致' }
]
},
phone: {
validators: [
{ name: 'phone' }
],
formatters: ['removeSpaces', 'phone']
},
age: {
validators: [
{ name: 'required' },
{ name: 'range', params: [18, 100] }
]
},
website: {
validators: [
{ name: 'url' }
]
}
};
// 创建表单实例
const form = formValidator.createFormValidator(registrationSchema, {
formId: 'registration',
validateOnChange: true,
onChange: (field, newValue, oldValue) => {
console.log(`字段 ${field} 变更: ${oldValue} -> ${newValue}`);
},
onError: (errors) => {
console.log('表单验证失败:', errors);
}
});
try {
// 填写表单数据
console.log('开始填写注册表单...\n');
form.username = ' JohnDoe123 '; // 会被格式化为 "johndoe123"
form.email = ' [email protected] '; // 会被格式化为 "[email protected]"
form.password = 'password123';
form.confirmPassword = 'password123';
form.phone = '138 1234 5678'; // 会被格式化为 "138 1234 5678"
form.age = 25;
form.website = 'https://johndoe.com';
console.log('\n表单数据:');
console.log('- 用户名:', form.username);
console.log('- 邮箱:', form.email);
console.log('- 手机:', form.phone);
console.log('- 年龄:', form.age);
console.log('- 网站:', form.website);
console.log('\n表单状态:', form.$state);
// 提交表单
const submitResult = form.$submit((data) => {
console.log('\n表单提交成功,数据:', data);
return { success: true, message: '注册成功' };
});
console.log('提交结果:', submitResult);
// 测试验证错误
console.log('\n测试验证错误...');
form.email = 'invalid-email';
form.confirmPassword = 'different-password';
console.log('验证错误:', form.$errors);
} catch (error) {
console.error('表单操作错误:', error);
}
console.log();
4. 实际应用场景总结:
// 实际应用场景汇总
class ProxyApplicationSummary {
static demonstrateUseCases() {
console.log('=== Proxy 数据处理应用场景汇总 ===\n');
const useCases = [
{
name: '数据校验',
description: '实时验证用户输入,防止无效数据',
examples: ['表单验证', 'API数据校验', '配置文件验证'],
benefits: ['提高数据质量', '减少错误', '用户体验优化']
},
{
name: '自动格式化',
description: '自动转换和格式化数据格式',
examples: ['电话号码格式化', '货币格式化', '日期格式化'],
benefits: ['数据一致性', '显示标准化', '减少手动处理']
},
{
name: '类型转换',
description: '自动进行数据类型转换',
examples: ['字符串转数字', '日期转换', '布尔值转换'],
benefits: ['类型安全', '开发便利', '错误减少']
},
{
name: '计算属性',
description: '基于其他属性动态计算值',
examples: ['全名计算', '总价计算', '状态派生'],
benefits: ['数据同步', '逻辑集中', '性能优化']
},
{
name: '变更监听',
description: '监听数据变化并响应',
examples: ['UI更新', '数据同步', '日志记录'],
benefits: ['响应式编程', '解耦合', '事件驱动']
},
{
name: '访问控制',
description: '控制对象属性的读写权限',
examples: ['只读属性', '私有字段', '权限控制'],
benefits: ['数据安全', '接口设计', '封装性']
},
{
name: '缓存优化',
description: '缓存计算结果和昂贵操作',
examples: ['计算缓存', '请求缓存', '结果记忆'],
benefits: ['性能提升', '资源节约', '响应加速']
},
{
name: '调试辅助',
description: '增强开发和调试体验',
examples: ['操作日志', '性能监控', '错误追踪'],
benefits: ['开发效率', '问题定位', '质量保证']
}
];
useCases.forEach((useCase, index) => {
console.log(`${index + 1}. ${useCase.name}`);
console.log(` 描述: ${useCase.description}`);
console.log(` 示例: ${useCase.examples.join(', ')}`);
console.log(` 优势: ${useCase.benefits.join(', ')}`);
console.log('');
});
console.log('=== 最佳实践建议 ===');
console.log('✅ 合理使用 - Proxy 有性能开销,避免过度使用');
console.log('✅ 错误处理 - 在trap中妥善处理异常情况');
console.log('✅ 类型安全 - 结合TypeScript提供更好的类型支持');
console.log('✅ 文档说明 - 清晰记录Proxy的行为和限制');
console.log('✅ 测试覆盖 - 充分测试各种边界情况');
console.log('✅ 兼容性考虑 - 注意浏览器兼容性要求');
console.log('\n=== 注意事项 ===');
console.log('⚠️ 性能影响 - Proxy会增加属性访问的开销');
console.log('⚠️ 调试复杂 - 可能会干扰调试器的正常工作');
console.log('⚠️ 兼容性限制 - 不支持IE,需要考虑polyfill');
console.log('⚠️ 序列化问题 - Proxy对象可能无法正确序列化');
console.log('⚠️ 内存泄漏 - 需要注意清理引用,防止内存泄漏');
}
}
ProxyApplicationSummary.demonstrateUseCases();
Proxy 数据校验和格式化总结:
核心优势:
主要应用场景:
实现要点:
最佳实践: