JavaScript面试题精讲:从基础到高级的全面覆盖

JavaScript面试题精讲:从基础到高级的全面覆盖

【免费下载链接】FE-Interview 🔥🔥🔥 前端面试,独有前端面试题详解,前端面试刷题必备,1000+前端面试真题,Html、Css、JavaScript、Vue、React、Node、TypeScript、Webpack、算法、网络与安全、浏览器 【免费下载链接】FE-Interview 项目地址: https://gitcode.com/gh_mirrors/fei/FE-Interview

本文深入解析JavaScript从ES6+新特性到核心概念的全面知识体系,涵盖块级作用域、箭头函数、Promise异步编程、async/await、模块化系统等现代JavaScript核心特性,同时深度剖析原型链与闭包机制,并提供手写代码的实现技巧和实战示例。内容从基础到高级全面覆盖,帮助开发者系统掌握JavaScript面试必备知识点,提升编程能力和面试通过率。

ES6+新特性深度解析

随着JavaScript语言的快速发展,ES6(ECMAScript 2015)及后续版本带来了革命性的新特性,这些特性不仅提升了开发效率,更从根本上改变了JavaScript的编程范式。本文将深入解析ES6+的核心特性,帮助开发者全面掌握现代JavaScript开发的核心技能。

块级作用域:let与const的革命

ES6引入的letconst关键字彻底改变了JavaScript的作用域机制,解决了var声明带来的变量提升和全局污染问题。

// 传统的var声明存在的问题
function varExample() {
    console.log(i); // undefined,变量提升
    for (var i = 0; i < 3; i++) {
        setTimeout(() => console.log(i), 100); // 输出3次3
    }
}

// 使用let的块级作用域
function letExample() {
    for (let i = 0; i < 3; i++) {
        setTimeout(() => console.log(i), 100); // 输出0,1,2
    }
}

// const声明常量
const PI = 3.14159;
// PI = 3.14; // TypeError: Assignment to constant variable

// const对于对象和数组
const obj = { name: 'John' };
obj.name = 'Jane'; // 允许修改属性
obj = {}; // TypeError: Assignment to constant variable

作用域对比表:

特性varletconst
作用域函数作用域块级作用域块级作用域
变量提升否(暂时性死区)否(暂时性死区)
重复声明允许不允许不允许
初始值要求可选可选必须
重新赋值允许允许不允许

箭头函数:简洁的语法与this绑定

箭头函数不仅提供了更简洁的语法,更重要的是解决了传统函数中this指向的困惑问题。

// 传统函数
const numbers = [1, 2, 3, 4, 5];
const squares = numbers.map(function(n) {
    return n * n;
});

// 箭头函数简化
const squaresArrow = numbers.map(n => n * n);

// this绑定差异
const obj = {
    name: 'Object',
    traditionalFunction: function() {
        console.log(this.name); // Object
    },
    arrowFunction: () => {
        console.log(this.name); // undefined(指向外层作用域)
    }
};

// 多层嵌套的简洁性
const complexOperation = data => 
    data
        .filter(item => item.active)
        .map(item => ({
            ...item,
            fullName: `${item.firstName} ${item.lastName}`
        }))
        .sort((a, b) => a.age - b.age);

箭头函数特性总结:

  • 没有自己的this,继承外层函数的this
  • 不能用作构造函数,不能使用new关键字
  • 没有arguments对象,可以使用rest参数替代
  • 不能使用yield关键字,不能用作Generator函数

Promise与异步编程革命

Promise对象为JavaScript异步编程提供了标准化的解决方案,解决了回调地狱问题。

// 回调地狱示例
function callbackHell() {
    getUser(userId, function(user) {
        getPosts(user.id, function(posts) {
            getComments(posts[0].id, function(comments) {
                console.log(comments);
            });
        });
    });
}

// Promise链式调用
function promiseChain() {
    getUser(userId)
        .then(user => getPosts(user.id))
        .then(posts => getComments(posts[0].id))
        .then(comments => console.log(comments))
        .catch(error => console.error('Error:', error));
}

// Promise静态方法
const promises = [
    fetch('/api/users'),
    fetch('/api/posts'),
    fetch('/api/comments')
];

Promise.all(promises)
    .then(responses => Promise.all(responses.map(r => r.json())))
    .then(data => {
        const [users, posts, comments] = data;
        console.log('All data loaded:', { users, posts, comments });
    });

Promise.race(promises)
    .then(response => console.log('First response:', response));

Promise状态转换流程图:

mermaid

async/await:同步风格的异步编程

async/await基于Promise构建,让异步代码看起来像同步代码一样直观。

// 传统Promise写法
function fetchData() {
    return fetch('/api/data')
        .then(response => response.json())
        .then(data => processData(data))
        .catch(error => handleError(error));
}

// async/await写法
async function fetchDataAsync() {
    try {
        const response = await fetch('/api/data');
        const data = await response.json();
        const processedData = await processData(data);
        return processedData;
    } catch (error) {
        handleError(error);
    }
}

// 并行请求优化
async function fetchMultipleData() {
    const [userData, postData, commentData] = 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 { userData, postData, commentData };
}

