引言
今天来分享的这几个ES6神技,保证让你在下一场面试中吊打竞争对手,让面试官眼睛一亮——这小子有点东西!
1. 解构赋值的精妙运用
基础用法大家都会,但你能回答出这些进阶问题吗?
// 面试常问题:交换变量值有哪些方法?
let a = 1, b = 2;
// 方法1:传统方式
let temp = a;
a = b;
b = temp;
// 方法2:ES6解构
[a, b] = [b, a];
// 方法3:算术运算(仅限于数字)
a = a + b;
b = a - b;
a = a - b;
// 方法4:位运算(仅限于整数)
a = a ^ b;
b = a ^ b;
a = a ^ b;
深层解构是展示技术深度的好机会:
// 复杂对象解构
const company = {
name: "TechCorp",
employees: [
{
id: 1,
personal: {
name: "John",
address: {
city: "New York",
zipcode: "10001"
}
}
}
]
};
// 一行代码获取嵌套数据
const { employees: [{ personal: { address: { city } } }] } = company;
console.log(city); // "New York"
// 函数参数解构与默认值结合
function createElement({ tag = 'div', content = '', className = '' } = {}) {
return `<${tag} class="${className}">${content}</${tag}>`;
}
2. Promise及其背后的机制
不只是会用Promise,理解其原理才能让人眼前一亮:
// 手写简易Promise实现(面试加分项)
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(fn => fn());
}
};
const reject = (reason) => {
if (this.state === 'pending') {
this.state = 'rejected';
this.reason = reason;
this.onRejectedCallbacks.forEach(fn => fn());
}
};
try {
executor(resolve, reject);
} catch (err) {
reject(err);
}
}
then(onFulfilled, onRejected) {
if (this.state === 'fulfilled') {
onFulfilled(this.value);
} else if (this.state === 'rejected') {
onRejected(this.reason);
} else {
this.onFulfilledCallbacks.push(() => onFulfilled(this.value));
this.onRejectedCallbacks.push(() => onRejected(this.reason));
}
}
}
Promise组合方法的实战应用:
// 同时处理多个异步操作,但需要所有结果(无论成功失败)
function allSettled(promises) {
return Promise.all(promises.map(p =>
p.then(value => ({ status: 'fulfilled', value }))
.catch(reason => ({ status: 'rejected', reason }))
));
}
// 竞速应用:设置超时时间
function withTimeout(promise, timeout) {
return Promise.race([
promise,
new Promise((_, reject) =>
setTimeout(() => reject(new Error('Timeout')), timeout)
)
]);
}
3. Proxy和Reflect的元编程能力
这是ES6中最强大但最少被深入理解的特性之一:
// 实现自动依赖跟踪(Vue3响应式原理)
function reactive(target) {
const handler = {
get(obj, key, receiver) {
track(obj, key); // 依赖收集
return Reflect.get(obj, key, receiver);
},
set(obj, key, value, receiver) {
Reflect.set(obj, key, value, receiver);
trigger(obj, key); // 触发更新
return true;
}
};
return new Proxy(target, handler);
}
// 实现高级校验器
function createValidator(obj, validations) {
return new Proxy(obj, {
set(target, key, value) {
if (validations[key] && !validations[key](value)) {
throw new Error(`Invalid value for ${key}`);
}
return Reflect.set(target, key, value);
}
});
}
const person = createValidator({}, {
age: value => value >= 0 && value < 150,
email: value => /@/.test(value)
});
person.age = 25; // 成功
person.age = 200; // 抛出错误
4. 迭代器和生成器的底层原理
不只是for...of循环,理解迭代协议:
// 实现可迭代对象
class Range {
constructor(start, end, step = 1) {
this.start = start;
this.end = end;
this.step = step;
}
[Symbol.iterator]() {
let current = this.start;
return {
next: () => {
if (current <= this.end) {
const value = current;
current += this.step;
return { value, done: false };
}
return { done: true };
}
};
}
}
// 生成器控制异步流程
async function* asyncGenerator() {
const urls = ['/api1', '/api2', '/api3'];
for (const url of urls) {
try {
const response = await fetch(url);
yield await response.json();
} catch (error) {
yield { error: error.message };
}
}
}
// 使用for await...of处理
(async () => {
for await (const data of asyncGenerator()) {
console.log(data);
}
})();
5. 模块系统的深入理解
ES6模块与CommonJS的区别是常见面试题:
// ES6模块是静态的,支持tree-shaking
export const apiKey = '12345'; // 可以被tree-shaking
export default function() { console.log('default'); }
// 动态导入的应用
async function loadModule(condition) {
if (condition) {
const module = await import('./moduleA.js');
return module.default;
} else {
const module = await import('./moduleB.js');
return module.default;
}
}
// 循环引用的处理
// ES6模块处理循环引用比CommonJS更合理
// a.js
import { b } from './b.js';
export const a = 'a';
// b.js
import { a } from './a.js';
export const b = 'b';
// 不会报错,但值为undefined,需要设计时避免
6. 符号(Symbol)的实际应用场景
不只是唯一值,还有这些实用场景:
// 定义对象元数据
const USER_LEVEL = Symbol('userLevel');
const user = {
name: 'John',
[USER_LEVEL]: 'admin' // 不会出现在JSON.stringify、Object.keys中
};
// 实现自定义类型检测
class MyArray extends Array {
static [Symbol.hasInstance](instance) {
return Array.isArray(instance) && instance.hasOwnProperty('isMyArray');
}
}
// 替代枚举值
const LOG_LEVEL = {
DEBUG: Symbol('debug'),
INFO: Symbol('info'),
WARN: Symbol('warn'),
ERROR: Symbol('error')
};
面试实战技巧
- 不仅说出是什么,还要解释为什么:当被问到箭头函数时,不要只说"没有自己的this",而要解释设计初衷是为了解决函数嵌套中的this问题
- 结合使用场景:提到Proxy时,可以关联到Vue3的响应式原理或表单验证的实际应用
- 指出注意事项:比如箭头函数不能用作构造函数,没有arguments对象,这些细节能展现你的全面性
- 性能考量:提到扩展运算符时,可以讨论其在大型数组上的性能问题替代方案