返回

前端手撕高频面试题——拿下高薪Offer

前端

在前端开发的求职面试中,“手撕”前端的考题可以说是各位面试官最钟爱的环节了。而针对这方面的针对性训练,对各位求职者而言也是至关重要的。

为此,本文将对面试中高频出现的“手写”题目进行梳理,给出思路和参考的示例解答,供大家参考。

一、判断属性值相等
题目:

const obj1 = { a: 1, b: 2 };
const obj2 = { a: 1, b: 2 };
const obj3 = { a: '1', b: '2' };
console.log(obj1.a === obj2.a);  // true
console.log(obj1.a === obj3.a);  // false

解析:

  1. === 操作符对左右两边的值都会先进行全等转换,再进行严格相等判断;
  2. 严格相等判断会区分数据值的属性,即数值型与字符串型视为不等。

二、数组扁平化
题目:

const arr = [[1, 2, 3], [4, 5, 6], [7, 8, 9]];
console.log(arr.reduce((acc, cur) => acc.join(',') + cur.join(','), '));  // 1,2,3,4,5,6,7,8,9

解析:

  1. 利用 reduce() 方法依次遍历数组;
  2. 将每个嵌套数组 join 成字符串;
  3. 再将这些字符串片段逐次拼接起来即可达到扁平化。

三、深拷贝
题目:

const obj1 = { a: [1, 2, 3] };
const obj2 = deepCopy(obj1);
obj2.a[0] = 0;
console.log(obj1.a[0]);  // 0

解析:

  1. 利用递归遍历对象的每一层级,既可以复制复杂嵌套的对象。
  2. 遍历到最底层时,直接进行值的拷贝。
  3. 遍历到嵌套结构,则进行递归调用。

四、数组去重
题目:

const arr = [1, 2, 3, 2, 4, 5, 6, 5, 3];
console.log([...new Set(arr)]);  // [1, 2, 3, 4, 5, 6]

解析:

  1. 利用 Set 数据结构的特性,自动去重;
  2. 再将 Set 转换为 Array 输出。

五、防抖节流
题目:

function debounce(fn, delay) {
  let lastTime = 0;
  return (...args) => {
    if (Date.now() - lastTime < delay) {
      return;
    }
    lastTime = Date.now();
    return fn(...args);
  };
}

解析:

  1. 闭包中定义一个 lastTime 变量,用于计时;
  2. 判断时间间隔大于 delay,则触发防抖;
  3. 通过重复触发该方法,可达到防抖的效果。

六、节流
题目:

function throttle(fn, delay) {
  let flag = false;
  return (...args) => {
    if (flag) {
      return;
    }
    flag = true;
    window.requestAnimationFrame(() => {
      flag = false;
      fn(...args);
    });
  };
}

解析:

  1. 闭包中定义一个 flag 变量,用于判断节流;
  2. 通过 requestAnimationFrame 优化,保证在帧动画的第一次回调中再调用原方法。

七、继承
题目:

class Base {
  constructor(name) {
    this.name = name;
  }
  getName() {
    return this.name;
  }
}
class Child extends Base {
  constructor(age) {
    super('demo');
    this.age = age;
  }
  getAge() {
    return this.age;
  }
}
const child = new Child(18);
console.log(child.getName());  // demo
console.log(child.getAge());  // 18

解析:

  1. 子类在构造方法中 super 调用父类构造方法;
  2. super(...) 传入的参数应和父类构造器相匹配。

八、手写 Promise
题目:

class Promise {
  constructor(callback) {
    this.callback = callbacks;
  }
  then(success) {
    this.successCallback = success;
    this.executeCallback();
  }
  catch(fail) {
    this.failCallback = fail;
    this.executeCallback();
  }
  executeCallback() {
    try {
      const res = this.callback();
      if (res) {
        res.then(this.successCallback);
      }
    } catch (err) {
      this.failCallback && this.failCallback(err);
    }
  }
}

解析:

  1. 利用类实现了 Promise 的简易版本;
  2. 通过 then() 与 catch() 方法,定义好不同的回调;
  3. 再使用一个通用的方法来调用这些回调。

九、Vue 生命周期的顺序

<script>
export default {
  beforeCreate() { },
  created() { },
  beforeUpdate() { },
  mounted() { },
  activated() { },
  beforeUnmount() { },
  beforeDesotry() { },
  unmounted() { },
  desotryed() { }
};
</script>

解析:

  1. Vue 组件生命周期的顺序如下:
    • 创建前:beforeCreate
    • 已初始化:created
    • 更新前:beforeUpdate
    • 已装载:mounted
    • 激活后:activated
    • 卸载前:beforeUnmount
    • 销毁前:beforeDesotry
    • 已卸载:unmounted
    • 已销毁:desotryed
  2. 根据组件的使用场景,选择性地使用这些钩子。

十、手写一个简易的 Vuex 仓库

class Store {
  constructor() {
    this.state = {};
    this.actions = {};
    this.mutations = {};
    this.getters = {};
  }
  setState(newState) {
    this.state = { ...this.state, ...newState };
  }
  getState() {
    return this.state;
  }
  // 其余略
}

解析:

  1. 使用类来简化 Vuex 的基础架构;
  2. 暴露 state、actions、mutations、getters 等 API;
  3. 利用 setState() 方法来进行内部 state 管理。

以上便是前端面试中常见的高频手撕题目,希望能对大家有所帮助。切记,勤加训练,熟能生巧,才能在激烈的竞争中脱颖而出。