前端面试题库 - ES6+新特性篇

一、Promise与异步编程

1. 深入理解Promise原理及使用

Promise三种状态:

  • pending(待定):初始状态
  • fulfilled(已兑现):操作成功
  • rejected(已拒绝):操作失败
javascript 复制代码
// 基本使用
const promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    const success = Math.random() > 0.5;
    if (success) {
      resolve('操作成功');
    } else {
      reject('操作失败');
    }
  }, 1000);
});

promise
  .then(result => {
    console.log(result);
    return '链式调用';
  })
  .then(result => {
    console.log(result);
  })
  .catch(error => {
    console.error(error);
  })
  .finally(() => {
    console.log('无论成功失败都执行');
  });

Promise链式调用规则:

javascript 复制代码
// 1. then返回新的Promise
Promise.resolve(1)
  .then(value => {
    console.log(value); // 1
    return value + 1;
  })
  .then(value => {
    console.log(value); // 2
    return Promise.resolve(value + 1);
  })
  .then(value => {
    console.log(value); // 3
  });

// 2. 错误传播
Promise.reject('错误')
  .then(value => {
    console.log('不会执行');
  })
  .then(value => {
    console.log('也不会执行');
  })
  .catch(error => {
    console.error(error); // '错误'
    return '恢复正常';
  })
  .then(value => {
    console.log(value); // '恢复正常'
  });

// 3. 错误处理最佳实践
function fetchData(url) {
  return fetch(url)
    .then(response => {
      if (!response.ok) {
        throw new Error(`HTTP error! status: ${response.status}`);
      }
      return response.json();
    })
    .catch(error => {
      console.error('Fetch error:', error);
      throw error; // 重新抛出,让调用者处理
    });
}

2. Promise静态方法详解

javascript 复制代码
// 1. Promise.all - 全部成功才成功
const p1 = Promise.resolve(1);
const p2 = Promise.resolve(2);
const p3 = Promise.resolve(3);

Promise.all([p1, p2, p3])
  .then(results => {
    console.log(results); // [1, 2, 3]
  })
  .catch(error => {
    console.error('有一个失败');
  });

// 应用场景:并发请求
async function fetchMultipleData() {
  const [users, posts, comments] = await Promise.all([
    fetch('/api/users').then(r => r.json()),
    fetch('/api/posts').then(r => r.json()),
    fetch('/api/comments').then(r => r.json())
  ]);
  return { users, posts, comments };
}

// 2. Promise.race - 第一个完成的结果
Promise.race([
  new Promise(resolve => setTimeout(() => resolve('快'), 100)),
  new Promise(resolve => setTimeout(() => resolve('慢'), 500))
]).then(result => {
  console.log(result); // '快'
});

// 应用场景:请求超时
function fetchWithTimeout(url, timeout = 5000) {
  return Promise.race([
    fetch(url),
    new Promise((_, reject) => 
      setTimeout(() => reject(new Error('请求超时')), timeout)
    )
  ]);
}

// 3. Promise.allSettled - 等待所有Promise完成(无论成功失败)
Promise.allSettled([
  Promise.resolve(1),
  Promise.reject('error'),
  Promise.resolve(3)
]).then(results => {
  console.log(results);
  // [
  //   { status: 'fulfilled', value: 1 },
  //   { status: 'rejected', reason: 'error' },
  //   { status: 'fulfilled', value: 3 }
  // ]
});

// 4. Promise.any - 第一个成功的结果
Promise.any([
  Promise.reject('错误1'),
  Promise.resolve('成功'),
  Promise.reject('错误2')
]).then(result => {
  console.log(result); // '成功'
});

3. 手写实现Promise