// 错误处理模式
async function robustFetch() {
    try {
        const result = await someAsyncOperation();
        return { success: true, data: result };
    } catch (error) {
        console.error('Operation failed:', error);
        return { success: false, error: error.message };
    }
}

模块化系统:import与export

ES6模块系统提供了官方的模块化解决方案,取代了之前的CommonJS和AMD等方案。

// math.js - 命名导出
export const PI = 3.14159;
export function square(x) {
    return x * x;
}
export function cube(x) {
    return x * x * x;
}

// utils.js - 默认导出
export default function formatDate(date) {
    return new Intl.DateTimeFormat().format(date);
}

// app.js - 导入使用
import { PI, square, cube } from './math.js';
import formatDate from './utils.js';
import * as math from './math.js'; // 命名空间导入

console.log(PI); // 3.14159
console.log(square(5)); // 25
console.log(math.cube(3)); // 27
console.log(formatDate(new Date()));

模块加载时序图:

mermaid

解构赋值:简洁的数据提取

解构赋值提供了一种从数组或对象中提取数据的简洁语法。

// 数组解构
const numbers = [1, 2, 3, 4, 5];
const [first, second, ...rest] = numbers;
console.log(first, second, rest); // 1, 2, [3, 4, 5]

// 对象解构
const user = {
    id: 1,
    name: 'John Doe',
    email: 'john@example.com',
    address: {
        city: 'New York',
        country: 'USA'
    }
};

const { name, email, address: { city } } = user;
console.log(name, email, city); // John Doe, john@example.com, New York

// 函数参数解构
function printUser({ name, email, age = 25 }) {
    console.log(`${name} (${email}), ${age} years old`);
}

printUser(user);

// 交换变量
let a = 1, b = 2;
[a, b] = [b, a];
console.log(a, b); // 2, 1

模板字符串:强大的字符串处理

模板字符串提供了多行字符串、字符串插值和标签模板等功能。

// 多行字符串
const multiLine = `
    This is a
    multi-line
    string
`;

// 字符串插值
const name = 'John';
const age = 30;
const greeting = `Hello, my name is ${name} and I'm ${age} years old.`;

// 表达式计算
const price = 19.99;
const quantity = 3;
const total = `Total: $${(price * quantity).toFixed(2)}`;

// 标签模板
function highlight(strings, ...values) {
    return strings.reduce((result, str, i) => {
        const value = values[i] ? `<strong>${values[i]}</strong>` : '';
        return result + str + value;
    }, '');
}

const message = highlight`Hello ${name}, welcome to our ${'website'}!`;
console.log(message); // Hello <strong>John</strong>, welcome to our <strong>website</strong>!

扩展运算符与rest参数

扩展运算符和rest参数提供了处理函数参数和数组/对象的强大工具。

// 数组扩展
const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
const combined = [...arr1, ...arr2]; // [1, 2, 3, 4, 5, 6]

// 对象扩展
const obj1 = { a: 1, b: 2 };
const obj2 = { c: 3, d: 4 };
const merged = { ...obj1, ...obj2 }; // { a: 1, b: 2, c: 3, d: 4 }

// 函数参数处理
function sum(...numbers) {
    return numbers.reduce((total, num) => total + num, 0);
}

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

// 解构配合rest参数
const [first, second, ...others] = [1, 2, 3, 4, 5];
console.log(first, second, others); // 1, 2, [3, 4, 5]

// 浅拷贝应用
const original = { x: 1, y: 2 };
const copy = { ...original };

增强的对象字面量

ES6为对象字面量提供了更简洁的语法和新的功能。

// 属性简写
const name = 'John';
const age = 30;
const person = { name, age }; // { name: 'John', age: 30 }

// 方法简写
const calculator = {
    add(a, b) {
        return a + b;
    },
    multiply(a, b) {
        return a * b;
    }
};

// 计算属性名
const propName = 'status';
const dynamicObj = {
    [propName]: 'active',
    ['user_' + Math.random()]: 'randomUser'
};

// Object.assign与扩展运算符对比
const defaults = { theme: 'light', notifications: true };
const userSettings = { theme: 'dark' };

// 使用Object.assign
const settings1 = Object.assign({}, defaults, userSettings);

// 使用扩展运算符
const settings2 = { ...defaults, ...userSettings };

Map、Set、WeakMap、WeakSet

ES6引入了新的数据结构,提供了比传统对象和数组更专业的解决方案。

// Map vs Object
const map = new Map();
map.set('name', 'John');
map.set(42, 'The Answer');
map.set({}, 'Object key');

console.log(map.get('name')); // John
console.log(map.size); // 3

// Set去重
const numbers = [1, 2, 2, 3, 4, 4, 5];
const uniqueNumbers = [...new Set(numbers)]; // [1, 2, 3, 4, 5]

// WeakMap用于私有数据
const privateData = new WeakMap();

class Person {
    constructor(name) {
        privateData.set(this, { name });
    }
    
    getName() {
        return privateData.get(this).name;
    }
}

// 不同数据结构的特性对比

数据结构选择指南:

