跳转至

前端面试题库

reduce实现

https://www.lanqiao.cn/problems/2604/learning/?page=6&first_category_id=2&second_category_id=10

function myReduce(arr, callback, initValue) {
  let ans;
  if (initValue != undefined) 
    ans = callback(initValue, arr[0], 0, arr);
  else
    ans=arr[0]
  for (let index = 1; index < arr.length; index++)
    ans = callback(ans, arr[index], index, arr);
  return ans;
}
const arr = [1, 2, 3];
const sum = myReduce(arr,  (pre, cur, index, arr) => pre + cur + index + arr[index], 0);

Object.is实现

const is = (val1, val2) => {
    if (val1 === val2) {
    // 处理 +0 === -0
        return x !== 0 || 1 / x === 1 / y;
    }else {
        // 处理NaN,利用NaN !== NaN 的特型
        return x !== x && y !== y
    }
};
function myObjectIs(x, y) {
    if (Number.isNaN(x) && Number.isNaN(y)) return true;
    if (x === 0 && 1 / x !== 1 / y) return false;
    if (x === y) return true;
    return false;
}

实现 lodash 的 get 方法

function myGet(object, path, defaultValue) {
  let ans = object;
  if (!Array.isArray(path)) {
    path = path.replaceAll(/\[(.*?)\]/g, `.$1`);
    path = path.split(".");
  }
  if (Array.isArray(path)) {
    for (let item of path) {
      ans = ans[item];
      if (ans === undefined) return defaultValue;
    }
  }
  return ans
}
const object = { a: [{ b: { c: 3 } }] };

// path 为 String 的示例。
myGet(object, "a[0].b.c"); // 返回 3
myGet(object, "a.0.b.c"); // 返回 3

// path 为 Array 的示例。
myGet(object, ["a", "0", "b", "c"]); // 返回 3

// 传入 defaultValue 的示例。
// 如果解析的值是 undefined,最终返回传入的默认值。
myGet(object, "a.b.c", "default"); // 返回 'default'
  • /\[(.*?)\]/ 变为非贪婪匹配

实现深拷贝

function deepClone(target, hash = new WeakMap()) {
    if (typeof target !== 'object' || target === null) return target
    if (target instanceof RegExp) return new RegExp(target)
    if (target instanceof Date) return new Date(target)
    if (target instanceof Map) {
        let map = new Map()
        target.forEach((value, key) => {
            // map的key和value都可能是object
            map.set(deepClone(key, hash), deepClone(value, hash))
        })
        return map
    }
    if (target instanceof Set) {
        let set = new Set()
        target.forEach((value) => {
            // map的key和value都可能是object
            set.set(deepClone(value, hash))
        })
        return set
    }

    if (hash.has(target)) return hash.get(target)
    let cloneObj = target.constructor()
    for (let key in target) { // 数组也在这里处理
        cloneObj[key] = deepClone(target[key])
    }
    let symbolKeys = Object.getOwnPropertySymbols(target)
    for (let key in symbolKeys) {
        cloneObj[key] = deepClone(target[key])
    }
    return cloneObj
}

flat实现

function myFlat(arr, depth = 1) {
  let ans = [];
  if (depth <= 0) return arr;
  for (let item of arr) {
    if (Array.isArray(item)) ans.push(...myFlat(item, depth - 1));
    else ans.push(item);
  }
  return ans;
}

实现new

function myNew(Fn, ...args) {
    const instance = Object.create(Fn.prototype)
    // 或者:instance.__proto__ = Fn.prototype,两者等价
    const result = Fn.apply(instance, args)
    // 如果构造函数调用返回了一个对象,那么就返回这个对象,否则返回它的实例
    return typeof result === 'object' ? result : instance
}
function myNew(constructor, ...args) {
  // 1. 创建一个新的空对象
  const obj = {};
  // 2. 将这个新对象的原型链接到构造函数的原型对象
  Object.setPrototypeOf(obj, constructor.prototype);
  // 3. 将构造函数的 `this` 绑定到这个新对象上,并执行构造函数
  const result = constructor.apply(obj, args);
  // 4. 如果构造函数返回一个对象,则返回这个对象;否则,返回新创建的对象
  return (result && typeof result === 'object') ? result : obj;
}