javascript 复制代码
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 (error) {
      reject(error);
    }
  }
  
  then(onFulfilled, onRejected) {
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
    onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason };
    
    const promise2 = new MyPromise((resolve, reject) => {
      if (this.state === 'fulfilled') {
        setTimeout(() => {
          try {
            const x = onFulfilled(this.value);
            this.resolvePromise(promise2, x, resolve, reject);
          } catch (error) {
            reject(error);
          }
        });
      }
      
      if (this.state === 'rejected') {
        setTimeout(() => {
          try {
            const x = onRejected(this.reason);
            this.resolvePromise(promise2, x, resolve, reject);
          } catch (error) {
            reject(error);
          }
        });
      }
      
      if (this.state === 'pending') {
        this.onFulfilledCallbacks.push(() => {
          setTimeout(() => {
            try {
              const x = onFulfilled(this.value);
              this.resolvePromise(promise2, x, resolve, reject);
            } catch (error) {
              reject(error);
            }
          });
        });
        
        this.onRejectedCallbacks.push(() => {
          setTimeout(() => {
            try {
              const x = onRejected(this.reason);
              this.resolvePromise(promise2, x, resolve, reject);
            } catch (error) {
              reject(error);
            }
          });
        });
      }
    });
    
    return promise2;
  }
  
  resolvePromise(promise2, x, resolve, reject) {
    if (promise2 === x) {
      return reject(new TypeError('循环引用'));
    }
    
    if (x instanceof MyPromise) {
      x.then(resolve, reject);
    } else {
      resolve(x);
    }
  }
  
  catch(onRejected) {
    return this.then(null, onRejected);
  }
  
  finally(callback) {
    return this.then(
      value => MyPromise.resolve(callback()).then(() => value),
      reason => MyPromise.resolve(callback()).then(() => { throw reason })
    );
  }
  
  static resolve(value) {
    if (value instanceof MyPromise) return value;
    return new MyPromise(resolve => resolve(value));
  }
  
  static reject(reason) {
    return new MyPromise((_, reject) => reject(reason));
  }
  
  static all(promises) {
    return new MyPromise((resolve, reject) => {
      const results = [];
      let count = 0;
      
      promises.forEach((promise, index) => {
        MyPromise.resolve(promise).then(
          value => {
            results[index] = value;
            count++;
            if (count === promises.length) {
              resolve(results);
            }
          },
          reject
        );
      });
    });
  }
}

4. async/await深度解析

基本概念:

  • async函数返回Promise
  • await等待Promise完成
  • 更优雅的异步代码书写方式
javascript 复制代码
// 1. 基本使用
async function fetchUserData(userId) {
  try {
    const response = await fetch(`/api/users/${userId}`);
    const user = await response.json();
    return user;
  } catch (error) {
    console.error('获取用户数据失败:', error);
    throw error;
  }
}

// 2. 错误处理
async function multipleRequests() {
  try {
    const user = await fetchUserData(1);
    const posts = await fetch(`/api/users/${user.id}/posts`).then(r => r.json());
    return { user, posts };
  } catch (error) {
    // 统一错误处理
    console.error(error);
  }
}

// 3. 并发执行
async function concurrentRequests() {
  // ❌ 串行执行(慢)
  const user1 = await fetchUserData(1);
  const user2 = await fetchUserData(2);
  const user3 = await fetchUserData(3);
  
  // ✅ 并行执行(快)
  const [user1, user2, user3] = await Promise.all([
    fetchUserData(1),
    fetchUserData(2),
    fetchUserData(3)
  ]);
  
  return [user1, user2, user3];
}

// 4. 循环中使用await
async function processUsers(userIds) {
  // ❌ forEach不会等待
  userIds.forEach(async (id) => {
    const user = await fetchUserData(id);
    console.log(user);
  });
  
  // ✅ 串行处理
  for (const id of userIds) {
    const user = await fetchUserData(id);
    console.log(user);
  }
  
  // ✅ 并行处理
  const users = await Promise.all(
    userIds.map(id => fetchUserData(id))
  );
  return users;
}

// 5. 实现sleep函数
function sleep(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

async function demo() {
  console.log('开始');
  await sleep(2000);
  console.log('2秒后');
}

async/await vs Promise对比:

javascript 复制代码
// Promise方式
function getUserPosts() {
  return fetchUserData(1)
    .then(user => fetch(`/api/users/${user.id}/posts`))
    .then(response => response.json())
    .then(posts => {
      console.log(posts);
      return posts;
    })
    .catch(error => {
      console.error(error);
    });
}

// async/await方式(更清晰)
async function getUserPosts() {
  try {
    const user = await fetchUserData(1);
    const response = await fetch(`/api/users/${user.id}/posts`);
    const posts = await response.json();
    console.log(posts);
    return posts;
  } catch (error) {
    console.error(error);
  }
}

二、解构赋值与展开运算符

5. 解构赋值的高级用法

javascript 复制代码
// 1. 数组解构
const [a, b, c] = [1, 2, 3];
const [first, , third] = [1, 2, 3]; // 跳过元素
const [x, ...rest] = [1, 2, 3, 4]; // rest: [2, 3, 4]
const [m, n, o = 0] = [1, 2]; // 默认值

// 交换变量
let p = 1, q = 2;
[p, q] = [q, p];

// 2. 对象解构
const { name, age } = { name: 'Alice', age: 18 };
const { name: userName, age: userAge } = { name: 'Bob', age: 20 }; // 重命名
const { city = 'Beijing' } = {}; // 默认值

// 嵌套解构
const user = {
  id: 1,
  info: {
    name: 'Charlie',
    address: {
      city: 'Shanghai'
    }
  }
};
const { info: { name, address: { city } } } = user;

// 3. 函数参数解构
function createUser({ name, age = 18, ...others }) {
  return { name, age, ...others };
}

createUser({ name: 'David', gender: 'male' });

// 4. 动态属性名解构
const key = 'username';
const { [key]: value } = { username: 'admin' };
console.log(value); // 'admin'

// 5. 实际应用场景
// React Hooks
const [count, setCount] = useState(0);

// API响应处理
const { data: { users, total }, status } = await axios.get('/api/users');

// 配置对象
function request({ url, method = 'GET', headers = {}, data }) {
  // ...
}

6. 展开运算符的应用场景

javascript 复制代码
// 1. 数组操作
const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];