需求场景推荐数据结构原因
键值对存储Map键可以是任意类型,有序迭代
唯一值集合Set自动去重,高效的存在性检查
对象私有数据WeakMap不影响垃圾回收,键必须是对象
简单配置对象Object字面量语法简洁,JSON兼容
频繁增删操作Map/Set性能优于数组的splice操作

Proxy与Reflect:元编程的强大工具

Proxy和Reflect提供了对对象基本操作的拦截和自定义能力。

// Proxy示例:数据验证
const validator = {
    set(target, property, value) {
        if (property === 'age') {
            if (typeof value !== 'number' || value < 0) {
                throw new TypeError('Age must be a positive number');
            }
        }
        return Reflect.set(target, property, value);
    }
};

const person = new Proxy({}, validator);
person.age = 25; // 成功
// person.age = -5; // TypeError: Age must be a positive number

// Proxy实现负索引数组
function createNegativeIndexArray(array) {
    return new Proxy(array, {
        get(target, index) {
            const idx = parseInt(index);
            if (idx < 0) {
                return target[target.length + idx];
            }
            return target[idx];
        }
    });
}

const arr = createNegativeIndexArray([1, 2, 3, 4, 5]);
console.log(arr[-1]); // 5
console.log(arr[-2]); // 4

// Reflect的使用
const obj = { a: 1 };
console.log(Reflect.get(obj, 'a')); // 1
Reflect.set(obj, 'b', 2);
console.log(obj.b); // 2

## 异步编程与Promise实战

在现代JavaScript开发中,异步编程是不可或缺的核心技能。从早期的回调地狱到如今的Promise和async/await,JavaScript的异步处理方式经历了革命性的演进。掌握Promise不仅能让代码更加优雅,更是前端工程师面试中的必考知识点。

### Promise基础概念与三种状态

Promise是JavaScript中处理异步操作的标准化方案,它代表一个异步操作的最终完成(或失败)及其结果值。每个Promise对象都有三种可能的状态:

| 状态 | 描述 | 特性 |
|------|------|------|
| **pending** | 初始状态,既不是成功也不是失败 | 可转换为fulfilled或rejected |
| **fulfilled** | 操作成功完成 | 具有不可变的终值 |
| **rejected** | 操作失败 | 具有不可变的拒因 |

```javascript
// Promise基本用法示例
const promise = new Promise((resolve, reject) => {
  // 异步操作
  setTimeout(() => {
    const success = Math.random() > 0.5;
    if (success) {
      resolve('操作成功!');
    } else {
      reject(new Error('操作失败!'));
    }
  }, 1000);
});

promise
  .then(result => console.log(result))
  .catch(error => console.error(error));

Promise链式调用的魔力

Promise的真正威力在于其链式调用能力,这彻底解决了回调地狱的问题:

function asyncOperation1() {
  return new Promise(resolve => setTimeout(() => resolve(10), 1000));
}

function asyncOperation2(value) {
  return new Promise(resolve => setTimeout(() => resolve(value * 2), 500));
}

function asyncOperation3(value) {
  return new Promise(resolve => setTimeout(() => resolve(value + 5), 300));
}

// 链式调用示例
asyncOperation1()
  .then(result1 => {
    console.log('第一步结果:', result1);
    return asyncOperation2(result1);
  })
  .then(result2 => {
    console.log('第二步结果:', result2);
    return asyncOperation3(result2);
  })
  .then(finalResult => {
    console.log('最终结果:', finalResult);
  })
  .catch(error => console.error('错误:', error));

Promise常用静态方法详解

Promise.all - 并行处理多个异步操作
const promises = [
  fetch('/api/user/1'),
  fetch('/api/user/2'), 
  fetch('/api/user/3')
];

Promise.all(promises)
  .then(responses => Promise.all(responses.map(r => r.json())))
  .then(users => {
    console.log('所有用户数据:', users);
  })
  .catch(error => {
    console.error('有一个请求失败:', error);
  });
Promise.race - 竞速模式
const timeoutPromise = new Promise((_, reject) => 
  setTimeout(() => reject(new Error('请求超时')), 5000)
);

const apiPromise = fetch('/api/data');

Promise.race([apiPromise, timeoutPromise])
  .then(response => response.json())
  .then(data => console.log('数据:', data))
  .catch(error => console.error('错误:', error));
Promise.allSettled - 等待所有Promise完成
const promises = [
  Promise.resolve('成功1'),
  Promise.reject(new Error('失败1')),
  Promise.resolve('成功2')
];

Promise.allSettled(promises)
  .then(results => {
    results.forEach((result, index) => {
      if (result.status === 'fulfilled') {
        console.log(`Promise ${index}:`, result.value);
      }

【免费下载链接】FE-Interview 🔥🔥🔥 前端面试,独有前端面试题详解,前端面试刷题必备,1000+前端面试真题,Html、Css、JavaScript、Vue、React、Node、TypeScript、Webpack、算法、网络与安全、浏览器 【免费下载链接】FE-Interview 项目地址: https://gitcode.com/gh_mirrors/fei/FE-Interview

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值