new的工作:

  1. 创建一个新的对象obj
  2. 将对象与构建函数通过原型链连接起来
  3. 把构建函数汇总的this绑定到obj
  4. 如果返回值不是对象就忽略,是对象的话返回

实现bind

Function.prototype.myBind = function (context, ...args1) {
    if (this === Function.prototype) {
        throw new TypeError('Error');
    }
    const _this = this;
    return function F(...args2) {
        // 判断是否用于构造函数
        if (this instanceof F) {
            return new _this(...args1, ...args2);
        }
        return _this.apply(context, args1.concat(args2));
    };
};

function greet(greeting, punctuation) {
    return `${greeting}, ${this.name}${punctuation}`;
}

const person = { name: 'Alice' };
const greetPerson = greet.myBind(person, 'Hello');
console.log(greetPerson('!')); // 输出: "Hello, Alice!"

// 构造函数调用
function Person(name, age) {
    this.name = name;
    this.age = age;
}

const BoundPerson = Person.myBind(null, 'Alice');
const alice = new BoundPerson(30);
console.log(alice.name); // 输出: "Alice"
console.log(alice.age);  // 输出: 30
console.log(alice instanceof Person); // 输出: true
console.log(alice instanceof BoundPerson); // 输出: true

new.target 属性允许你检测函数或构造方法是否是通过new运算符被调用的。在通过new运算符被初始化的函数或构造方法中,new.target返回一个指向构造方法或函数的引用。

在普通的函数调用中,new.target 的值是undefined。

使用call实现bind

function myBind(fn, context, ...arg) {
    return function () {
        return new.target ? new fn(...arg, ...arguments) : fn.call(context, ...arg, ...arguments)
    }
}

实现call

使用apply实现call

function myCall(fn, context, ...arg) {
  return fn.apply(context,arg)
}
function myCall(fn, context, ...args) {
    context.fn = fn
    let res = context.fn(...args)
    delete context.fn
    return res
}

实现Compose

function myCompose(...functions) {
  return function(arg){
    for( let fn of functions.reverse()){
        arg=fn(arg)
    }
    return arg
  }
}
functions.reduce((a, b) => (...args) => a(b(...args)))
functions.reduce((x, y) => z => x(y(z)))

实现Promise队列

class Scheduler {
  constructor(limit) {
    this.limit = limit;
    this.queue = [];
    this.curr = 0;
  }
  add(creator) {
    return new Promise((reslover) => {
      const thisTask = () => {
        creator().then(()=>{
          reslover()
          this.curr--
          if(this.queue.length>0){
            this.queue.shift()()
          }
        });
      };
      if (this.curr == this.limit) {
        this.queue.push(thisTask);
      } else {
        this.curr++
        thisTask();
      }
    });
  }
}
const scheduler = new Scheduler(3);

const addTask = (time, order) => {
  scheduler
    .add(() => {
      return new Promise((resolve) => {
        setTimeout(resolve, time);
      });
    })
    .then(() => console.log(order));
};

addTask(1000, "1");
addTask(500, "2");
addTask(300, "3");
addTask(400, "4");
module.exports = Scheduler;
  • 创建一个执行Promise任务的函数
  • 如果条件允许就执行,执行完后执行中任务数减减,并开启下一个任务的执行
  • 不允许就放到队列里面
  • 维护一个待执行的任务队列
  • 维护一个数字,表示当前有多少任务正在执行

跳转到 9-逃离二向箔

实现Promise

最简

class MyPromise {
  constructor(f) {
      this.f = f
  }
  then(f) {
      this.f(data => this.data = f(data))
      return new MyPromise(resolve => resolve(this.data))
  }
  catch(f) {
      this.f(null, f)
      return this
  }
}

GPT

class MyPromise {
    static PENDING = 'pending';
    static FULFILLED = 'fulfilled';
    static REJECTED = 'rejected';

