前端面试那些你必须手撕的代码

目录

1.call

2.apply

3.bind

4.promise.all

5.promise.race

6.promise.allsettled

7.new

8.数组扁平化

9.发布订阅模式

10.观察者模式

11.防抖

12.节流

13.最大并发为max的调度器

14.函数柯里化,实现累加器

15.instanceof


        因为最近在找实习,所以会遇到很多手撕代码题目,干脆统一手打后进行总结,下面的代码都是可以运行的代码,基本是来自于牛客、其他人面经和自己面试被问到。后续如果还有会继续补充。

1.call

        call、apply、bind应该算是手写和八股中最常见的三兄弟了,但是他们每个都有自己的特点,比如call和apply是立即执行,而bind是绑定this后不立即执行。再有就是apply是第二个参数是数组,而call是依次传入参数,记住这几点,相对于死记硬背就简单多了。

​​​​​​​Function.prototype.myCall = function(cont){
    let context = cont ||window
    let args = [...arguments].slice(1)
    context._fn = this;
    let res = context._fn(...args);
    delete context._fn;
    return res;
}

2.apply

Function.prototype.myCall = function(cont){
    let context = cont ||window
    let args = [...arguments].slice(1)
    context._fn = this;
    let res = context._fn(...args);
    delete context._fn;
    return res;
}

3.bind

Function.prototype.MyApply = function (cont){
  let context = cont || window;
  if(typeof this != 'function'){
    throw new Error('this is not a function!')
  }
  const _this  = this;
  return function (){
    _this.call(context,...arguments);
  }
}

4.promise.all

        promise 应该也算是常考点了,all 和 race 手写一起服用效果更佳呦。当然还有一个是allsettled 。记住 all 是所有promise完成,race是一个完成,但是有rejected 他俩就结束。allsettled是所有都结束,不管是哪个状态。

function myPromiseAll (promiseArr){
  return new Promise(function(resolve,reject){
    let result = []
    let count = 0;
    for(let i =0;i<promiseArr.length;i++){
      promiseArr[i].then(res=>{
        //注意这里是按照输入数组的顺序,而不是push
        result[i]=res;
        count++;
        if(count==promiseArr.length){
          resolve(result);
        }
      },err=>{
        reject(err)
      })
    }
  })
}

5.promise.race

function myPromiseRace (promiseArr){
  return new Promise(function(resolve,reject){
      for(let i =0;i<promiseArr.length;i++){
        promiseArr[i].then(res=>{
          resolve(res)
        },err=>{
          reject(err)
        })
      }
  })
}

6.promise.allsettled

function myAllSetteld (promiseArr){
  return new Promise(function(resolve,reject){
    let count = 0;
    let result = [];
    for(let i =0;i<promiseArr.length;i++){
      promiseArr[i].then(res=>{
        result[i]= {'status':'fulfilled','value':res};
        count++;
        if(count==promiseArr.length) resolve(result)
      },err=>{
        result[i] = {'status':'rejected','reason':err};
        count++;
        if(count==promiseArr.length) resolve(result)
      })
    }
  })
}

7.new

        要记住new的原理,都在注释里面了。

function myNew (constructor,...args){
  /* 1. 创建一个新对象
     2. 将构造函数原型上的方法复制给新对象
     3. 传入参数执行构造函数,并获取执行结果
     4. 判断返回值是是否为对象,不为对象返回执行结果 
  */
 let newObj = Object.create(constructor.prototype);
 let result = constructor.apply(newObj,args);
 return typeof result ==='object' ? result : newObj;
}

8.数组扁平化

        常问,一般使用递归就可以完成。

function flat (arr,depth=Infinity){
  let res= []
  arr.map(el=>{
    if(Array.isArray(el)&&depth>0){
      //注意concat返回值
      res = res.concat(flat(el,depth-1));
    }else{
      res.push(el);
    }
  })
  return res;
}

9.发布订阅模式

        牛客前端手撕代码里面的题目,别问,问就是我下午复习,晚上面试官就让我手撕了(人品爆发↑)

class EventEmitter {
  constructor(){
    // 是一个对象来记录事件名和相应的触发函数
    this.events = {}
  }
  // 绑定一个事件的事件处理函数
  on(event,fcCallBack){
    if(!this.events[event]){
      this.events[event] = [fcCallBack]
    }else{
      this.events[event].push(fcCallBack)
    }
  }


