最近前端面试的一些代码题

最近面了一些大小厂,虽然结果不尽人意,但还是想写篇总结发出来回馈一下社区,毕竟之前也看了很多大佬的面经分享,对我还是很有帮助。

某哈游-协同文档

深度克隆

javascript 复制代码
function cloneDeep(value) {
  const map = new Map();

  function _cloneDeep(value) {
    const isObject = typeof value === 'object' && value !== null;

    if (!isObject) return value;

    if (map.has(value)) {
      return map.get(value);
    }

    const clone = Array.isArray(value) ? [] : {};
    for (const [key, v] of value.entries()) {
      clone[key] = _cloneDeep(v);
    }
    map.set(value, clone);
    return clone;
  }

  return _cloneDeep(value);
}

点评

直接用 structuredClone

JSON.parse(JSON.stringify(value)) 的问题:

  • JSON.stringify 不支持循环引用, 字符串没法表示无限递归
  • JSON.stringify 不能处理 JSON 支持的类型以外的类型,JSON 只支持 object, array, number, string, boolean, null。例如 Date, Set, Map
  • 性能不如 structuredClone

字节跳动-恰饭平台

实现 transform 函数:

javascript 复制代码
transform({
  'A': 1,
  'B.C': 2,
  'B.D.E': 3,
  'CC.DD.EE': 4,
});

得到:

javascript 复制代码
const result = {
  A: 1,
  B: {
    C: 2,
    D: {
      E: 3,
    },
  },
  CC: {
    DD: {
      EE: 4,
    },
  },
};

实现:

javascript 复制代码
function set(obj, keyPath, value) {
  let i = 0;
  while (i < keyPath.length - 1) {
    const key = keyPath[i];
    const current = obj[key];
    if (current === undefined) {
      obj[key] = {};
    }
    obj = obj[key];
    i++;
  }

  obj[keyPath[i]] = value;
}

function transform(obj) {
  const result = {};
  for (const [key, value] of Object.entries(obj)) {
    set(result, key.split('.'), value);
  }
  return result;
}

点评

估计本意是想考察实现 lodash.set

我写的时候有点紧张,那个在线平台第一次用,不太熟悉,不知道咋跑测试用例,写完代码后,面试官说讲下思路就行,就说了下思路,他说没问题。

一面挂了,我发现面试官面完后还 follow 了我 github,本来印象还挺好的。挂了就挂了,本来没放心上,结果也不知道这面试官面评写了啥,导致别的部门 hr 直接不给面试机会了。3 年前应届面试的时候,最后 leader 面面完之后也是没下文,感觉当时表现也还行。以后就算还会面字节也不会考虑这个部门了。

北京XX设计

爬楼梯

爬楼梯,拿羽毛球,青蛙跳,以及斐波那契数列,这几个题的思路和代码都长一样。

点评

但凡你刷过 leetcode,就会做。

面试官一开始说最后做到动态规划题吧,当时我心头一惊,草,我最怕的就是动态规划了,结果还好是到简单题。

这家公司我也挺无语的,hr 在 boss 上主动约我面试。一面面完我问面试结果,它和我说过了,下周会安排二面,结果过了两周也没安排二面。也不知道是过年太忙了,还是没 hc 了,还是说有没有可能他被裁了?

某电商大厂

实现 Array.prototype.reduce

javascript 复制代码
Array.prototype.reduce = function (callback, init) {
  let array = this;
  let acc = init;
  if (init === undefined) {
    acc = array[0];
    array = array.slice(1);
  }

  for (const [index, item] of array.entries()) {
    acc = callback(acc, item, init === undefined ? index + 1 : index);
  }
  return acc;
};

点评

这种简单题我感觉更多是考察一个开发的代码风格怎么样。

他们那个在线代码平台是在我的 chrome 上打开老是崩溃,浪费挺多时间,应该是和某个 chrome 插件有关系,关掉大部分插件后就不崩溃了。

当时我写的时候没处理不传初始值的情况,因为我平时写代码一直都是写初始值的,当然面试官也问了这个问题,我就说了下没传初始值就是用第一个元素当初始值。

Monkey

实现 Monkey 函数,运行后得到下面的输出。

javascript 复制代码
Monkey('Alan').eat('Banana').sleep(4).eat('Apple').sleep(5).eat('Pear');
// my name is Alan
// I eat Banana
// 等待 4s
// I eat Apple
// 等待 5s
// I eat Pear

好像是腾讯校招最近特别喜欢考的题,怀疑面试官是腾讯跳槽过去的。这道题我之前在牛客上看过,但是记不清了。

临时发挥写的有点问题:

javascript 复制代码
function Monkey(name) {
  console.log(`my name is ${name}`);
  let waiting = 0;

  const result = {
    eat(food) {
      if (waiting === 0) {
        console.log(`I eat ${food}`);
      } else {
        setTimeout(() => {
          console.log(`I eat ${food}`);
        }, waiting);
      }

      return result;
    },

    sleep(seconds) {
      console.log(`等待 ${seconds}s`);
      waiting += seconds;
      return result;
    },
  };
  return result;
}
// my name is Alan
// I eat Banana
// 等待 4s
// 等待 5s
// I eat Apple
// I eat Pear

面试完想了下:

