首页 > 其他分享 >前端面试经典手写题

前端面试经典手写题

时间:2023-08-05 21:12:51浏览次数:42  
标签:function resolve return 前端 面试 let const 手写 fn

1、手写Promise


class Promise2 {
  state = "pending";
  callbacks = [];
  constructor(fn) {
    fn(this.resolve.bind(this), this.reject.bind(this));
  }
  resolve(result) {
    if (this.state !== "pending") return;
    this.state = "fullFilled";

    nextTick(() => {
      this.callbacks.forEach((handle) => {
        if (typeof handle[0] === "function") {
          handle[0].call(undefined, result);
        }
      });
    });
  }
  reject(reason) {
    if (this.state !== "pending") return;
    this.state = "rejected";
    nextTick(() => {
      this.callbacks.forEach((handle) => {
        if (typeof handle[1] === "function") {
          handle[1].call(undefined, reason);
        }
      });
    });
  }
  then(success, fail) {
    const handle = [];
    if (typeof success === "function") {
      handle[0] = success;
    }
    if (typeof fail === "function") {
      handle[1] = fail;
    }
    this.callbacks.push(handle);
    return this;
  }
}

function nextTick(fn) {
  if (process !== undefined && typeof process.nextTick === "function") {
    return process.nextTick(fn);
  } else {
    var counter = 1;
    const observer = new MutationObserver(fn);
    var textNode = document.createTextNode(String(counter));

    observer.observe(textNode, {
      // 踪字符更改
      characterData: true,
    });
    counter += 1;
    textNode.data = String(counter);
  }
}

// 方法返回一个Promise实例,此实例在 iterable 参数内所有的promise 都完成(resolved)时回调完成(resolve);
// 如果参数中 promise有一个失败(rejected),此实例回调失败(reject),失败的原因是第一个失败promise的结果。

Promise2.all = function(arrP) {
  let list = [];
  len = 0;
  return new Promise2((resolve, reject) => {
    for (let i = 0; i < arrP.length; i++) {
      arrP[i].then(
        (val) => {
          list[i] = val;
          len++;
          len === arrP.length && resolve(list);
        },
        (err) => {
          reject(error);
        }
      );
    }
  });
};

Promise.prototype.myAll = (iterator) => {
  return new Promise((resolve, reject) => {
    const ret = []
    let count = 0
    Array.from(iterator).forEach((item, index) => {
      Promise.resolve(item).then(data => {
        ret[index] = data
        count++
        if(count === iterator.length) {
          resolve(ret)
        }
      }, reject)
    })
  })
}

// 方法返回一个Promise实例,一旦迭代器中的某个 promise 完成(resolved)或失败(rejected),返回的 promise 就会 resolve 或 reject

Promise2.race = function(arrP) {
  let flag1 = false;
  let flag2 = false;
  return new Promise2((resolve, reject) => {
    for (let i = 0; i < arrP.length; i++) {
      arrP[i].then(
        (data) => {
          !flag2 && !flag1 && resolve(data);
          flag1 = true;
          return;
        },
        (error) => {
          !flag2 && !flag1 && reject(error);
          flag2 = true;
          return;
        }
      );
    }
  });
};

new Promise2((resolve, reject) => {
  let [val, time] = [Math.random(), Math.random() * 1000];
  setTimeout(() => {
    val > 0.2 ? resolve(val) : reject(val);
  }, time);
}).then(
  (val) => console.log("promise 测试:", val),
  (err) => console.error("promise 测试:" + err)
);

const getPList = () => {
  let arrP = [];
  for (let i = 0; i < 10; i++) {
    arrP[i] = new Promise2((resolve, reject) => {
      let [v, t] = [Math.random(), Math.random() * 1000];
      setTimeout(() => {
        v > 0.1 ? resolve(v) : reject(v);
      }, t);
    });
  }
  return arrP;
};

Promise2.all(getPList()).then(
  (data) => console.log("promise.all 测试:", data),
  (err) => console.error("promise.all 测试:" + err)
);

Promise2.race(getPList()).then(
  (data) => console.log("promise.race 测试:", data),
  (err) => console.error("promise.race 测试:" + err)
);

2、手写new

