目录
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
因为最近在找实习,所以会遇到很多手撕代码题目,干脆统一手打后进行总结,下面的代码都是可以运行的代码,基本是来自于牛客、其他人面经和自己面试被问到。后续如果还有会继续补充。
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;
}
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;
}
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);
}
}
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{
//注意这里是按照输入数组的顺序,而不是push
result[i]=res;
count++;
if(count==promiseArr.length){
resolve(result);
}
},err=>{
reject(err)
})
}
})
}
function myPromiseRace (promiseArr){
return new Promise(function(resolve,reject){
for(let i =0;i{
resolve(res)
},err=>{
reject(err)
})
}
})
}
function myAllSetteld (promiseArr){
return new Promise(function(resolve,reject){
let count = 0;
let result = [];
for(let i =0;i{
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)
})
}
})
}
要记住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;
}
常问,一般使用递归就可以完成。
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;
}
牛客前端手撕代码里面的题目,别问,问就是我下午复习,晚上面试官就让我手撕了(人品爆发↑)
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)
}
}
也是牛客前端手撕的题目,思路不难,但是最好自己实现一下。
/*
描述
请补全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}`)
}
}
防抖节流是常问的话题,最好记住一种常用的实现方式。
function debound (fn,delay){
let id = null;
return function(){
if(id){
clearTimeout(id)
id = setTimeout(fn,delay)
}else{
id = setTimeout(fn,delay)
}
}
}
function throttle (fn,delay){
let start = 0;
return function(){
let now = Date.now();
if(now-start>delay){
fn();
start = now;
}
}
}
字节面试被问到,当时有点思路,但是没写出来。(血泪教训,多自己手敲代码,别看懂逻辑就结束)
/* 实现一个带并发限制的异步调度器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')
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
原型链相关的,但是逻辑不难。
function myInstanceof(target,Fn){
let proto = target.__proto__;
while(true){
if(proto===Fn.prototype){
return true;
}
if(proto===null){
return false;
}
proto = proto.__proto__;
}
}