javascript 复制代码
function Monkey(name) {
  console.log(`my name is ${name}`);
  const queue = [];
  let waiting = 0;
  let running = false;

  const result = {
    eat(food) {
      const sleepTime = waiting;
      if (waiting !== 0) {
        waiting = 0;
      }

      const task = () => {
        running = true;
        if (sleepTime !== 0) {
          console.log(`等待 ${sleepTime}s`);
        }

        setTimeout(() => {
          console.log(`I eat ${food}`);
          running = false;

          if (queue.length > 0) {
            const frontTask = queue.shift();
            frontTask();
          }
        }, sleepTime * 1000);
      };

      if (running === false) {
        task();
      } else {
        queue.push(task);
      }
      return result;
    },

    sleep(seconds) {
      waiting += seconds;
      return result;
    },
  };
  return result;
}

插曲

面试官:vue data 中的数组是怎样监听它的修改? 我:覆写数组方法 面试官:怎么覆写的呢? 我:由于刚做过 Array.prototype.reduce,没想太多,回答:直接改原型上的方法呗

其实回答的时候我就感觉不太对劲,我刚想改说应该是直接增加实例属性,并且用 Object.defineProperty 修改了 enumerationfalse,但是主要面试官也没反问说这有问题就问下一个问题了。

面完研究了下,应该是在原型链上增加一个中间原型来实现的:

javascript 复制代码
// 获取数组的原型
const arrayProto = Array.prototype;
// 基于数组的原型创建一个新的对象
const middleProto = Object.create(arrayProto);

// 一个要被重写的方法列表
const methodsToPatch = ['push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse'];

for (const method of methodsToPatch) {
  // 缓存原始方法
  const original = arrayProto[method];
  // 定义重写的方法
  Object.defineProperty(middleProto, method, {
    value: function mutator(...args) {
      // 调用原始方法
      const result = original.apply(this, args);
      // 获取数组对象的 ob 对象,它是一个 Observer 实例
      const ob = this.__ob__;
      // 通知变更
      ob.dep.notify();
      return result;
    },
    enumerable: false,
    writable: true,
    configurable: true,
  });
}

// 假设 arr 是 Vue 管理的数组
// 将 arr 的原型指向重写了方法的对象
Reflect.setPrototypeOf(arr, middleProto);

某电商

TypeScript 体操

实现一个函数,返回值的类型和参数类型相同:

typescript 复制代码
const identity = <T>(a: T): T => {
  // ...
};

点评

考察泛型的理解和使用。

应该是看我简历上写了熟悉类型体操,本来还想说终于可以在面试的时候表演一波体操,但这完全体现不出我的实力呀。

异步加法

要求:

javascript 复制代码
// 已知异步方法
function add(a, b, callback) {
  return setTimeout(() => {
    callback(a + b);
  });
}

// 实现...
async function sum(...args) {}

// 效果
sum(1, 2, 3).then((value) => console.log(value)); // => 6

答案:

javascript 复制代码
const _add = (a, b) => {
  return new Promise((resolve) => {
    add(a, b, (result) => resolve(result));
  });
};

async function sum(...args) {
  let result = 0;
  for (const arg of args) {
    result = await _add(result, arg);
  }
  return result;
}

sum(1, 2, 3).then((value) => console.log(value)); // => 6

点评

考察对 JavaScript 异步的理解和处理。

使用栈实现队列

这是二面的题目,和 leetcode 原题不一样的是多了个时间复杂度要求为常数级printMin 方法:

javascript 复制代码
class Queue {
  put() {}
  take() {}
  size() {}
}

class Stack {
  constructor() {
    this.queue = new Queue();
    this.min = Infinity;
  }

  push(value) {
    this.queue.put(value);
    if (value < this.min) {
      this.min = value;
    }
  }

  pop() {
    let top;
    let min = Infinity;
    for (let i = 0, len = this.queue.size(), last = len - 1; i < len; i++) {
      const front = this.queue.take();
      if (i < last) {
        if (front < min) {
          min = front;
        }
        this.queue.put(front);
      } else {
        top = front;
      }
    }
    this.min = min;
    return top;
  }

  // 要求常数级
  printMin() {
    if (this.queue.size() === 0) {
      throw new Error('Stack is empty!');
    }
    return this.min;
  }
}

总结

题目都不难,做到:

  • 不要太紧张
  • 多刷刷题保持手感

就没太大问题。

目前还在找工作,如果你对我感兴趣,这是我的在线简历,也欢迎私信或者邮件互加微信交流找工作的事情。

相关推荐
forwardMyLife5 分钟前
element-plus的面包屑组件el-breadcrumb
javascript·vue.js·ecmascript
ice___Cpu6 分钟前
Linux 基本使用和 web 程序部署 ( 8000 字 Linux 入门 )
linux·运维·前端
JYbill8 分钟前
nestjs使用ESM模块化
前端
加油吧x青年27 分钟前
Web端开启直播技术方案分享
前端·webrtc·直播
吕彬-前端1 小时前
使用vite+react+ts+Ant Design开发后台管理项目(二)
前端·react.js·前端框架
小白小白从不日白1 小时前
react hooks--useCallback
前端·react.js·前端框架
恩婧1 小时前
React项目中使用发布订阅模式
前端·react.js·前端框架·发布订阅模式
mez_Blog1 小时前
个人小结(2.0)
前端·javascript·vue.js·学习·typescript
孙小二写代码1 小时前
[leetcode刷题]面试经典150题之1合并两个有序数组(简单)
算法·leetcode·面试
珊珊而川2 小时前
【浏览器面试真题】sessionStorage和localStorage
前端·javascript·面试