// 合并数组
const merged = [...arr1, ...arr2]; // [1, 2, 3, 4, 5, 6]

// 数组拷贝
const copied = [...arr1]; // 浅拷贝

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

// 找最大值
const max = Math.max(...[1, 5, 3, 9, 2]); // 9

// 2. 对象操作
const obj1 = { a: 1, b: 2 };
const obj2 = { b: 3, c: 4 };

// 合并对象(后面的覆盖前面的)
const merged = { ...obj1, ...obj2 }; // { a: 1, b: 3, c: 4 }

// 对象拷贝
const copied = { ...obj1 };

// 添加/修改属性
const updated = { ...obj1, b: 10, d: 5 };

// 3. 函数参数
function sum(...numbers) {
  return numbers.reduce((acc, num) => acc + num, 0);
}
sum(1, 2, 3, 4); // 10

// 4. React实际应用
// 状态更新
setState(prevState => ({
  ...prevState,
  count: prevState.count + 1
}));

// Props传递
const props = { name: 'Alice', age: 18 };
<Component {...props} />

// 5. 条件属性
const isAdmin = true;
const user = {
  name: 'Bob',
  ...(isAdmin && { role: 'admin' })
};

三、箭头函数与函数增强

7. 箭头函数的特性与限制

核心特性:

javascript 复制代码
// 1. this绑定
const obj = {
  name: 'Alice',
  
  // 普通函数:this动态绑定
  sayName1: function() {
    setTimeout(function() {
      console.log(this.name); // undefined,this指向window
    }, 100);
  },
  
  // 箭头函数:this继承外层
  sayName2: function() {
    setTimeout(() => {
      console.log(this.name); // 'Alice',this指向obj
    }, 100);
  }
};

// 2. 没有arguments对象
const normalFunc = function() {
  console.log(arguments); // ✅ 可用
};

const arrowFunc = () => {
  console.log(arguments); // ❌ ReferenceError
};

// 使用剩余参数代替
const arrowFunc2 = (...args) => {
  console.log(args); // ✅ 可用
};

// 3. 不能作为构造函数
const Person = (name) => {
  this.name = name;
};
// new Person('Alice'); // ❌ TypeError

// 4. 没有prototype属性
console.log(arrowFunc.prototype); // undefined

// 5. 不能用作Generator函数
// const gen = *() => {}; // ❌ 语法错误

何时使用箭头函数:

适合使用:

  • 需要继承外层this的场景
  • 简短的函数表达式
  • 数组方法的回调
  • Promise链中的回调

不适合使用:

  • 对象方法(需要动态this)
  • 需要使用arguments
  • 需要作为构造函数
  • 需要动态绑定this的事件处理器
javascript 复制代码
// ❌ 对象方法不要用箭头函数
const calculator = {
  value: 0,
  add: (num) => {
    this.value += num; // this不指向calculator
  }
};

// ✅ 使用普通函数
const calculator = {
  value: 0,
  add(num) {
    this.value += num;
  }
};

8. 函数默认参数与剩余参数

javascript 复制代码
// 1. 默认参数
function greet(name = 'Guest', greeting = 'Hello') {
  return `${greeting}, ${name}!`;
}

greet(); // 'Hello, Guest!'
greet('Alice'); // 'Hello, Alice!'
greet('Bob', 'Hi'); // 'Hi, Bob!'

// 默认参数表达式
function createId(prefix = 'user', id = Date.now()) {
  return `${prefix}_${id}`;
}