    constructor(executor) {
        this.state = MyPromise.PENDING; // 初始状态
        this.value = undefined; // 成功的值
        this.reason = undefined; // 失败的原因
        this.onFulfilledCallbacks = []; // 成功回调队列
        this.onRejectedCallbacks = []; // 失败回调队列

        const resolve = (value) => {
            if (this.state === MyPromise.PENDING) {
                this.state = MyPromise.FULFILLED;
                this.value = value;
                this.onFulfilledCallbacks.forEach(callback => callback(this.value));
            }
        };

        const reject = (reason) => {
            if (this.state === MyPromise.PENDING) {
                this.state = MyPromise.REJECTED;
                this.reason = reason;
                this.onRejectedCallbacks.forEach(callback => callback(this.reason));
            }
        };

        try {
            executor(resolve, reject);
        } catch (error) {
            reject(error);
        }
    }

    then(onFulfilled, onRejected) {
        onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
        onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason };

        const promise2 = new MyPromise((resolve, reject) => {
            if (this.state === MyPromise.FULFILLED) {
                setTimeout(() => {
                    try {
                        const x = onFulfilled(this.value);
                        resolvePromise(promise2, x, resolve, reject);
                    } catch (error) {
                        reject(error);
                    }
                }, 0);
            } else if (this.state === MyPromise.REJECTED) {
                setTimeout(() => {
                    try {
                        const x = onRejected(this.reason);
                        resolvePromise(promise2, x, resolve, reject);
                    } catch (error) {
                        reject(error);
                    }
                }, 0);
            } else if (this.state === MyPromise.PENDING) {
                this.onFulfilledCallbacks.push(() => {
                    setTimeout(() => {
                        try {
                            const x = onFulfilled(this.value);
                            resolvePromise(promise2, x, resolve, reject);
                        } catch (error) {
                            reject(error);
                        }
                    }, 0);
                });

                this.onRejectedCallbacks.push(() => {
                    setTimeout(() => {
                        try {
                            const x = onRejected(this.reason);
                            resolvePromise(promise2, x, resolve, reject);
                        } catch (error) {
                            reject(error);
                        }
                    }, 0);
                });
            }
        });

        return promise2;
    }

    catch(onRejected) {
        return this.then(null, onRejected);
    }
}

function resolvePromise(promise2, x, resolve, reject) {
    if (promise2 === x) {
        return reject(new TypeError('Chaining cycle detected for promise'));
    }

    let called = false;
    if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
        try {
            const then = x.then;
            if (typeof then === 'function') {
                then.call(x, y => {
                    if (called) return;
                    called = true;
                    resolvePromise(promise2, y, resolve, reject);
                }, r => {
                    if (called) return;
                    called = true;
                    reject(r);
                });
            } else {
                resolve(x);
            }
        } catch (error) {
            if (called) return;
            called = true;
            reject(error);
        }
    } else {
        resolve(x);
    }
}

// 测试自定义的 Promise 和 catch 方法
const promise = new MyPromise((resolve, reject) => {
    setTimeout(() => {
        reject('失败');
    }, 1000);
});

promise.then(value => {
    console.log(value);
}).catch(reason => {
    console.error(reason); // 输出 "失败"
});

官方答案

const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'

class MyPromise {
    constructor(fn) {
        this.status = PENDING
        this.value = undefined
        this.reason = undefined
        this.resolveCallbacks = []
        this.rejectCallbacks = []

        const resolveHandler = (value) => {
            this.status = FULFILLED
            this.value = value
            this.resolveCallbacks.forEach(fn => fn(this.value))
        }

        const rejectHandler = (reason) => {
            this.status = REJECTED
            this.reason = reason
            this.rejectCallbacks.forEach(fn => fn(this.reason))
        }
        try {
            fn(resolveHandler, rejectHandler)
        } catch (err) {
            rejectHandler(err)
        }
    }

    then(onResolve, onReject) {
        onResolve = typeof onResolve === 'function' ? onResolve : value => value
        onReject = typeof onReject === 'function' ? onReject : error => error

        if (this.status === PENDING) {
            return new MyPromise((resolve, reject) => {
                this.resolveCallbacks.push(() => {
                    try {
                        const newValue = onResolve(this.value)
                        resolve(newValue)
                    } catch (err) {
                        reject(err)
                    }
                })

                this.rejectCallbacks.push(() => {
                    try {
                        const newReason = onReject(this.reason)
                        reject(newReason)
                    } catch (err) {
                        reject(err)
                    }
                })
            })
        }

        if (this.status === FULFILLED) {
            return new MyPromise((resolve, reject) => {
                try {
                    const newValue = onResolve(this.value)
                    resolve(newValue)
                } catch (err) {
                    reject(err)
                }
            })
        }

        if (this.status === REJECTED) {
            return new MyPromise((resolve, reject) => {
                try {
                    const newReason = onReject(this.reason)
                    reject(newReason)
                } catch (err) {
                    reject(err)
                }
            })
        }
    }

