前端面试经典手写题

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 = 0Array.from(iterator).forEach((item, index) => {Promise.resolve(item).then(data => {ret[index] = datacount++if(count === iterator.length) {resolve(ret)}}, reject)})})
}// 方法返回一个Promise实例,一旦迭代器中的某个 promise 完成(resolved)或失败(rejected),返回的 promise 就会 resolve 或 rejectPromise2.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.prototypeobj.__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
// 终止态:返回值已等于请求数,执行cbclass 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.limitthis.isRequest = falsethis.queue = []}add(fn) {this.queue.push(fn)if(!this.isRequest) {this.request()}}request(end) {this.isRequest = trueend = end || this.limitconst 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.lengthlet 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 targetreturn target !== null && (type === 'object' || type === 'function')
}function getType(target) {return Object.prototype.toString.call(target)
}function getInit(target) {const Ctor = target.constructorreturn 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: constructorconst 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 targetconst type = getType(target)let cloneTargetif(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}// 克隆mapif (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;}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/25218.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

软件外包开发的GO开发框架

近些年GO语言使用的越来越多&#xff0c;尤其是在web应用开发和高性能服务器的项目里。在开发新项目时掌握一些常用的开发框架可以节省开发时间提高工作效率&#xff0c;也是对软件开发人员基本的技能要求。今天和大家分享一些常见的GO语言开发框架&#xff0c;希望对大家有所帮…

基于SPSSPRO实现层次分析法(AHP)

层次分析法&#xff0c;简称AHP&#xff0c;是指将与决策总是有关的元素分解成目标、准则、方案等层次&#xff0c;在此基础之上进行定性和定量分析的决策方法。&#xff08;摘自百度百科&#xff09; 层次分析法有着广泛使用&#xff0c;涉及到的平台也多种多样&#xff0c;今…

相机传感器格式与镜头光圈参数

相机靶面大小 CCD/CMOS图像传感器尺寸&#xff08;sensor format&#xff09;1/2’‘、1/3’‘、1/4’实际是多大 1英寸——靶面尺寸为宽12.7mm*高9.6mm&#xff0c;对角线16mm。 2/3英寸——靶面尺寸为宽8.8mm*高6.6mm&#xff0c;对角线11mm。 1/2英寸——靶面尺寸为宽6.…

安装CUDA与CUDNN与Pytorch(最新超级详细图文版本2023年8月最新)

一、安装CUDA 1.1、下载安装包 cuda可以认为就是Nvidia为了显卡炼丹搞的一个软件&#xff0c;其下载地址为&#xff1a;CUDA Toolkit 12.2 Update 1 Downloads | NVIDIA Developer 当你点进这个链接的时候&#xff0c;你需要依次选择 1是选择系统&#xff0c;这里选windows…

C++派生类的构造函数

1.构造函数 定义了派生类之后&#xff0c;要使用派生类就需要声明该类的对象。对象在使用之前必须初始化。 派生类的成员对象是由所有基类的成员对象共同组成的。因此构造派生类函数的对象时&#xff0c;就要对基类的成员对象和新增的成员对象进行初始化。 基类的构造函数并…

RabbitMQ消息队列

目录 网址&#xff1a; 一、项目准备 1.导入依赖 2.抽取工具类 配置的属性在哪里呢 二、代码编写 1.简单模式 生产者 消费者 2.Work queues工作队列模式 生产者 消费者1 消费者2 3.Publish/Subscribe发布与订阅模式 生产者 消费者1 消费者2 4.Routing路由模式…

git【潦草学习】

初始配置git 查询版本号 初次使用git前配置用户名与邮箱地址 git config --global user.name "your name" git config --global user.email "your email" git config -l 发现最后两行多出了用户名和邮箱&#xff0c;说明配置成功

【雕爷学编程】Arduino动手做(184)---快餐盒盖,极低成本搭建机器人实验平台3

吃完快餐粥&#xff0c;除了粥的味道不错之外&#xff0c;我对个快餐盒的圆盖子产生了兴趣&#xff0c;能否做个极低成本的简易机器人呢&#xff1f;也许只需要二十元左右 知识点&#xff1a;轮子&#xff08;wheel&#xff09; 中国词语。是用不同材料制成的圆形滚动物体。简…

解决Map修改key的问题

需求 现在返回json数据带有分页的数据&#xff0c;将返回data属性数据变更为content&#xff0c;数据不变&#xff0c;key发生变化 实现1&#xff0c;源数据比较复杂&#xff0c;组装数据比较麻烦 说明&#xff1a;如果使用这种方式完成需求&#xff0c;需要创建对象&#xff0…

VLAN原理+配置

目录 一&#xff0c; 以太网二层交换机 二&#xff0c;三层架构&#xff1a; 三&#xff0c;VLAN配置思路 1.创建vlan 2.接口划入vlan 3.trunk干道 4.vlan间路由器 5.DHCP池塘配置 四&#xff0c;华为VLAN部分的接口模式讲解&#xff1a; 五&#xff0c;华为VLAN部分的…

mysql二进制方式升级8.0.34

一、概述 mysql8.0.33 存在如下高危漏洞&#xff0c;需要通过升级版本修复漏洞 Oracle MySQL Cluster 安全漏洞(CVE-2023-0361) mysql/8.0.33 Apache Skywalking <8.3 SQL注入漏洞 二、查看mysql版本及安装包信息 [rootlocalhost mysql]# mysql -V mysql Ver 8.0.33 fo…

Eureka增加账号密码认证登录

一、业务背景 注册中心Eureka在微服务开发中经常使用到&#xff0c;用来管理发布的微服务&#xff0c;供前端或者外部调用。但是如果放到生产环境&#xff0c;我们直接通过URL访问的话&#xff0c;这显然是不安全的。 所以需要给注册中心加上登录认证。 通过账号和密码认证进行…

HOT76-数据流的中位数

leetcode原题链接&#xff1a;数据流的中位数 题目描述 中位数是有序整数列表中的中间值。如果列表的大小是偶数&#xff0c;则没有中间值&#xff0c;中位数是两个中间值的平均值。 例如 arr [2,3,4] 的中位数是 3 。例如 arr [2,3] 的中位数是 (2 3) / 2 2.5 。 实现 …

K8s(健康检查+滚动更新+优雅停机+弹性伸缩+Prometheus监控+配置分离)

前言 快速配置请直接跳转至汇总配置 K8s SpringBoot实现零宕机发布&#xff1a;健康检查滚动更新优雅停机弹性伸缩Prometheus监控配置分离&#xff08;镜像复用&#xff09; 配置 健康检查 健康检查类型&#xff1a;就绪探针&#xff08;readiness&#xff09; 存活探针&am…

D3JS教程_编程入门自学教程_菜鸟教程-免费教程分享

教程简介 D3是Data-Driven Documents的缩写&#xff0c;D3.js是一个基于数据管理文档的资源JavaScript库。 D3 是最有效的数据可视化框架之一。它允许开发人员在 HTML、CSS 和 SVG 的帮助下在浏览器中创建动态的交互式数据可视化。数据可视化是将过滤后的数据以图片和图形的形…

Ubuntu下nvidia-smi失败,使用dkms解决

Ubuntu下nvidia-smi失败&#xff0c;使用dkms解决 错误信息 nvidia-smi显示无法与驱动通讯 NVIDIA-SMI has failed because it couldnt communicate with the NVIDIA driver. Make sure that the latest NVIDIA driver is installed and running.原因 一般来说是因为机器重…

useEffect中的函数会执行2次原因

一、useEffect介绍 useEffect是React18的新特性&#xff0c;表示React的生命周期Hooks组件。等价于Claas组件的componentDidMount、componentDidUpdate&#xff0c;useEffect的返回函数等价于componentWillUnmount。&#xff08;组件卸载、重新挂载都会触发这个函数&#xff0c…

【计算机网络】socket编程

文章目录 1. 网络通信的理解2.进程PID可以取代端口号吗&#xff1f;3. 认识TCP协议4. 认识 UDP协议5. socket编程接口udp_server.hpp的代码解析socket——创建 socket 文件描述符Initserver——初始化1.创建套接字接口&#xff0c;打开网络文件bind——绑定的使用 2.给服务器指…

[webpack] 基本配置 (一)

文章目录 1.基本介绍2.功能介绍3.简单使用3.1 文件目录和内容3.2 下载依赖3.3 启动webpack 4.基本配置4.1 五大核心概念4.2 基本使用 1.基本介绍 Webpack 是一个静态资源打包工具。它会以一个或多个文件作为打包的入口, 将我们整个项目所有文件编译组合成一个或多个文件输出出去…

webpack基础知识八:说说如何借助webpack来优化前端性能?

一、背景 随着前端的项目逐渐扩大&#xff0c;必然会带来的一个问题就是性能 尤其在大型复杂的项目中&#xff0c;前端业务可能因为一个小小的数据依赖&#xff0c;导致整个页面卡顿甚至奔溃 一般项目在完成后&#xff0c;会通过webpack进行打包&#xff0c;利用webpack对前…