// 新生成一个对象
// 将构造函数的作用域赋值给新对象(即绑定新对象的 this)
// 执行构造函数中的代码(即为这个新对象添加属性)
// 返回新对象

function myNew() {
  // 创建对象
  let obj = new Object();

  // 取第一个参数
  let fn = Array.prototype.shift.call(arguments);

  //obj.__proto__指向fn.prototype
  obj.__proto__ = fn.prototype;

  // 执行结果
  let result = fn.apply(obj, arguments);

  return typeof result === "object" ? result : obj;
}

function Person(name) {
  this.name = name;
}

// var p1 = myNew(Person, "xx");
// console.log(p1.name);

function P(name) {
  this.name = name;
  return 1;
}

var p2 = myNew(P, "xm");
console.log(p2);

3、手写instanceof

实现思路:

1、leftVaule代表实例对象

2.rightVaule代表构造函数

3.利用typeof方法,判断输入的leftVaule是否为对象,如果不是,则返回false

4.遍历leftVaule的原型链,直到找到rightVaule的prototype,如果查找失败的话,返回false,反之,返回true


function myInstanceof(leftValue, rightValue) {
  if (typeof leftValue !== "object" || leftValue === null) return false;

  let leftProto = leftValue.__proto__;
  let rightProto = rightValue.prototype;
  while (true) {
    if (leftProto === null) {
      return false;
    }
    if (leftProto === rightProto) {
      return true;
    }
    leftProto = leftProto.__proto__;
  }
}

myInstanceof([], Array);

4、并发请求限制

限制请求数,一个请求完成替换下一个请求

第一次分段

第二次添加下一个

控制startIndex与endIndex

终止态:返回值已等于请求数,执行cb

// class LimitFetch {}

// 
// 第一次分段
// 第二次添加下一个
// 控制startIndex与endIndex
// 终止态:返回值已等于请求数,执行cb

class LimitFetch {
  constructor(opts) {
    this.requestList = opts.requestList;
    this.limit = opts.limit;
    this.cb = opts.cb;
    this.startIndex = 0;
    this.result = {};
    this.resultCount = 0;
    this.batchRequest();
  }
  batchRequest(num) {
    const endIndex = this.startIndex + (num || this.limit);
    const len = this.requestList.length;

    for (let i = this.startIndex; i < endIndex; i++) {
      this.startIndex++;
      if (!this.requestList[i]) return;
      this.requestList[i]().then((res) => {
        this.result[i] = res;
        this.resultCount++;
        if (this.resultCount === len) {
          this.cb(this.result);
        }
        if (i < len - 1) {
          this.batchRequest(1);
        }
      });
    }
  }
}

// 函数写法
function limitFetch(requestList, limit, cb) {
  let startIndex = 0;
  let results = {};
  let resultCount = 0;

  function batchRequest(num) {
    const endIndex = startIndex + (num || limit);

    for (let i = startIndex, len = requestList.length; i < endIndex; i++) {
      if (!requestList[i]) continue;

      startIndex++;
      requestList[i]().then((res) => {
        resultCount++;
        results[i] = res;
        if (i < len - 1) {
          batchRequest(1);
        }
        if (resultCount === len) {
          cb(results);
        }
      });
    }
  }
  batchRequest();
}

let requestList = [];

function fn(time) {
  return function () {
    // console.log(time);
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        console.log(time);
        resolve(time);
      }, time);
    });
  };
}

for (let i = 0; i < 5; i++) {
  requestList.push(fn(1 * 1000));
}
// limitFetch(requestList, 3, (res) => {
//   console.log(res);
// });

new LimitFetch({
  requestList,
  limit: 3,
  cb: (res) => {
    console.log(res);
  },
});


// 限制并发
// 可以新增加
class BtRequest{
  constructor(opts) {
    this.limit  = opts.limit
    this.isRequest = false
    this.queue = []
  }

  add(fn) {
    this.queue.push(fn)
    if(!this.isRequest) {
      this.request()
    }
  }

  request(end) {
    this.isRequest = true
    end = end || this.limit
    const requestList = this.queue.splice(0, end)
        
    if(!requestList.length) {
      this.isRequest = false
    }
   requestList.forEach(item => {
    Promise.resolve(item()).then((res) => {
        console.log(1,res);
        this.request(1)
      })
    })
    
  }
}


