面试题目汇总

为了脱敏已将公司名称移除。

公司一

一面

  1. 介绍一下自己,讲一下最近一份工作中的印象比较深的难点以及如何解决的?
  2. Vue2 与 Vue3 的区别;
  • diff 算法
  • 响应追踪
  • 组合式 api 与选项 api
  • ...
  1. 为什么要用虚拟 dom?
  • 进行 diff 更加方便;
  • 可缓存 dom 节点;
  • 跨平台更加方便,可以自定义渲染器,抹平了平台之间的差异;
  • ...
  1. 实现一个类似于微信抢红包的函数,保证每个人一定可以抢到红包并且他们的金额差距在一个范围内不会过大。
  • 我这边的实现如下:

    function fn(money: number, count: number) {
      const res: number[] = [];
      for (let i = 0; i < count; i++) {
        // 获取当前的平均值
        const avg = Number((money / (count - i)).toFixed(2));
        // 两个不同的红包的差值
        const val = Number((Math.random() * 0.5).toFixed(2));
        // 当前的红包金额
        let cur = Math.random() > 0.5 ? avg + val : avg - val;
        // 最后一个时直接把全部红包发放给用户
        if (i === count - 1) {
          res.push(Number(money.toFixed(2)));
        } else {
          // 其他时候把当前的金额发放给用户,总金额减少当前的值
          res.push(Number(cur.toFixed(2)));
          money -= cur;
        }
      }
      // 最后返回红包金额数组
      return res;
    }
    
    • 其中,我的实现最初是用的总金额的平均值,这种情况下需要判断会不会发放超额,在面试官的引导下,我修改为当前的金额值,且对 money 做了一次money -= cur的操作,我之前是没有对 money 做操作的,而是循环一次取一次发放总金额然后用总金额减去发放金额,还是感谢面试官的提醒加引导!
  • 最后就是反向提问环节。

二面

  1. 介绍一下自己,讲一下最近一份工作中的印象比较深的难点以及如何解决的?
  2. 计算机网络部分
  • 比如 304、什么时候会命中缓存?
  • option 请求什么时候发送?
  • 协商缓存和强缓存
  1. 给定一个字符串数组list,如何查找出其中以某字符串开头的值的集合?
const list = ['goods', 'goodsoods', 'good'];
function search(list: string[], keyword: string) {
  return list.filter(item => item.startWith(keyword));
}
  1. 询问了如何增强上述查询的效率:
  • 我回答了把其中的数据存储为哈希表的格式:
const list = {
  goods: ['goods', 'goodsoods'],
  good: ['goods', 'goodsoods', 'good'],
};
  • 面试官说这个可以,但是太耗费内存了,让我想一下有没有其他的方式。

    • 最后在面试官的提示下有了如下数据结构:
    const list = {
      g: {
        end: 'g',
        o: {
          end: 'go',
          o: {
            end: 'goo',
            d: {
              end: 'good',
              s: {
                end: 'goods',
              },
            },
          },
        },
      },
    };
    
  1. 然后就是让我把最开始的list转换成上述树结构。
  • 给的时间比较少当时确实没有写出来。
  • 面试结束后,我觉得自己凉了,然后就自己静下心来重新写了下这个题目:
function list2tree(list: string[]) {
  /** 字符串转为树 */
  function str2tree(str: string) {
    const res = {};
    let curObj = res;
    for (let i = 0; i < str.length; i++) {
      const char = str.charAt(i);
      curObj[char] = {
        end: str.slice(0, i + 1),
      };
      curObj = curObj[char];
    }
    curObj.end = str;
    return res;
  }
  /** 深层次合并两个对象 */
  function mergeObj(o1: object, o2: object) {
    const obj = {};
    for (const key of Object.keys(o2)) {
      if (isObj(o1[key]) && isObj(o2[key])) {
        obj[key] = mergeObj(o1[key], o2[key]);
      } else {
        obj[key] = o2[key];
      }
    }

    for (const key of Object.keys(o1)) {
      if (!o2.hasOwnProperty(key)) {
        obj[key] = o1[key];
      }
    }
    return obj;
  }
  /** 判断是否是一个对象 */
  function isObj(o: any): o is object {
    return typeof o === 'object' && o !== null;
  }
  /** 先把每个字符串都转成对象,然后合并数组中的对象 */
  return list.map(str2tree).reduce(mergeObj, {});
}
  1. 最后说一句,我以为自己凉凉了,结果最后面试官还让我过了。

