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

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

某哈游-协同文档

深度克隆

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;
  }
}

总结

题目都不难,做到:

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

就没太大问题。

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

相关推荐
FogLetter几秒前
图片懒加载:让网页飞起来的魔法技巧 ✨
前端·javascript·css
Mxuan1 分钟前
vscode webview 插件开发(精装篇)
前端
Mxuan2 分钟前
vscode webview 插件开发(交付篇)
前端
Mxuan3 分钟前
vscode 插件与 electron 应用跳转网页进行登录的实践
前端
拾光拾趣录3 分钟前
JavaScript 加载对浏览器渲染的影响
前端·javascript·浏览器
Codebee4 分钟前
OneCode图表配置速查手册
大数据·前端·数据可视化
然我4 分钟前
React 开发通关指南:用 HTML 的思维写 JS🚀🚀
前端·react.js·html
Mxuan6 分钟前
vscode webview 插件开发(毛坯篇)
前端
FogLetter7 分钟前
前端性能优化:深入理解回流与重绘
前端·css
清沫27 分钟前
键盘效率提升指南(VSCode+Vim+SurfingKeys)
前端·vim·visual studio code