const request = new BtRequest({limit: 1})

request.add(() => {
  console.log(100);
  // return 500
})
request.add(() => {
  console.log(200);
  // return 600

})
request.add(() => {
  console.log(300);
  // return 700

})
request.add(() => {
  console.log(400);
  // return 800

})

5、手写发布订阅者

使用一个对象作为缓存

on 负责把方法发布到缓存的 EventName 对应的数组

emit 负责遍历触发(订阅) EventName 下的方法数组

off 找方法的索引,并删除

// 使用一个对象作为缓存
// on 负责把方法发布到缓存的 EventName 对应的数组
// emit 负责遍历触发(订阅) EventName 下的方法数组
// off 找方法的索引,并删除

function indexOf(a, b) {
  return a.indexOf(b);
}

class EventBus {
  constructor() {
    this.cache = {};
  }

  on(eventName, fn) {
    this.cache[eventName] = this.cache[eventName] || [];
    this.cache[eventName].push(fn);
  }

  off(eventName, fn) {
    const index = this.cache[eventName].indexOf(fn);
    if (index !== -1) {
      this.cache[eventName].splice(index, 1);
    }
  }

  emit(eventName) {
    this.cache[eventName].forEach((fn) => {
      fn();
    });
  }

 once(eventName, cb) {
    const one = (...args) => {
      cb(...args)
      this.off(eventName, one)
    }
    this.on(eventName,one)
  }
}

6、手写一个搜索的组件

支持防抖

<template>
  <div>
    {{a}}
    <input type='text' @input="onInput()">
    <p>{{res}}</p>
  </div>
</template>
<script>

const fetch = () => Promise.resolve('this is fetch data')
export default{
  data() {
    return {
      a: '1',
      res: undefined
    }
  },
  methods: {
    deboundce(fn, time) {
      let timer;

      return function() {
        if(timer) {
          clearTimeout(timer)
        }
        timer = setTimeout(fn, time)
      }
    },
    async fetchData() {
      console.log(11)
      // return Promise.resolve(1)
      this.res = await fetch()
    },
    async onInput() {
      const fn =  this.deboundce(this.fetchData, 1000)
      fn()
      // console.log(data)

    }
  }
}

</script>

7、手写Promise.allSettled

Promise.allSettled 只关心所有 promise 是不是都被 settle 了,不管其是 rejected状态的 promise,还是非 rejected状态(即fulfilled)的 promise, 我都可以拿到它的最终状态并对其进行处理

Promise.allSettled 的结果数组中可能包含以下两种格式的数据

{status:"fulfilled", value:result} 对于成功的响应

{status:"rejected", reason:error} 对于 error

const promise1 = Promise.resolve(3)
const promise2 = new Promise((resolve, reject) => setTimeout(reject, 100,'foo'))
const promise3 = [promise1, promise2]

Promise.myAllSettled = function(promises) {
  return new Promise(resolve => {
    const data = [], len = promises.length
    let count = len;
    for(let i = 0; i < len; i++) {
      const promise = promises[i]
      promise.then(res => {
        data[i] = {status: 'fulfilled', value: res}
      },error => {
        data[i] = {status: 'rejected', value: error}
      }).finally(() => {
        if(!--count) {
          resolve(data)
        }
      })

    }
  })
}

Promise.myAllSettled(promise3)
.then(results => results.forEach(result => console.log(result.status)))

8、手写bind

要支持能做为构造函数

思路:Function 的原型对象上增加一个函数,返回值是一个函数,函数的fn.prototype.constructor 指向函数和函数的 prototype 指向 Object.create(this.prototype)

const obj = {
name: 'xiao'
}

function func(first,last){
console.log(first + this.name, last);

}

Function.prototype.myBind = function(context,...args ){
console.log(context);
context.fn = this;

const fn = function() {
context.fn.apply(context,[...args])
}

fn.prototype = Object.create(this.prototype)
fn.prototype.constructor = this;

return fn
}
const fn1 = func.myBind(obj,'li', 'ming')
fn1()

const fn2 = new fn1()

console.log(fn2);

9、手写一个防抖