    catch(onReject) {
        this.then(null, onReject)
    }

    static resolve(value) {
        return new MyPromise((resolve, reject) => { resolve(value) })
    }

    static reject(reason) {
        return new MyPromise((resolve, reject) => { reject(reason) })
    }

    static all(list = []) {
        // 补充代码
        return new MyPromise((resolve, reject) => {
            let ans = [], count = 0
            list.forEach(item => {
                item.then(res => {
                    ans.push(res)
                    count++
                    if (count === list.length) {
                        resolve(ans)
                    }
                })
            })
        })
    }

    static race(list = []) {
        return new MyPromise((resolve, reject) => {
            let run = false
            list.forEach(pro => {
                pro.then((res) => {
                    if (!run) {
                        resolve(res)
                        run = true
                    }
                })
            })
        })
    }
    function promiseRace(promises) {
        return new Promise((resolve, reject) => {
            for (const promise of promises) {
                Promise.resolve(promise).then(resolve, reject);
            }
        });
    }
}

module.exports = MyPromise

主要是清楚Promise规范

  • 实例化了Promise后,就会执行回调
  • 执行了reslove后,状态就变为fulfilled,把reslove的参数保存下来
  • then

实现await

function myAsync(genFn) {
    let gen = genFn();
    let ret
    while (1) {
        ret = gen.next();
        if (ret.done) break
    }
    return Promise.resolve(ret.value)
}
function fn() {
    return myAsync(function* () {
        yield 1;
        yield 2;
        return 3;
    });
}
const p = fn();
p.then((val) => {
    console.log(val); // 打印 3
});
module.exports = myAsync;

async 函数其实就是 Generator 函数的语法糖,它的实现原理,就是将 Generator 函数和自动执行器,包装在一个函数里,就是本题要实现的 myAsync 函数。

这个 myAsync 函数接收一个 Generator 回调函数作为参数,在 myAsync 函数中执行 Generator 函数并自动执行 next 方法,最终返回一个 Promise 实例,把状态为 done 的值 resolve 出来,把错误的信息 reject 出来。

setTimeout实现setInterval

function myInterval(fn, delay, stop={value:false}) {
    if (!stop.value) {
        setTimeout(() => {
            if (!stop.value) {
                fn();
                myInterval(fn, delay, stop)
            }
        }, delay);
    }
    return () => { stop.value = true; };
}

不能直接使用递归,可以嵌套个函数递归

function myInterval(fn, delay) {
  let id;
  function exec() {
    id = setTimeout(() => {
      fn();
      exec();
    }, delay);
  }
  exec()
  return ()=>{
    clearTimeout(id)
  }
}

使用await + promise

function myInterval(fn, delay) {
  let stop = false, timer;
  setTimeout(async () => {
    while (!stop) {
      await new Promise((res, rej) => {
        timer = setTimeout(() => {
          fn();
          res();
        }, delay);
      });
    }
  });
  return () => {
    stop = true;
    clearTimeout(timer);
  };
}
  • 调用reslove => Promise解决

实现Reactive

实现一个简单的响应式(reactive)系统,可以帮助我们理解 Vue.js 或其他响应式框架的基本原理。下面是一个基本的实现,展示了如何使用 JavaScript 的 Proxy 对象来创建一个响应式系统。

  1. 创建一个 reactive 函数,用于将普通对象转换为响应式对象。
  2. 创建一个 watch 函数,用于在响应式对象的属性发生变化时执行回调函数。

1 reactive 函数

reactive 函数使用 Proxy 对象来拦截对目标对象的操作,并在属性发生变化时触发更新。

