欢迎光临
我们一直在努力

前端高频速记(数组去重-深拷贝-this-Promise-事件循环-快排)

前端高频:数组去重、深拷贝、this、Promise、事件循环、算法等(速记版)

汇总 10 个常见前端面试高频点:数组去重、深拷贝、this、Promise 封装 Ajax、箭头函数、事件循环、=== vs Object.is、Promise、递归、快速排序。


1. 数组去重的方式都有哪些?

数组去重的本质:识别重复元素并剔除。

常见方法对比

方法代码示例特点
Set 去重(最简) const newArr = […new Set(arr)]; ES6 简洁高效;对引用类型(对象/数组)按引用地址去重(通常不符合“内容相同视为重复”的需求)
includes / indexOf 遍历 arr.forEach(x => !res.includes(x) && res.push(x)) 直观、兼容性好(ES5+ includes 需注意兼容);时间复杂度一般 O(n²)
filter + indexOf arr.filter((x,i)=>arr.indexOf(x)===i) 利用“首次出现位置”等于当前索引;简洁但仍可能 O(n²)
对象键名去重 obj[item]=true 可自定义规则;需处理类型(1 vs '1')/ key 冲突问题
reduce 去重 arr.reduce((pre,cur)=>pre.includes(cur)?pre:[…pre,cur],[]) 函数式写法;注意性能(扩展运算符拷贝成本)

引用类型(对象数组)去重补充

对象数组通常按 唯一标识 去重(推荐),例如 id:

const uniqById = (arr) => {
const map = new Map();
arr.forEach(item => map.set(item.id, item)); // 后者覆盖前者
return […map.values()];
};

或者序列化(不推荐通用):JSON.stringify 可能受 key 顺序/循环引用影响。


2. 手写深拷贝的逻辑,讲述一下?

深拷贝目标:复制所有层级,新对象与原对象完全独立。