// 防抖 // 不管事件触发频率多高,一定在事件触发n秒后才执行,如果你在一个事件触发的 n 秒内又触发了这个事件,就以新的事件的时间为准,n秒后才执行,总之,触发完事件 n 秒内不再触发事件,n秒后再执行。 // 关联记忆: 函数防抖就是法师发技能的时候要读条,技能读条没完再按技能就会重新读条。

function debounce(fn, wait, immediate) {
  let timeout;

  return function () {
    let context = this;
    let args = arguments;
    if (timeout) clearTimeout(timeout);
    if (immediate) {
      var callNow = !timeout;
      timeout = setTimeout(() => {
        timeout = null;
      }, wait);
      if (callNow) fn.apply(context, args);
    } else {
      timeout = setTimeout(function () {
        fn.apply(context, args);
      }, wait);
    }
  };
}

10、手写一个 深拷贝

// 基础版本
function clone2=(target, map = new WeakMap()) {

  if (typeof target === 'object') {
    let cloneTarget = Array.isArray(target) ? [] : {};
    
    if(map.get(target)) return map.get(target)
    map.set(target, cloneTarget)

    for (const key in target) {
          cloneTarget[key] = clone(target[key], map);
      }
      return cloneTarget;
  } else {
      return target;
  }
};


// 资深版本
function isObject (target) {
  const type = typeof target
  return target !== null && (type === 'object' || type === 'function')
}

function getType(target) {
  return Object.prototype.toString.call(target)
}

function getInit(target) {
  const Ctor = target.constructor
  return new Ctor()
}
const mapTag = '[object Map]';
const setTag = '[object Set]';
const arrayTag = '[object Array]';
const objectTag = '[object Object]';
const argsTag = '[object Arguments]'

const boolTag = '[object Boolean]';
const dateTag = '[object Date]';
const errorTag = '[object Error]';
const numberTag = '[object Number]';
const regexpTag = '[object RegExp]';
const stringTag = '[object String]';
const symbolTag = '[object Symbol]';
const funcTag = '[object Function]'

function forEach(array, iteratee) {
  let index = -1;
  const length = array.length;
  while (++index < length) {
      iteratee(array[index], index);
  }
  return array;
}

const deepTag = [mapTag, setTag, arrayTag, objectTag, argsTag]

//TODO: Reg 的拷贝方法
function cloneReg(targe) {
  const reFlags = /\w*$/;
  const result = new targe.constructor(targe.source, reFlags.exec(targe));
  result.lastIndex = targe.lastIndex;
  return result;
}

//TODO:  Symbol 的拷贝方法
function cloneSymbol(targe) {
  return Object(Symbol.prototype.valueOf.call(targe));
}