function reactive(target) {
  const handlers = {
    get(target, key, receiver) {
      try {
        return new Proxy(target[key], handlers);
      } catch (e) {
        return Reflect.get(target, key, receiver);
      }
    },
    set(target, key, value, receiver) {
      const result = Reflect.set(target, key, value, receiver);
      console.log(`Property ${key} set to ${value}`);
      trigger(target, key);
      return result;
    }
  };
  return new Proxy(target, handlers);
}

2 watch 函数

watch 函数用于在响应式对象的属性发生变化时执行回调函数。我们可以使用一个简单的发布-订阅模式来实现这一功能。

const watchers = new Map();

function watch(obj, key, callback) {
  if (!watchers.has(obj)) {
    watchers.set(obj, new Map());
  }
  const objWatchers = watchers.get(obj);
  if (!objWatchers.has(key)) {
    objWatchers.set(key, []);
  }
  objWatchers.get(key).push(callback);
}

function trigger(obj, key) {
  const objWatchers = watchers.get(obj);
  if (objWatchers && objWatchers.has(key)) {
    objWatchers.get(key).forEach(callback => callback());
  }
}

使用示例

const state = reactive({
  count: 0,
  nested: {
    value: 42
  }
});

watch(state, 'count', () => {
  console.log(`Count changed to ${state.count}`);
});

watch(state.nested, 'value', () => {
  console.log(`Nested value changed to ${state.nested.value}`);
});

state.count = 1; // 输出: Property count set to 1, Count changed to 1
state.nested.value = 100; // 输出: Property value set to 100, Nested value changed to 100

2618 检查是否是类的对象实例

https://leetcode.cn/problems/check-if-object-instance-of-class/

var checkIfInstanceOf = function (obj, classFunction) {
  if (obj === null || obj === undefined || !(classFunction instanceof Function))
    return false;
  return Object(obj) instanceof classFunction;
};

使用Object(obj)即可将基本类型转为引用类型

手动实现instanceof

var checkIfInstanceOf = function (obj, classFunction) {
  if ( obj === null || obj === undefined || classFunction === null || classFunction === undefined )
    return false;
  return (obj.__proto__ === classFunction.prototype || checkIfInstanceOf(obj.__proto__, classFunction));
};

一张图搞懂原型、原型对象、原型链 https://juejin.cn/post/6844904146445811720

var checkIfInstanceOf = function (obj, classFunction) {
    let obj_proto = obj?.__proto__ ?? null;
    const class_prototype = classFunction?.prototype ?? null;
    while (1) {
        if (obj_proto === null) 
            return false
        if (obj_proto === class_prototype) 
            return true;
        obj_proto = obj_proto.__proto__;
    }
};

console.log( checkIfInstanceOf(5, Number))

5也有__proto__,但不能直接用instanceof

function myInstanceof (target, Fn) {
    return Fn.prototype.isPrototypeOf(target)
}
function myInstanceof(target, Fn) {
    return ['object', 'function'].includes(typeof target) && !!target && (target.__proto__ == Fn.prototype || myInstanceof(target.__proto__, Fn))
}

2622. 有时间限制的缓存

var TimeLimitedCache = function() {
    this.delays={}
    this.obj = {}
};

/** 
 * @param {number} key
 * @param {number} value
 * @param {number} duration time until expiration in ms
 * @return {boolean} if un-expired key already existed
 */
TimeLimitedCache.prototype.set = function(key, value, duration) {
    let flag
    if(this.obj[key]) flag=true
    else flag=false
    this.obj[key]=value
    this.delays[key]=new Date().getTime() + duration
    return flag
};

/** 
 * @param {number} key
 * @return {number} value associated with key
 */
TimeLimitedCache.prototype.get = function(key) {
    if(new Date().getTime() >= this.delays[key] || !this.obj[key]){
        return -1
    }
    else{
        return this.obj[key]
    }
};

/** 
 * @return {number} count of non-expired keys
 */
TimeLimitedCache.prototype.count = function() {
    let count=0
    for(const item in this.obj){
        if(new Date().getTime()<this.delays[item]){
            count++
        }
    }
    return count
};


const timeLimitedCache = new TimeLimitedCache()
timeLimitedCache.set(1, 42, 1000); // false
timeLimitedCache.get(1) // 42
timeLimitedCache.count() // 1