// 参数默认值可以引用其他参数
function multiply(a, b = a * 2) {
  return a * b;
}
multiply(5); // 50

// 2. 剩余参数
function sum(first, ...rest) {
  console.log(first); // 第一个参数
  console.log(rest); // 其余参数数组
  return rest.reduce((acc, num) => acc + num, first);
}

sum(1, 2, 3, 4, 5); // 15

// 3. 实际应用
// 日志函数
function log(level, ...messages) {
  const timestamp = new Date().toISOString();
  console.log(`[${timestamp}] [${level}]`, ...messages);
}

log('INFO', 'Server started', 'Port: 3000');

// 组合多个函数
function compose(...fns) {
  return (value) => fns.reduceRight((acc, fn) => fn(acc), value);
}

const add1 = x => x + 1;
const multiply2 = x => x * 2;
const subtract3 = x => x - 3;

const combined = compose(subtract3, multiply2, add1);
combined(5); // (5 + 1) * 2 - 3 = 9

四、模块化

9. ES6模块与CommonJS的区别

ES6模块(ESM):

javascript 复制代码
// math.js - 导出
export const PI = 3.14159;
export function add(a, b) {
  return a + b;
}
export default class Calculator {
  // ...
}

// app.js - 导入
import Calculator, { PI, add } from './math.js';
import * as math from './math.js';
import { add as sum } from './math.js'; // 重命名

CommonJS:

javascript 复制代码
// math.js - 导出
const PI = 3.14159;
function add(a, b) {
  return a + b;
}
module.exports = { PI, add };
// 或
exports.PI = PI;
exports.add = add;

// app.js - 导入
const { PI, add } = require('./math.js');
const math = require('./math.js');

核心区别:

特性 ES6模块 CommonJS
加载方式 编译时加载(静态) 运行时加载(动态)
输出 值的引用 值的拷贝
this指向 undefined 当前模块
循环依赖 支持 有限支持
Tree Shaking 支持 不支持
使用场景 浏览器、现代Node.js Node.js
javascript 复制代码
// ES6模块 - 值的引用
// counter.js
export let count = 0;
export function increment() {
  count++;
}

// main.js
import { count, increment } from './counter.js';
console.log(count); // 0
increment();
console.log(count); // 1(引用,值改变了)

// CommonJS - 值的拷贝
// counter.js
let count = 0;
function increment() {
  count++;
}
module.exports = { count, increment };

// main.js
const { count, increment } = require('./counter.js');
console.log(count); // 0
increment();
console.log(count); // 0(拷贝,值不变)

10. 动态导入与代码分割

javascript 复制代码
// 1. 动态import
button.addEventListener('click', async () => {
  const module = await import('./heavy-module.js');
  module.doSomething();
});

// 2. 条件导入
async function loadModule(type) {
  if (type === 'admin') {
    const admin = await import('./admin.js');
    return admin.default;
  } else {
    const user = await import('./user.js');
    return user.default;
  }
}

// 3. React路由懒加载
import React, { lazy, Suspense } from 'react';

const AdminPanel = lazy(() => import('./AdminPanel'));

function App() {
  return (
    <Suspense fallback={<div>Loading...</div>}>
      <AdminPanel />
    </Suspense>
  );
}

// 4. Webpack代码分割
import(/* webpackChunkName: "lodash" */ 'lodash').then(({ default: _ }) => {
  console.log(_.chunk([1, 2, 3, 4], 2));
});
相关推荐
前端那点事7 小时前
Vue nextTick 超全解析|作用、使用场景、底层原理、Vue2/Vue3区别
前端·vue.js
前端那点事7 小时前
Vue面试高频:子组件能直接修改父组件数据吗?单向数据流原理+正确写法全覆盖
前端·vue.js
前端那点事7 小时前
为什么 Vue 的 template 标签不能用 v-show?底层机制+踩坑复盘+生产级解决方案
前端·vue.js
weelinking8 小时前
【claude】14_Claude作为技术文档助手
前端·人工智能·react.js·数据挖掘·前端框架
jiayong238 小时前
前端面试题库 - JavaScript核心基础篇
前端·javascript·面试
JAVA面经实录9178 小时前
Java多线程并发高频面试100题(完整版·含答案·背诵版)
java·开发语言·面试
软件技术NINI8 小时前
泉州html+css 4页
前端·javascript·css·html
再吃一根胡萝卜8 小时前
OpenScreen:免费开源的录屏神器,做出专业级演示视频
前端