三面

  1. 一开始还是老生常谈的问题,介绍一下自己,讲一下最近一份工作中的印象比较深的难点以及如何解决的?
  2. 做一道算法题,题目内容是有一个长度为n+1的数组,从里面随机取n次,填充满一个长度为n的数组,求最后剩余的数字是什么?
  • 这道题没让我写,只是让我说一下自己的思路。
  • 很明显,这道题只需要遍历一次每次随机生成一个索引,然后把该索引的值从n+1的数组中删除即可,循环n次后剩下的最后一个元素就是最后的值了。如果需要放到另一个数组中,那么就删除这个索引的时候,在另一个数组存一下这个值即可,最后原数组剩下的元素就是那个值了。
  1. 然后问了这些后面试就草草的结束了,不知道能不能过。

公司二

需要针对性的看待面试公司的技术栈然后重点关注下该技术栈,例如该公司使用 Vue 技术栈,那么我们自然就不要往 React 上多说 。

一面

  1. 介绍下自己,简单介绍下的兴趣爱好是什么?
  • 我说自己比较喜欢羽毛球和足球,不过不会踢足球只喜欢看。
  • 平时读读书,比如红宝书(JavaScript 高级程序设计)也读了几遍等等
  1. 最近的技术中用到的技术栈是什么?
  • Vue3 与 React
  1. Vue2 与 Vue3 的区别是什么?
  • 响应式原理不同
    • Vue2 使用Object.defineProperty,Vue3 使用Proxy,与之相比 Vue3 性能更加优秀,不需要再重写数组上的方法等。
  • diff 算法不同
    • Vue2 使用双端 diff 中间部分无法 diff
    • Vue3 使用双端 diff,中间部分使用最长递增子序列原理进行 diff
  • composition Api 与 options Api
  1. 手写 Vue3 的 diff 算法核心逻辑即最长递增子序列部分(贪心 + 二分)
  2. 一道 Promise 的执行顺序打印输出题目
  3. let、var、function 的初始化与暂时性死区
  4. 设计一个监控报警系统有哪些东西需要考虑
  5. 为什么要用虚拟 Dom,虚拟 Dom 真的快吗?
  • 先下结论,虚拟 Dom 的速度远不及直接操作 Dom 的命令式调用性能好。
    • 从 Jquery 的命令式操作 Dom 到现在的各大框架声明式是一个趋势,我们要知道哪怕我们现在是声明式的操作方式框架底层做的仍然是命令式的调用。所以该操作的 Dom 是少不了操作的,倘若我们可以使用最少的操作次数命令式的操作 Dom 那么此时的性能是最好的,但是此时我们的心智负担非常大,且项目不利于维护。
    • 使用声明式的操作方式可以减小我们的心智负担,此时真正操作 Dom 的时机框架帮我们做了,增强了项目的可维护性。
    • 如果一个框架能减小我们开发时的心智负担,又能提供还不错的性能,那么我们没有理由不去使用该方式。
  • 虚拟 Dom 可以将我们的操作真是 Dom 的映射到一个数据结构上,我们只需要操作该数据结构即可让框架帮我们操作真实的 Dom,同时又能保证还不错的性能。
  • 虚拟 Dom 中的 diff 比起真实 Dom 更加方便。
  • 虚拟 Dom 可以来做跨平台,比如 Dom 只在浏览器中存在,但是其他的环境中没有 Dom 我们此时可以使用虚拟 Dom 来自定义渲染器从而自定义在该平台上如何做渲染。
  1. 本来都结束了,临结束面试官又问了个红宝书问题,算是自己埋下的坑,还好自己答出来了。问题是:两个值做加法与减法时底层做了什么?然后问了几个打印的结果,还好我确实这块有印象全部答对了。
  • 一面就这样愉快的结束了,最后就是反问环节了。