//TODO: cloneFunction
function cloneFunction(func) {
  const bodyReg = /(?<={)(.|\n)+(?=})/m;
  const paramReg = /(?<=().+(?=)\s+{)/;
  const funcString = func.toString();
  if (func.prototype) {
      console.log('普通函数');
      const param = paramReg.exec(funcString);
      const body = bodyReg.exec(funcString);
      if (body) {
          console.log('匹配到函数体:', body[0]);
          if (param) {
              const paramArr = param[0].split(',');
              console.log('匹配到参数:', paramArr);
              return new Function(...paramArr, body[0]);
          } else {
              return new Function(body[0]);
          }
      } else {
          return null;
      }
  } else {
      return eval(funcString);
  }
}

function cloneOtherType(targe, type) {
  //TODO: constructor
  const Ctor = targe.constructor;
  switch (type) {
      case boolTag:
      case numberTag:
      case stringTag:
      case errorTag:
      case dateTag:
          return new Ctor(targe);
      case regexpTag:
          return cloneReg(targe);
      case symbolTag:
          return cloneSymbol(targe);
      case funcTag:
        return cloneFunction(targe)
      default:
          return null;
  }
}

function clone(target, map = new WeakMap()) {
  if(!isObject(target)) return target

  const type = getType(target)
  let cloneTarget
  if(deepTag.includes(type)) {
    cloneTarget = getInit(target, type)
  }else {
    return cloneOtherType(target, type)
  }

  if (map.get(target)) {
    return map.get(target);
  }
  map.set(target, cloneTarget);

  if(type === setTag) {
    target.forEach(val => {
      cloneTarget.add(clone(val, map))
    })
    return cloneTarget
  }

  // 克隆map
  if (type === mapTag) {
    target.forEach((value, key) => {
        cloneTarget.set(key, clone(value,map));
    });
    return cloneTarget;
  }

   // 克隆对象和数组
    const keys = type === arrayTag ? undefined : Object.keys(target);
    forEach(keys || target, (value, key) => {
        if (keys) {
            key = value;
        }
        cloneTarget[key] = clone(target[key], map);
    });

    return cloneTarget;

}

标签:function,resolve,return,前端,面试,let,const,手写,fn
From: https://www.cnblogs.com/yiyi17/p/17608631.html

相关文章

  • 高级 / 资深前端面试题集锦
    以下是一线互联网公司高级前端面试题总结,包括百度、腾讯、网易、字节、知乎、京东、滴滴,小米,感兴趣的欢迎留言交流。1、请简述JsBridge2、请说一下SSR的单机QPS3、请说一下eggJs的初始化原理4、前端错误如何捕获,promise的错误是如何捕获的5、vue的domdiff算法6、vue的Chil......
  • 前端面试经典算法题
    前言现在面试流行考核算法,做过面试官,也被面试。问算法对面试官来说,是一种解脱,找出了一个看似很高明且能偷懒的办法选择人,避免了不知道问啥的尴尬;被面试者,也找到了一种新的面试八股文,刷就对了;算法题让面试与被面试找到了一种平衡。在实际的开发中,很多被考核的算法确实没啥卵用,面......
  • c#面试编程题
    3个可乐瓶可以换⼀瓶可乐,现在有364瓶可乐,问⼀共可以喝多少瓶可乐,剩下⼏个空瓶。intpingNumber=364;intheNumber=364;while(pingNumber>2){heNumber+=pingNumber/3;pingNumber=(......
  • 6.2 手写卷积类
    importtorchfromtorchimportnnfromd2limporttorchasd2lclassConv2D(nn.Module):def__init__(self,kernel_size):super().__init__()self.weight=nn.Parameter(torch.rand(kernel_size))#如kernel_size=(2,2),则随机初始化一个2x2的卷积......
  • SpringBoot+SpringCloud面试题整理附答案
    什么是SpringBoot?1、用来简化spring初始搭建和开发过程使用特定的方式进行配置(properties或者yml文件)2、创建独立的spring引用程序main方法运行3、嵌入Tomcat无需部署war包,直接打成jar包nohupjava-jar–&启动就好4、简化了maven的配置4、自动配置spring添加对应的starter自......
  • 前端常用页面适配
    1、媒体查询(media)通过在CSS文件中使用@media规则来定义媒体查询。@mediascreenand(max-width:768px){/*在宽度小于等于768px时应用的CSS样式*/}@mediascreenand(min-width:768px)and(max-width:992px){/*在宽度大于768px且小于等于992px时应用的CSS......
  • 前端多人协作之代码规范
    代码规范学习自并感谢Geekers-Admin和Hooks-Admin开源项目的作者HalseySpicy一、EditorConfigEditorConfig用于定义项目中的编辑器配置。可以确保团队成员在不同的编辑器中保持一致的代码风格和格式。......
  • 01手写顺序表
    一、简介学习数据结构的第一个程序,手写实现顺序表。实现功能创建表清空表中元素判断表中数据是否为空求表中有效数据长度指定数据元素定位指定位置插入元素释放空间打印顺序表的内容删除指定位置上的元素二、完整代码sqlist.h#ifndef__SQLIST_H#define__SQLIST......
  • java中ConcurrentHashMap底层原理 - 面试宝典
    ConcurrentHashMap是Java中的线程安全的哈希表实现。它通过使用分段锁(Segment)来实现并发访问的高效率。下面是ConcurrentHashMap的底层原理:数据结构:ConcurrentHashMap内部由一个Segment数组和若干个哈希桶(HashEntry)组成。每个Segment包含一个哈希桶数组和一个共享的锁。每个哈希桶......
  • 面试互联网企业的经验
    持续更新中......因为博主今年研二,目前是8月初,正在找工作,所以在这里分享总结一下自己面试得到的经验。(因为学校较好,所以一般简历不会挂我,大小厂都会给机会...)1.不要紧张,语速放慢从我的两段大厂实习经历来看,大厂的员工从学历到能力方面,和自己的差距其实不是特......