10個常見的前端手寫功能,你全都會嗎?
theme: channing-cyan highlight: atom-one-light
萬丈高樓平地起,地基打的牢,才能永遠立於不敗之地。今天給大家帶來的是10個常見的 JavaScript 手寫功能,重要的地方已添加註釋。有的是借鑑別人的,有的是自己寫的,如有不正確的地方,歡迎多多指正。
1、防抖
```js function debounce(fn, delay) { let timer return function (...args) { if (timer) { clearTimeout(timer) } timer = setTimeout(() => { fn.apply(this, args) }, delay) } }
// 測試 const task = () => { console.log('run task') } const debounceTask = debounce(task, 1000) window.addEventListener('scroll', debounceTask) ```
2、節流
```js function throttle(fn, delay) { let last = 0 // 上次觸發時間 return (...args) => { const now = Date.now() if (now - last > delay) { last = now fn.apply(this, args) } } }
// 測試 const task = () => { console.log('run task') } const throttleTask = throttle(task, 1000) window.addEventListener('scroll', throttleTask) ```
3、深拷貝
```js function deepClone(obj, cache = new WeakMap()) { if (typeof obj !== 'object') return obj // 普通類型,直接返回 if (obj === null) return obj if (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、實現 Promise
```js class MyPromise { constructor(executor) { // executor執行器 this.status = 'pending' // 等待狀態 this.value = null // 成功或失敗的參數 this.fulfilledCallbacks = [] // 成功的函數隊列 this.rejectedCallbacks = [] // 失敗的函數隊列 const that = this function resolve(value) { // 成功的方法 if (that.status === 'pending') { that.status = 'resolved' that.value = value that.fulfilledCallbacks.forEach(myFn => myFn(that.value)) //執行回調方法 } } function reject(value) { //失敗的方法 if (that.status === 'pending') { that.status = 'rejected' that.value = value that.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、異步控制併發數
```js function limitRequest(urls = [], limit = 5) { return new Promise((resolve, reject) => { const len = urls.length let count = 0 // 當前進行到第幾個任務
const start = async () => {
const url = urls.shift() // 從數組中拿取第一個任務
if (url) {
try {
await axios.post(url)
if (count == len - 1) {
// 最後一個任務
resolve()
} else {
count++
// 成功,啟動下一個任務
start()
}
} catch (e) {
count++
// 失敗,也啟動下一個任務
start()
}
}
}
// 啟動limit個任務
while (limit > 0) {
start()
limit -= 1
}
}) }
// 測試 limitRequest(['http://xxa', 'http://xxb', 'http://xxc', 'http://xxd', 'http://xxe']) ```
6、ES5繼承(組合繼承)
```js 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 = new Parent() // 原型鏈繼承 Child.prototype.contructor = Child Child.prototype.study = function () { console.log(this.name + ' is studying') }
// 測試 let child = new Child('xiaoming', 16) console.log(child.name) // xiaoming child.eat() // xiaoming is eating child.study() // xiaoming is studying ```
7、數組排序
sort 排序
```js // 對數字進行排序,簡寫 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((a, b) => { if (a > b) return 1 else if (a < b) return -1 else return 0 }) console.log(arr) // ['a', 'b', 'c', 'd', 'e'] ```
冒泡排序
```js function bubbleSort(arr) { let len = arr.length for (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 去重
js
cosnt newArr = [...new Set(arr)]
Array.from 去重
js
const newArr = Array.from(new Set(arr))
indexOf 去重
```js 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參數
```js 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、事件總線 | 發佈訂閲模式
```js 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) ```
以上就是工作或求職中最常見的手寫功能,你是不是全都掌握了呢,歡迎在評論區交流。如果文章對你有所幫助,不要忘了點上寶貴的一讚!
聽説點讚的人運氣都不差,相信來年第一個升職加薪的一定是你~😃
- Vite 性能篇:掌握這些優化策略,一起縱享絲滑!
- Vite 配置篇:日常開發掌握這些配置就夠了!
- Vite 入門篇:學會它,一起提升開發幸福感!
- 還有人沒嘗過 Pinia 嗎,請收下這份食用指南!
- 10個常見的使用場景,助你從 Vue2 絲滑過渡到 Vue3 !
- 前端手寫功能第二彈,提升內力,等待下一次爆發!
- 效率提升之 —— 如何優雅的帶走上份工作的VSCode配置
- Vue組件遞歸——組件化開發的必備技能!
- 10個常用的JS工具庫,80%的項目都在用!
- 如何嚴格判斷文件上傳類型?再不會你就out啦!
- Element-UI 奇淫技巧第二彈!提升開發效率,延長摸魚時間~
- 原來虛擬列表如此簡單!萬萬沒想到
- 10個常見的前端手寫功能,你全都會嗎?
- 一款強大到沒朋友的圖片編輯插件,愛了愛了!
- 如何實現拖拽上傳、上傳進度條,以及取消上傳?
- Vue 1分鐘實現右鍵菜單,懶人的福音!
- Vue 如何快速實現頭像裁剪?方法比你想象的簡單
- 前端持久化之瀏覽器存儲技術(localStorage、sessionStorage 、session、cookies)