0%

前端八股(高频手撕代码)

高频手撕代码考察

  1. 手写Promise.all()

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    //接受一个数据(或可迭代对象)
    //等所有 Promise 成功,返回一个新 Promise,结果是 按顺序 的结果数组。
    //如果其中有一个失败(reject),立即返回 reject。
    function promiseAll(promises) {
    return new Promise((resolve, reject) => {
    if (!Array.isArray(promises)) {
    return reject(new TypeError("args not an array"));
    }

    let results = [];
    let completed = 0;

    promises.forEach((p, index) => {
    Promise.resolve(p).then(value => {
    results[index] = value;
    completed++;
    if (completed === promises.length) {
    resolve(results);
    }
    }) catch (error => {
    reject(error);
    });
    });

    if (promises.length === 0) {
    resolve([]);
    }
    });
    }

    const p1 = Promise.resolve(1);
    const p2 = new Promise(res => setTimeout(() => res(2), 1000));
    const p3 = 3;

    promiseAll([p1, p2, p3])
    .then(results => console.log("success:", results))
    .catch(error => console.error("failed:", error));

  2. 手撕防抖

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    function debounce(fn, delay) {
    let timer = null;

    return function(...args) {
    const context = this;
    if(timer) {
    clearTimeout(timer);
    }

    timer = setTimeout(() => {
    fn.apply(context, args);
    }, delay);
    };
    }

    function onResize() {
    console.log("resize 事件触发:", new Date().toLocaleTimeString());
    }
    // 500ms 防抖
    window.addEventListener("resize", debounce(onResize, 500));

  • 在防抖函数里:
    • context = this; 保存了外部调用时的 this(比如某个按钮 DOM 对象)。
    • args = arguments(用扩展语法 …args 收集)保存了调用时传进来的参数。
  • 当定时器触发时,如果直接写 fn():
    • this 会丢失(定时器里的 this 默认是 window 或 undefined)。
    • 参数也没法自动带过去。
  • 所以要用 fn.apply(context, args):
    • 保证 this 正确 → 就是调用 debounce 包裹函数时的 this。
    • 保证参数不丢失 → args 是数组,apply 会把它展开传给 fn。
  1. 手撕new函数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    function myNew(Constructor, ...args) {
    // create null obj, let its __proto__ pointed to constructor's prototype
    const obj = Object.create(Constructor.prototype);

    // execute constructor, bind this to obj
    const result = Constructor.apply(obj, args);

    return (result !== null && (typeof result === 'object' || typeof result === 'function'))
    ? result
    : obj;
    }

    function Person(name, age) {
    this.name = name;
    this.age = age;
    }
    Person.prototype.sayHi() = function() {
    console.log("Hi, I'm " + this.name);
    };

    const p1 = myNew(Person, 'Alice', 30);
    p1.sayHi();

  2. 手撕LRU,时间复杂度O(1)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    class LRUCache {
    constructor(capacity) {
    this.capacity = capacity;
    this.map = new Map(); //有序表
    }

    get(key) {
    if (!this.map.has(key)) return -1;
    const value = this.map.get(key);
    this.map.delete(key);
    this.map.set(key, value);//放到队尾
    return value;
    }

    put(key, value) {
    if (this.map.has(key)) {
    this.map.delete(key);
    } else if (this.map.size >= this.capacity) {
    const firstKey = this.map.keys().next().value;
    this.map.delete(firsetKey);//找到并删除队首
    }
    this.map.set(key, value);
    }
    }