当前位置: 首页 > news >正文

刷刷前端手写题

闭包用途

闭包

闭包让你可以在一个内层函数中访问到其外层函数的作用域

防抖

描述

        前面所有触发都被取消,最后一次执行,在规定时间之后才会触发,也就是说如果连续快速的触发,用户操作频繁,但只会执行一次

        常用场景:输入框输入

代码实现

1、lodash的debounce函数

2、 当用户点击按钮时,debounce 包装的 getBtnValue 函数会延迟 3000 毫秒执行。如果在这 3000 毫秒内用户再次点击按钮,那么之前的定时器会被清除,重新开始计时。因此,getBtnValue 函数只会在用户停止点击 3000 毫秒后才执行

function debounce(fn,apply){let timer;// 初始状态下,timer是undefinedreturn function(){// 如果timer有值,清除之前的定时器if(timer) {clearTimeout(timer);}timer = setTimeout(()=>{fn.apply(this,args)},delay)} 
}
function getValue(e){console.log('1111')
}
const btn = document.createElement('button')
btn.innerHTML = 'btn'
document.body.appendChild(btn)
btn.onclick = debounce(getValue, 3000)

         浏览器环境:timer 会被赋值为一个整数,例如 1、2、3 等;Node.js 环境:timer 会被赋值为一个 Timeout 对象。

节流

描述

有规律执行,减少时间执行次数,拖放,滚屏;

只会在第一个点击时执行一次,后续点击将被忽略,直到 delay时间过去后才能再次执行