  //触发函数
  emit(event,...args){
    if(this.events[event]){
      this.events[event].forEach(el=>{
        el.apply(this,args);
      })
    }
  }


  //停止某个事件的事件处理函数
  off (event,fcCallBack){
    if(!this.events[event]) return;
    this.events[event].filter(item=> item!= fcCallBack)
  }


  //绑定单次执行事件处理函数
  once (event,fcCallBack){
    function fn(){
      fcCallBack();
      this.off(event,fcCallBack)
    }
    this.on(event,fn)
  }

}

10.观察者模式

        也是牛客前端手撕的题目,思路不难,但是最好自己实现一下。

/*
描述
    请补全JavaScript代码,完成"Observer"、"Observerd"类实现观察者模式。要求如下:
    1. 被观察者构造函数需要包含"name"属性和"state"属性且"state"初始值为"走路"
    2. 被观察者创建"setObserver"函数用于保存观察者们
    3. 被观察者创建"setState"函数用于设置该观察者"state"并且通知所有观察者
    4. 观察者创建"update"函数用于被观察者进行消息通知,该函数需要打印(console.log)数据,数据格式为:小明正在走路。其中"小明"为被观察者的"name"属性,"走路"为被观察者的"state"属性
    注意:
    1. "Observer"为观察者,"Observerd"为被观察者
*/
class Observerd {
    constructor(name,state='走路'){
        this.name = name;
        this.state = state;
        this.observers = [];
    } 
    setObserver(observer){
        this.observers.push(observer);
    }
    setState(state){
        this.state = state;
        this.observers.forEach(item=>item.update(this))
    }
 }


 class Observer {
     update(observerd){
         console.log(`${observerd.name}正在${observerd.state}`)
     }
 }

11.防抖

        防抖节流是常问的话题,最好记住一种常用的实现方式。

function debound (fn,delay){
    let id = null;
    return function(){
        if(id){
            clearTimeout(id)
            id = setTimeout(fn,delay)
        }else{
            id = setTimeout(fn,delay)
        }
    }
}

12.节流

function throttle (fn,delay){
    let start = 0;
    return function(){
        let now = Date.now();
        if(now-start>delay){
            fn();
            start = now;
        }
    }
}

13.最大并发为max的调度器

        字节面试被问到,当时有点思路,但是没写出来。(血泪教训,多自己手敲代码,别看懂逻辑就结束)

/* 实现一个带并发限制的异步调度器scheduler,保证同时运行最多的任务有max */
class Scheduler{
    constructor(max){
        this.cache = [];    //缓存队列
        this.task = [];     // 当前执行队列
        this._max = max;    //最大并发任务
    }


    add(promiseCreator){
        return new Promise(resolve=>{
            // 最大并发执行数量
            promiseCreator.resolve = resolve; // 保存当前promise的状态
            if(this.task.length < this._max) { // 最大并发任务处理
                this.runWork(promiseCreator)
            }else{
                this.cache.push(promiseCreator)
            }
        })
    }


    runWork(promiseCreator) {
        this.task.push(promiseCreator)
        promiseCreator().then(() => {
          promiseCreator.resolve()
          this.task.splice(this.task.indexOf(promiseCreator), 1) // 当前任务执行完成 清除task中的数据
          if(this.cache.length) {
               this.runWork(this.cache.shift()) // 根据执行的缓存顺序执行,保证执行的有序性
          }
        })
    }
    
}


const timeout = (time) => new Promise(resolve=>{
    setTimeout(resolve,time)
})


const scheduler = new Scheduler(2);


const addTask = (time,order) =>{
    const result = scheduler.add(()=>timeout(time,order));
    result.then(()=>{
        console.log(order);
    })
}
addTask(1000, '1')
addTask(500, '2')
addTask(300, '3')
addTask(400, '4')

14.函数柯里化,实现累加器

function add(a,b,c){
    console.log(a+b+c)
}
function curry (fn,...args){
    let len = fn.length;
    return function(){
        let _args= [...args,...arguments]
        if(_args.length<len){
            return curry.call(this,fn,..._args);
        }
        return fn.apply(this,_args)
    }
}
let tmp = curry(add);
tmp(3)(1)(4);

15.instanceof

        原型链相关的,但是逻辑不难。

function myInstanceof(target,Fn){
  let proto = target.__proto__;
  while(true){
    if(proto===Fn.prototype){
      return true;
    }
    if(proto===null){
      return false;
    }
    proto =  proto.__proto__;
  }
}

  • 0
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值