10个常见的前端手写功能
- 1、防抖
- 2、节流
- 3、深拷贝
-
- JSON方法
- 递归拷贝
- 4、手写Promise
- 5、异步控制并发数
- 6、继承
-
- ES5 继承(寄生组合继承)
- ES6继承
- 7、数组排序
-
- sort排序
- 冒泡排序
- 8、数组去重
-
- Set去重
- indexOf去重
- 9、获取 url 参数
-
- URLSearchParams 方法
- split 方法
- 10、发布订阅模式
1、防抖
触发高频事件后 n 秒后函数只会执行一次,如果 n 秒内高频事件再次被触发,则重新计算时间;
function debouncefn, delay) {let timerreturn function ...args) {if timer) {clearTimeouttimer)}timer = setTimeout) => {fn.applythis, args)}, delay)}
}// 测试
function task) {console.log'run task')
}
const debounceTask = debouncetask, 1000)
window.addEventListener'scroll', debounceTask)
2、节流
高频事件触发,但在 n 秒内只会执行一次,所以节流会稀释函数的执行频率。
function throttlefn, delay) {let last = 0 // 上次触发时间return function ...args) {const now = Date.now)if now - last > delay) {last = nowfn.applythis, args)}}
}// 测试
function task) {console.log'run task')
}
const throttleTask = throttletask, 1000)
window.addEventListener'scroll', throttleTask)
3、深拷贝
JSON方法
// 不支持值为undefined、函数和循环引用的情况
const cloneObj = JSON.parseJSON.stringifyobj))
递归拷贝
function deepCloneobj, cache = new WeakMap)) {if obj === null || typeof obj !== 'object') return objif obj instanceof Date) return new Dateobj)if obj instanceof RegExp) return new RegExpobj)if cache.hasobj)) return cache.getobj) // 如果出现循环引用,则返回缓存的对象,防止递归进入死循环let cloneObj = new obj.constructor) // 使用对象所属的构造函数创建一个新对象cache.setobj, cloneObj) // 缓存对象,用于循环引用的情况for let key in obj) {if obj.hasOwnPropertykey)) {cloneObj[key] = deepCloneobj[key], cache) // 递归拷贝}}return cloneObj
}// 测试
const obj = { name: 'Jack', address: { x: 100, y: 200 } }
obj.a = obj // 循环引用
const newObj = deepCloneobj)
console.lognewObj.address === obj.address) // false
4、手写Promise
class MyPromise {constructorexecutor) {this.status = 'pending' // 初始状态为等待this.value = null // 成功的值this.reason = null // 失败的原因this.onFulfilledCallbacks = [] // 成功的回调函数数组this.onRejectedCallbacks = [] // 失败的回调函数数组let resolve = value => {if this.status === 'pending') {this.status = 'fulfilled'this.value = value;this.onFulfilledCallbacks.forEachfn => fn)) // 调用成功的回调函数}}let reject = reason => {if this.status === 'pending') {this.status = 'rejected'this.reason = reasonthis.onRejectedCallbacks.forEachfn => fn)) // 调用失败的回调函数}};try {executorresolve, reject)} catch err) {rejecterr)}}thenonFulfilled, onRejected) {return new MyPromiseresolve, reject) => {if this.status === 'fulfilled') {setTimeout) => {const x = onFulfilledthis.value);x instanceof MyPromise ? x.thenresolve, reject) : resolvex)})}if this.status === 'rejected') {setTimeout) => {const x = onRejectedthis.reason)x instanceof MyPromise ? x.thenresolve, reject) : resolvex)})}if this.status === 'pending') {this.onFulfilledCallbacks.push) => { // 将成功的回调函数放入成功数组setTimeout) => {const x = onFulfilledthis.value)x instanceof MyPromise ? x.thenresolve, reject) : resolvex)})})this.onRejectedCallbacks.push) => { // 将失败的回调函数放入失败数组setTimeout) => {const x = onRejectedthis.reason)x instanceof MyPromise ? x.thenresolve, reject) : resolvex)})})}})}
}// 测试
function p1) {return new MyPromiseresolve, reject) => {setTimeoutresolve, 1000, 1)})
}
function p2) {return new MyPromiseresolve, reject) => {setTimeoutresolve, 1000, 2)})
}
p1).thenres => {console.logres) // 1return p2)
}).thenret => {console.logret) // 2
})
5、异步控制并发数
function limitRequesturls = [], limit = 3) {return new Promiseresolve, reject) => {const len = urls.lengthlet count = 0// 同时启动limit个任务while limit > 0) {start)limit -= 1}function start) {const url = urls.shift) // 从数组中拿取第一个任务if url) {axios.posturl).thenres => {// todo}).catcherr => {// todo}).finally) => {if count == len - 1) {// 最后一个任务完成resolve)} else {// 完成之后,启动下一个任务count++start)}})}}})
}// 测试
limitRequest['http://xxa', 'http://xxb', 'http://xxc', 'http://xxd', 'http://xxe'])
6、继承
ES5 继承(寄生组合继承)
function Parentname) {this.name = name
}
Parent.prototype.eat = function ) {console.logthis.name + ' is eating')
}function Childname, age) {Parent.callthis, name)this.age = age
}
Child.prototype = Object.createParent.prototype)
Child.prototype.constructor = Child// 测试
let xm = new Child'xiaoming', 12)
console.logxm.name) // xiaoming
console.logxm.age) // 12
xm.eat) // xiaoming is eating
ES6继承
class Parent {constructorname) {this.name = name}eat) {console.logthis.name + ' is eating')}
}class Child extends Parent {constructorname, age) {supername)this.age = age}
}// 测试
let xm = new Child'xiaoming', 12)
console.logxm.name) // xiaoming
console.logxm.age) // 12
xm.eat) // xiaoming is eating
7、数组排序
sort排序
// 对数字进行排序,简写
const arr = [3, 2, 4, 1, 5]
arr.sorta, b) => a - b)
console.logarr) // [1, 2, 3, 4, 5]// 对字母进行排序,简写
const arr = ['b', 'c', 'a', 'e', 'd']
arr.sort)
console.logarr) // ['a', 'b', 'c', 'd', 'e']
冒泡排序
function bubbleSortarr) {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.logbubbleSort[2, 3, 1, 5, 4])) // [1, 2, 3, 4, 5]
8、数组去重
Set去重
const newArr = [...new Setarr)]
// 或
const newArr = Array.fromnew Setarr))
indexOf去重
const newArr = arr.filteritem, index) => arr.indexOfitem) === index)
9、获取 url 参数
URLSearchParams 方法
// 创建一个URLSearchParams实例
const urlSearchParams = new URLSearchParamswindow.location.search);
// 把键值对列表转换为一个对象
const params = Object.fromEntriesurlSearchParams.entries));
split 方法
function getParamsurl) {const res = {}if url.includes'?')) {const str = url.split'?')[1]const arr = str.split'&')arr.forEachitem => {const key = item.split'=')[0]const val = item.split'=')[1]res[key] = decodeURIComponentval) // 解码})}return res
}// 测试
const user = getParams'http://www.baidu.com?user=%E9%98%BF%E9%A3%9E&age=16')
console.loguser) // { user: '阿飞', age: '16' }
10、发布订阅模式
class EventEmitter {constructor) {this.cache = {}}onname, fn) {if this.cache[name]) {this.cache[name].pushfn)} else {this.cache[name] = [fn]}}offname, fn) {const tasks = this.cache[name]if tasks) {const index = tasks.findIndexf) => f === fn || f.callback === fn)if index >= 0) {tasks.spliceindex, 1)}}}emitname, 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)
转载于:https://juejin.cn/post/7031322059414175774