核心步骤

  • 判断类型:基本类型 / null 直接返回
  • 处理特殊对象:Date、RegExp 等
  • 创建新容器:数组 [] / 对象 {}
  • 递归遍历自身属性:对每个 key 递归 deepClone
  • (进阶)循环引用:用 WeakMap 记忆化缓存
  • 基础版代码

    function deepClone(obj) {
    // 1. 基本类型 / null
    if (obj === null || typeof obj !== 'object') return obj;

    // 2. 特殊引用类型
    if (obj instanceof Date) return new Date(obj);
    if (obj instanceof RegExp) return new RegExp(obj);

    // 3. 创建容器
    const newObj = Array.isArray(obj) ? [] : {};

    // 4. 递归拷贝自身属性
    for (let key in obj) {
    if (Object.prototype.hasOwnProperty.call(obj, key)) {
    newObj[key] = deepClone(obj[key]);
    }
    }

    return newObj;
    }

    进阶:处理循环引用(WeakMap)

    function deepClone(obj, cache = new WeakMap()) {
    if (obj === null || typeof obj !== 'object') return obj;

    if (obj instanceof Date) return new Date(obj);
    if (obj instanceof RegExp) return new RegExp(obj);

    if (cache.has(obj)) return cache.get(obj);

    const newObj = Array.isArray(obj) ? [] : {};
    cache.set(obj, newObj);

    for (let key in obj) {
    if (Object.prototype.hasOwnProperty.call(obj, key)) {
    newObj[key] = deepClone(obj[key], cache);
    }
    }

    return newObj;
    }


    3. this 指向的理解?

    this 是函数执行时的上下文对象,由调用方式决定(不是定义位置)。

    规则速查

    调用场景this 指向示例
    普通函数调用 非严格:全局对象;严格:undefined fn()
    对象方法调用 调用它的对象 obj.fn()
    构造函数 new 新实例对象 new Person()
    call/apply/bind 显式指定对象 fn.call(x)
    箭头函数 继承外层 this () => this
    DOM 事件处理 触发事件的元素(传统绑定) btn.onclick = function(){}

    口诀:谁调用,this 指向谁;箭头函数除外(它不绑定自己的 this)。


    4. 如何使用 Promise 封装原生 Ajax(XHR)?

    把回调式 XHR 封装成 Promise:成功 resolve,失败 reject,便于链式调用。

    function ajax(options) {
    const {
    url = '',
    method = 'GET',
    data = {},
    headers = { 'Content-Type': 'application/json' }
    } = options;

    return new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest();

    const upperMethod = method.toUpperCase();

    let finalUrl = url;
    let sendData = null;

    // GET 拼参
    if (upperMethod === 'GET') {
    const params = new URLSearchParams(data).toString();
    if (params) finalUrl += (finalUrl.includes('?') ? '&' : '?') + params;
    } else {
    sendData = JSON.stringify(data);
    }

    xhr.open(upperMethod, finalUrl, true);

    // 设置请求头
    Object.keys(headers).forEach(k => xhr.setRequestHeader(k, headers[k]));

    xhr.onload = function () {
    if (xhr.status >= 200 && xhr.status < 300) {
    let res = xhr.responseText;
    try { res = JSON.parse(res); } catch (e) {}
    resolve({ data: res, status: xhr.status });
    } else {
    reject(new Error(`请求失败:${xhr.status}`));
    }
    };

    xhr.onerror = function () {
    reject(new Error('网络请求失败'));
    };

    xhr.send(sendData);
    });
    }


    5. 箭头函数和普通函数的区别?

    核心差异集中在:this / arguments / 构造能力 / prototype。

    特性箭头函数普通函数
    this 继承外层(不可被 call/bind 改变) 由调用方式决定
    构造函数 不能 new 可以 new
    arguments 没有自己的 arguments 有自己的 arguments
    prototype 没有 prototype 有 prototype
    写法 简洁,可隐式返回 相对冗长
    yield 不能用(不能作 generator) 可以作 generator

    6. 浏览器环境中事件循环(Event Loop)的理解?

    浏览器单线程通过事件循环协调同步与异步任务。

    关键概念

    • 执行栈:同步代码执行位置
    • 任务队列:
      • 宏任务:script、setTimeout、setInterval、Ajax、DOM 事件、requestAnimationFrame 等
      • 微任务:Promise.then/catch/finally、MutationObserver、queueMicrotask、await 后续等

    执行顺序(简化)

  • 执行当前宏任务(同步代码)
  • 执行完后,清空所有微任务
  • 进行一次渲染(浏览器可能在此时机更新)
  • 取出一个宏任务执行
  • 循环往复
  • 示例:

    console.log('1(同步)');

    setTimeout(() => console.log('2(宏任务)'), 0);

    Promise.resolve().then(() => console.log('3(微任务)'));

    console.log('4(同步)');

    // 1 -> 4 -> 3 -> 2


    7. === 和 Object.is() 的区别?

    两者都用于“严格相等”,但对 NaN 与 +0/-0 的处理不同。

    场景===Object.is()
    NaN NaN === NaN 为 false true
    +0 vs -0 true false
    其它多数情况 一致 一致

    Object.is(NaN, NaN); // true
    Object.is(+0, –0); // false


    8. Promise 的理解?

    Promise 是 ES6 的异步解决方案,本质是:异步操作状态机 + 链式调用机制。

    状态(不可逆)

    • pending → fulfilled(成功)或 rejected(失败)

    特点

    • then/catch/finally 返回新 Promise,可链式调用
    • 错误可“穿透”到最近的 catch

    常用静态方法

    • Promise.all:全成功才成功;一个失败就失败
    • Promise.race:最先完成的决定结果
    • Promise.allSettled:全部结束(无论成功失败),返回每个结果
    • Promise.resolve/reject:快速创建 Promise

    9. 递归函数的理解?

    递归:函数调用自身。核心是:

    • 递归条件:把问题拆成更小问题
    • 终止条件:必须有出口,否则栈溢出

    阶乘示例:

    function factorial(n) {
    if (n === 0) return 1;
    return n * factorial(n – 1);
    }

    常见场景:树/DOM 遍历、分治算法(快排/归并)、数学问题。


    10. 快速排序(Quick Sort)的逻辑是什么?

    快排核心:分治(选基准 pivot → 分区 → 递归)。

    思路

  • 选 pivot
  • 小于 pivot 放左边,大于等于放右边
  • 递归排序左右两边并合并
  • 基础实现(非原地,易理解):

    function quickSort(arr) {
    if (arr.length <= 1) return arr;

    const pivot = arr[0];
    const left = [];
    const right = [];

    for (let i = 1; i < arr.length; i++) {
    if (arr[i] < pivot) left.push(arr[i]);
    else right.push(arr[i]);
    }

    return […quickSort(left), pivot, …quickSort(right)];
    }

    补充:工程中会做 pivot 优化(如三数取中)+ 原地 partition 降低空间开销,避免有序数组导致最坏 O(n^2)。


    总结速记

    • 去重:Set 最快最简;对象数组用 Map + id 最稳
    • 深拷贝:类型判断 → 新容器 → 递归;循环引用用 WeakMap
    • this:由调用方式决定;箭头函数继承外层 this
    • Promise 封装 XHR:Promise 包一层,成功 resolve,失败 reject
    • 事件循环:同步 → 清空微任务 →(渲染)→ 取一个宏任务 → 循环
    • Object.is:修复 === 对 NaN、+0/-0 的缺陷
    • 快排:选基准、分区、递归(分治思想)
    赞(0)
    未经允许不得转载:171主机测评 » 前端高频速记(数组去重-深拷贝-this-Promise-事件循环-快排)
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址