10 个常见的前端手写功能,你全都会吗?
万丈高楼平地起,地基打的牢,才能永远立于不败之地。今天给大家带来的是 10 个常见的 JavaScript 手写功能,重要的地方已添加注释。有的是借鉴别人的,有的是自己写的,如有不正确的地方,欢迎多多指正。
1、防抖 function debounce(fn, delay) {let timerreturn function (...args) {if (timer) {clearTimeout(timer)}timer = setTimeout(() => {fn.apply(this, args)}, delay)}}
// 测试 function task() {console.log('run task')}const debounceTask = debounce(task, 1000)window.addEventListener('scroll', debounceTask)复制代码 2、节流 function throttle(fn, delay) {let last = 0 // 上次触发时间 return (...args) => {const now = Date.now()if (now - last > delay) {last = nowfn.apply(this, args)}}}
// 测试 function task() {console.log('run task')}const throttleTask = throttle(task, 1000)window.addEventListener('scroll', throttleTask)复制代码 3、深拷贝 function deepClone(obj, cache = new WeakMap()) {if (typeof obj !== 'object') return obj // 普通类型,直接返回 if (obj === null) return objif (cache.get(obj)) return cache.get(obj) // 防止循环引用,程序进入死循环 if (obj instanceof Date) return new Date(obj)if (obj instanceof RegExp) return new RegExp(obj)
// 找到所属原型上的 constructor,所属原型上的 constructor 指向当前对象的构造函数 let cloneObj = new obj.constructor()cache.set(obj, cloneObj) // 缓存拷贝的对象,用于处理循环引用的情况 for (let key in obj) {if (obj.hasOwnProperty(key)) {cloneObj[key] = deepClone(obj[key], cache) // 递归拷贝}}return cloneObj}
// 测试 const obj = { name: 'Jack', address: { x: 100, y: 200 } }obj.a = obj // 循环引用 const newObj = deepClone(obj)console.log(newObj.address === obj.address) // false 复制代码 4、实现 Promiseclass MyPromise {constructor(executor) { // executor 执行器 this.status = 'pending' // 等待状态 this.value = null // 成功或失败的参数 this.fulfilledCallbacks = [] // 成功的函数队列 this.rejectedCallbacks = [] // 失败的函数队列 const that = thisfunction resolve(value) { // 成功的方法 if (that.status === 'pending') {that.status = 'resolved'that.value = valuethat.fulfilledCallbacks.forEach(myFn => myFn(that.value)) //执行回调方法}}function reject(value) { //失败的方法 if (that.status === 'pending') {that.status = 'rejected'that.value = valuethat.rejectedCallbacks.forEach(myFn => myFn(that.value)) //执行回调方法}}try {executor(resolve, reject)} catch (err) {reject(err)}}then(onFulfilled, onRejected) {if (this.status === 'pending') {// 等待状态,添加回调函数到成功的函数队列 this.fulfilledCallbacks.push(() => {onFulfilled(this.value)})// 等待状态,添加回调函数到失败的函数队列 this.rejectedCallbacks.push(() => {onRejected(this.value)})}if (this.status === 'resolved') { // 支持同步调用 console.log('this', this)onFulfilled(this.value)}if (this.status === 'rejected') { // 支持同步调用 onRejected(this.value)}}}
// 测试 function fn() {return new MyPromise((resolve, reject) => {setTimeout(() => {if(Math.random() > 0.6) {resolve(1)} else {reject(2)}}, 1000)})}fn().then(res => {console.log('res', res) // res 1},err => {console.log('err', err) // err 2})复制代码 5、异步控制并发数 function limitRequest(urls = [], limit = 3) {return new Promise((resolve, reject) => {const len = urls.lengthlet count = 0 // 当前进行到第几个任务
})}
// 测试 limitRequest(['http://xxa', 'http://xxb', 'http://xxc', 'http://xxd', 'http://xxe'])复制代码 6、ES5 继承(寄生组合继承)function Parent(name) {this.name = name}Parent.prototype.eat = function () {console.log(this.name + ' is eating')}
function Child(name, age) {Parent.call(this, name)this.age = age}Child.prototype = Object.create(Parent.prototype)Child.prototype.contructor = ChildChild.prototype.study = function () {console.log(this.name + ' is studying')}
// 测试 let child = new Child('xiaoming', 16)console.log(child.name) // xiaomingchild.eat() // xiaoming is eatingchild.study() // xiaoming is studying 复制代码 7、数组排序 sort 排序// 对数字进行排序,简写 const arr = [3, 2, 4, 1, 5]arr.sort((a, b) => a - b)console.log(arr) // [1, 2, 3, 4, 5]
// 对字母进行排序,简写 const arr = ['b', 'c', 'a', 'e', 'd']arr.sort()console.log(arr) // ['a', 'b', 'c', 'd', 'e']复制代码冒泡排序 function bubbleSort(arr) {let len = arr.lengthfor (let i = 0; i < len - 1; i++) {// 从第一个元素开始,比较相邻的两个元素,前者大就交换位置 for (let j = 0; j < len - 1 - i; j++) {if (arr[j] > arr[j + 1]) {let num = arr[j]arr[j] = arr[j + 1]arr[j + 1] = num}}// 每次遍历结束,都能找到一个最大值,放在数组最后}return arr}
//测试 console.log(bubbleSort([2, 3, 1, 5, 4])) // [1, 2, 3, 4, 5]复制代码 8、数组去重 Set 去重 cosnt newArr = [...new Set(arr)]复制代码 Array.from 去重 const newArr = Array.from(new Set(arr))复制代码 indexOf 去重 function resetArr(arr) {let res = []arr.forEach(item => {if (res.indexOf(item) === -1) {res.push(item)}})return res}
// 测试 const arr = [1, 1, 2, 3, 3]console.log(resetArr(arr)) // [1, 2, 3]复制代码 9、获取 url 参数 URLSearchParams 方法// 创建一个 URLSearchParams 实例 const urlSearchParams = new URLSearchParams(window.location.search);// 把键值对列表转换为一个对象 const params = Object.fromEntries(urlSearchParams.entries());复制代码 split 方法 function getParams(url) {const res = {}if (url.includes('?')) {const str = url.split('?')[1]const arr = str.split('&')arr.forEach(item => {const key = item.split('=')[0]const val = item.split('=')[1]res[key] = decodeURIComponent(val) // 解码})}return res}
// 测试 const user = getParams('http://www.baidu.com?user=%E9%98%BF%E9%A3%9E&age=16')console.log(user) // { user: '阿飞', age: '16' }复制代码 10、事件总线 | 发布订阅模式 class EventEmitter {constructor() {this.cache = {}}
on(name, fn) {if (this.cache[name]) {this.cache[name].push(fn)} else {this.cache[name] = [fn]}}
off(name, fn) {const tasks = this.cache[name]if (tasks) {const index = tasks.findIndex((f) => f === fn || f.callback === fn)if (index >= 0) {tasks.splice(index, 1)}}}
emit(name, once = false) {if (this.cache[name]) {// 创建副本,如果回调函数内继续注册相同事件,会造成死循环 const tasks = this.cache[name].slice()for (let fn of tasks) {fn();}if (once) {delete this.cache[name]}}}}
// 测试 const eventBus = new EventEmitter()const task1 = () => { console.log('task1'); }const task2 = () => { console.log('task2'); }
eventBus.on('task', task1)eventBus.on('task', task2)eventBus.off('task', task1)setTimeout(() => {eventBus.emit('task') // task2}, 1000)复制代码以上就是工作或求职中最常见的手写功能,你是不是全都掌握了呢,欢迎在评论区交流。如果文章对你有所帮助,不要忘了点上宝贵的一赞!
听说点赞的人运气都不差,相信来年第一个升职加薪的一定是你~😃
最后如果你觉得此文对你有一丁点帮助,点个赞。或者可以加入我的开发交流群:1025263163 相互学习,我们会有专业的技术答疑解惑
如果你觉得这篇文章对你有点用的话,麻烦请给我们的开源项目点点 star: https://gitee.com/ZhongBangKeJi/CRMEB不胜感激 !
评论