二面

  1. 还是老生常谈的介绍下自己等等一系列问题
  2. 对我说了下一面的面试官对我印象非常好,然后让我介绍了下项目以及用到的技术点
  • 简单介绍了 LRU Cache
  • 时间分片
  • 数据预取
  • 懒加载以及预加载
  • 资源的压缩以及不同图的使用场景(webp、png、jpg、base64 等等)
  • 包体积分隔 chunk
  • 使用 cdn
  1. 出了道题目算是被我钻了空子,面试官题目为,随机生成不同的整数放置到数组中直到数组被填满
  • 我这边给到了几个答案:

    • 方案一:

      function generateUniqueRandomNumbers() {
        let nums = Array.from({ length: 100 }, (_, i) => i + 1); // 创建一个包含1到100的数组
        let result = [];
      
        for (let i = 0; i < 10; i++) {
          let randomIndex = Math.floor(Math.random() * nums.length); // 随机选择一个索引
          result.push(nums[randomIndex]); // 将选择的数字添加到结果数组中
          nums.splice(randomIndex, 1); // 从原始数组中移除选择的数字
        }
        return result;
      }
      

      因为都是整数,所以通过索引取出来的一定都是不重复的,这样可以防止多次随机不到一个数导致额外的时间消耗,算是比较钻空子的方案。

    • 方案二:

      const set = new Set();
      while (set.size < 10) {
        set.add(Math.floor(Math.random() * 10));
      }
      const ret = [..set]
      

      方案二使用集合这个方法算是太简洁了

    • 方案三:

      const ret = [];
      while (ret.length < 10) {
        const randomNum = Math.floor(Math.random() * 10);
        !ret.includes(randomNum) && ret.push(randomNum);
      }
      

      方案三与方案二本质上没区别,只是将集合改用了数组而已。

      • 上述三种方法各有优劣,方案一中可以保证最大的循环次数就是我们生成的数量,一定不会超过这个数量的循环;方案二代码简洁,但是如果生成的随机数一直都是不符合要求的重复数则该循环就会无休止的运行,当然该 badcase 非常极端,但是是有这种可能性的,最少循环 10 次,最大就是无休止的运行了。
  1. 介绍下 http 状态码
  • 301 与 302 有啥区别以及使用场景等等
  • 状态码微 304 时还会往服务端发请求吗
  1. 问我说还有什么面试官没问到我可以主动说

三面

  1. 还是简单的介绍下自己
  2. 讲一下自己的项目中做的事情以及用到的技术栈
  3. 跟面试官侃侃而谈,没有问具体算法
  4. 最后是问有没有什么想反问的。

四面

  1. 四面基本就是跟 HR 谈了,可能会谈到为什么离职,当前薪资待遇等等
  2. 自己比起其他人来更加突出的点或者不足点是什么
  • 我们不是圣人,不可能没有不足点,当然也不要吹嘘自己多牛多牛,自己的优点讲实话就 ok 了;至于自己的不足点这块就不要说技术上的了,因为我们面的是技术岗,都已经走到了四面了我们如果再说技术有什么问题完全就是给自己挖坑,也许人家都想要你了你一句自己哪哪有问题也许人家会重新评估是否要你。
  1. 一般此时不是要给我们发 offer 的,所以我们不要急着去谈薪,即使我们不谈 hr 也会主动找我们谈,我们主动谈的话反而显得唯利是图了。
  • 不要一上来说自己最少要多少钱,要问清楚薪资架构后再做决策
    • 社保公积金缴纳基数与比例
    • 是否有年终奖
    • 是否双休
    • 加班如何计算
    • 是否有食堂或是餐补
    • 交通补助、加班有无打车补助、打车补助时间段是什么
    • 年假等法定节假日
  1. hr 问有无其他 offer 可以说一个跟当前公司差不多的公司的 offer,什么时候入职不要很快的入职,可以说月内入职。
    • 没谈好薪资就说很快的话 hr 会觉得你急需找工作会压价;同理有个其他的 offer 自己的竞争力也会变强。
  2. 看自己的面试情况再谈薪资,面的好就有点底,免得不好就少要点。
Last Updated:
Contributors: zhaoyuqiqi