代码实现

        function throttle(func, delay) {let timer; // 用于保存定时器标识符return function() {if (timer) return; // 如果 timer 已经存在,说明在 delay 时间内已经触发过,直接返回,跳过本次调用const args = arguments; // 保存当前的参数const context = this; // 保存当前的执行上下文// 设置一个定时器,在 delay 毫秒后执行 functimer = setTimeout(() => {func.apply(context, args); // 执行原始函数,传递当前上下文和参数timer = null; // 重置 timer,表示可以再次触发 func}, delay);};}const btn = document.createElement('button')btn.innerHTML = 'btn'document.body.appendChild(btn)function handleClick() {console.log('Button clicked!');}btn.onclick = throttle(handleClick, 3000);
  • 点击第一次:创建 timer,设置 delay 毫秒后执行 func
  • delay 期间再次点击:由于 timer 存在,函数直接返回,不会再次执行 func
  • delay 时间到达func 被执行,timer 被重置为 null
  • 允许新的点击执行:可以再次创建新的 timer 并触发 func。

因此,尽管多次点击,只有第一次点击时创建的定时器会生效!!

函数柯里化

描述

使用多个参数的一个函数转换成一系列使用一个参数的函数的技术。

代码实现

function curry(fn){let args = []return function res(...rest){if(arguments.length==0){//当没有参数传递给 res 时于是调用 fn(...args)return fn(...args)}else{args.push(...rest)return res}}
}
var sumFn = function(...arr){return arr.reduce((prev,cur)=>{return prev+cur})
}
let res = curry(sumFn)(1)(2)(3,4)() //15

手写New

描述

  new 操作符的主要作用是生成一个新对象,并将这个对象与构造函数的原型连接起来,同时构造函数中的代码会在新对象的上下文中执行,给新对象赋予属性和方法。

主要流程是:const person1 = new Person('Alice', 25);

  • 新建一个对象const person1 = {};
  • 设置原型person1.__proto__ = Person.prototype;//隐式原型指向构造函数的显示原型
  • 绑定 this:执行 Person 构造函数时,this 被绑定到 person1
  • 执行构造函数:在 Person 函数中,this.name = name;name 赋值给 person1
  • 返回对象:如果没有显式返回对象,new 操作符会返回 person1

代码实现

function Person(a){// 检查 this 是否是 Person 的实例//if (!(this instanceof Person)) {//throw new Error("Person 只能通过 new 关键字调用");//}this.a=a
}
function myNew(fn,...args){const obj={};obj._proto_=fn.prototypefn.apply(obj,args)return obj
}
const obj = myNew(Person,123)

数组去重

描述

顾名思义:console.log(uniqueArray([1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5])); // [1, 2, 3, 4, 5, 6]

代码实现

const uniqueArray = (arr) => {return [...new Set(arr)]
}

实现正则切分千分位(js)

描述

顾名思义:console.log(formatThousands(123456789)); // 输出: 123,456,789

代码实现

function format(num){//将数字转为字符串并分割整数和小数部分let arr = String(num).split(.)let char = arr[0].split('').reverse()return char.reduce((pre,cur,curIndex)=>{if(curIndex+1) %3 ===0 && curIndex !==char.length -1){return ','+cur+pre}return cur+pre},"")    
}

手写call/apply

描述:通过使用call/apply方法,可以将一个对象的方法应用到另一个对象上,区别是传入参数一个是参数列表,一个是数组;都是立即执行

代码实现

Function.prototype.myCall = function(context,...args){if (typeof this!=="function"){throw new TypeError("被调用的必须是函数")}context||globalThis//用Symbol来创建唯一的fn,防止名字冲突const fn = Symbol('key')// this是调用myCall的函数test,将函数绑定到上下文对象的新属性上context[fn] = thisconst res =  context[fn](...args)//hello,worlddelete context[fn]return res
}const test = {name:"xxx",hello: function(){console.log(`hello,${this.name}!`);}
}
const obj = {name:"world"};
test.hello.myCall(obj);//hello,world

手写Bind

描述

与call和apply方法不同,bind方法并不会立即执行函数,而是返回一个新函数,可以稍后调用;

且参数可以分多次传入

代码实现

Function.prototype.myBind = function(thisArg,...args1){const fn = this;return function(...args2){if(typeof fn!=="function"){throw new TypeError("被调用的是函数")}thisArg = thisArg||globalThis;let uniqueFn = Symbol("fn")thisArg[uniqueFn] = fn;//合并参数并调用函数const res = thisArg[uniqueFn](...args1,...args2)delete thisArg[uniqueFn]return res}
}
function greet(param1,param2) {console.log(`${param1},${this.name}${param2}`)   
}
const boundGreet = greet.myBind(obj,"Hey");
boundGreet("!!")// "Hey, Alice!!"

扁平化

描述

将多维转为一维

代码实现--数组

遍历,检查是数组,递归,不是数组扔进去

let arr = [1, [2, 3], [4, [5, 6, [7, 8]]]];
function flatten(arr){let res = []let len = arr.lengthfor (let i=0;i<len;i++){if(Array.isArray(arr[i])){res.concat(flatten(arr[i]))}else{res.push(arr[i])}}return res
}

代码实现--对象

function flattenObj(){let res = {}for (let key in obj){if(typeof obj[key] ==='object'&& obj[key] !==null){flatten(res,obj[key],`${key}`)}else{res[key]=obj[key]}}function flatten(res,obj,keyname){for(let key in obj){if(typeof obj[key] ==='object'&& obj[key] !==null){flatten(res,obj[key],`${keyname}.${key}`)}else{res[`${keyname}.${key}`]=obj[key]}}}return res
}const obj = {a: 1,b: [1, 2, { c: true }],c: { e: 2, f: 3 },g: null,};
let res = flattenObj(obj)
结果:{a: 1,'b.0': 1,'b.1': 2,'b.2.c': true,'c.e': 2,'c.f': 3,g: null
}

模拟Promise.all()

描述

        入参是个Promise实例组成的数组,返回值是个promise,因为可以使用.then,如果全部成功,状态变为resolved, 并且返回值组成一个数组传给回调,但凡有一个失败,状态变为rejected, 并将error返回给回调

代码实现

// 添加一个自定义的静态方法
Promise.MyAll = function(promises){let arr = []count = 0return new Promise((resolve,reject)=>{promises.forEach((item,i)=>{//将 item 转换为一个 PromisePromise.resolve(item).then(res=>{arr[i]=rescount+=1if(count === promises.length) resolve(arr)}).catch(reject)})})
}

 测试

const p1 = Promise.resolve('p1')
const p2 = new Promise((resolve, reject) => {setTimeout(() => {resolve('p2')}, 500)
})
Promise.MyAll([p1, p2]).then(res => console.log(res))//['p1','p2'].catch(err => console.log(err))

模拟Promise.race()

描述

返回状态以最快的那个为准

代码实现

Promise.MyRace(promises=>{return new Promise((resolve,reject)=>{for(const x of promises){Promise.resolve(x).then(resolve,reject)}})
})

观察者模式

描述

        观察者模式定义了对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知,并自动更新

例如公司(被观察者)发中秋福利给员工(观察者)

代码实现

//被观察者
class Subject{constructor(){this.observerList = []}addObserver(observer){this.observerList.push(observer)}removeObserver(observer){const index = this.observerList.findIndex(o => o.name===observer.name)this.observerList.splice(index,1)}notifyObservers(message){const observers = this.observerList;observers.forEach(observer=>observer.notified(message))}
}
//观察者
class Observer{constructor(name,subject){this.name = name;if(subject){subject.addObserver(this)}}notified(message){console.log(this.name,message)}
}

 测试

const subject = new Subject();
const observerA = new Observer('observerA', subject);
subject.notifyObservers('Hello from subject');//observerA Hello from subject

常见使用场景 

        数据绑定机制通常采用观察者模式。当数据模型变化时,所有绑定了该数据的组件(观察者)都会自动更新。例如,一个购物车系统,当用户添加商品到购物车时,购物车总价会自动更新。这个过程可以通过观察者模式来实现

订阅者模式

描述

发布-订阅是一种消息范式,消息的发送者(发布者)不会将消息直接发送给特定的接收者(订阅者)。而是将发布的消息分为不同的类别,无需了解哪些订阅者(如果有的话)可能存在

例如:公司发快递给消费者

代码实现

class Publisher {constructor(name, context) {this.name = name; // 设置发布者的名称this.context = context; // 设置发布者所属的 PubSub 实例}publish(type, content) {this.context.publish(type, content); // 调用 PubSub 实例的 publish 方法,发布消息}
}class Subscriber {constructor(name, context) {this.name = name; // 设置订阅者的名称this.context = context; // 设置订阅者所属的 PubSub 实例}subscribe(type, cb) {this.context.subscribe(type, cb); // 调用 PubSub 实例的 subscribe 方法,订阅某个消息类型}
}class PubSub {constructor() {this.messages = {}; // 存储已发布的消息,按消息类型存储this.listeners = {}; // 存储订阅者回调函数,按消息类型存储}publish(type, content) {const existContent = this.messages[type]; // 检查是否已有该类型的消息if (!existContent) {this.messages[type] = []; // 如果没有,初始化一个数组来存储该类型的消息}this.messages[type].push(content); // 将消息内容添加到对应类型的消息数组中}subscribe(type, cb) {const existListener = this.listeners[type]; // 检查是否已有订阅者监听该类型的消息if (!existListener) {this.listeners[type] = []; // 如果没有,初始化一个数组来存储该类型的订阅者回调函数}this.listeners[type].push(cb); // 将订阅者的回调函数添加到对应类型的监听数组中}notify(type) {const messages = this.messages[type]; // 获取该类型的所有已发布的消息const subscribers = this.listeners[type] || []; // 获取该类型的所有订阅者回调函数subscribers.forEach((cb, index) => cb(messages[index])); // 将对应的消息传递给订阅者的回调函数}
}

 测试

const TYPE_A = 'music'; // 定义一个消息类型
const pubsub = new PubSub(); // 创建一个 PubSub 实例const publisherA = new Publisher('publisherA', pubsub); // 创建一个发布者,并与 PubSub 实例关联
publisherA.publish(TYPE_A, 'we are young'); // 发布者发布一条类型为 'music' 的消息,内容是 'we are young'const subscriberA = new Subscriber('subscriberA', pubsub); // 创建一个订阅者,并与 PubSub 实例关联
subscriberA.subscribe(TYPE_A, res => {console.log('subscriberA received', res); // 订阅者订阅 'music' 类型的消息,并定义回调函数处理接收到的消息
});pubsub.notify(TYPE_A); // 通知所有订阅了 'music' 类型的订阅者,调用他们的回调函数并传递消息

常见使用场景 

事件总线:不同组件之间不直接通信,而是通过事件总线来发布和订阅事件。这使得组件之间高度解耦,组件可以独立发展

手写vuex

描述

Vuex 是 Vue.js 的状态管理模式,主要解决组件之间共享状态时的问题

代码实现

http://www.lryc.cn/news/430209.html

相关文章:

  • 论文解读:LONGWRITER: UNLEASHING 10,000+ WORD GENERATION FROM LONG CONTEXT LLMS
  • 一文了解Ansible原理以及常见使用模块
  • JavaEE从入门到起飞(九) ~Activiti 工作流
  • 微服务的保护
  • 2024前端面试题-网络篇
  • 移情别恋c++ ദ്ദി˶ー̀֊ー́ ) ——6.vector
  • 设计模式---简单工厂模式
  • Vue | Vue 中的 refInForde 用法
  • 【原创】java+swing+mysql房屋租赁管理系统设计与实现
  • Django 中render、redirect 和 HttpResponse的区别
  • CRYPTO 2020
  • java 函数接口Consumer简介与示例【函数式编程】【Stream】
  • 黑神话:悟空-配置推荐
  • Android14 蓝牙设备类型修改
  • vue3 语法糖<script setup>
  • 微服务设计原则——高性能:异步与并发
  • 机器学习——决策树,朴素贝叶斯
  • C语言基础(十)
  • 人像比对-人证比对-人脸身份证比对-人脸身份证实名认证-人脸三要素对比-实人认证
  • Android 上下滑隐藏显示状态栏
  • USBCAN-II/II+使用方法以及qt操作介绍
  • 笔记-系统规划与管理师-案例题-2022年-IT服务部署实施
  • Kubernetes 清理资源常用的 Kubernetes 清理命
  • 【数据结构初阶】二叉树--基本概念
  • Pytorch添加自定义算子之(12)-开闭原则设计tensorrt和onnxruntime推理语义分割模型
  • 第二百零九节 Java格式 - Java数字格式类
  • LSI-9361阵列卡笔记
  • ArcGIS热点分析 (Getis-Ord Gi*)——基于地级市尺度的七普人口普查数据的热点与冷点分析
  • ASIACRYPT 2021
  • C#学习之路day1