分享10個常見的JavaScript前端手寫功能
1、防抖
function debounce(fn, delay) { ? let timer ? return 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、節(jié)流
function throttle(fn, delay) { ? let last = 0 // 上次觸發(fā)時間 ? return (...args) => { ? ? const now = Date.now() ? ? if (now - last > delay) { ? ? ? last = now ? ? ? fn.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 (obj === null || typeof obj !== 'object') return obj ? if (obj instanceof Date) return new Date(obj) ? if (obj instanceof RegExp) return new RegExp(obj) ?? ? if (cache.get(obj)) return cache.get(obj) // 如果出現(xiàn)循環(huán)引用,則返回緩存的對象,防止遞歸進入死循環(huán) ? let cloneObj = new obj.constructor() // 使用對象所屬的構造函數(shù)創(chuàng)建一個新對象 ? cache.set(obj, cloneObj) // 緩存對象,用于循環(huán)引用的情況 ? 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 // 循環(huán)引用 const newObj = deepClone(obj) console.log(newObj.address === obj.address) // false
4、手寫 Promise
class MyPromise { ? constructor(executor) { ? ? this.status = 'pending' // 初始狀態(tài)為等待 ? ? this.value = null // 成功的值 ? ? this.reason = null // 失敗的原因 ? ? this.onFulfilledCallbacks = [] // 成功的回調函數(shù)存放的數(shù)組 ? ? this.onRejectedCallbacks = [] // 失敗的回調函數(shù)存放的數(shù)組 ? ? let resolve = value => { ? ? ? if (this.status === 'pending') { ? ? ? ? this.status = 'fulfilled' ? ? ? ? this.value = value; ? ? ? ? this.onFulfilledCallbacks.forEach(fn => fn()) // 調用成功的回調函數(shù) ? ? ? } ? ? } ? ? let reject = reason => { ? ? ? if (this.status === 'pending') { ? ? ? ? this.status = 'rejected' ? ? ? ? this.reason = reason ? ? ? ? this.onRejectedCallbacks.forEach(fn => fn()) // 調用失敗的回調函數(shù) ? ? ? } ? ? }; ? ? try { ? ? ? executor(resolve, reject) ? ? } catch (err) { ? ? ? reject(err) ? ? } ? } ? then(onFulfilled, onRejected) { ? ? // onFulfilled如果不是函數(shù),則修改為函數(shù),直接返回value ? ? onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value ? ? // onRejected如果不是函數(shù),則修改為函數(shù),直接拋出錯誤 ? ? onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err } ? ? return new MyPromise((resolve, reject) => { ? ? ? if (this.status === 'fulfilled') { ? ? ? ? setTimeout(() => { ? ? ? ? ? try { ? ? ? ? ? ? let x = onFulfilled(this.value); ? ? ? ? ? ? x instanceof MyPromise ? x.then(resolve, reject) : resolve(x) ? ? ? ? ? } catch (err) { ? ? ? ? ? ? reject(err) ? ? ? ? ? } ? ? ? ? }) ? ? ? } ? ? ? if (this.status === 'rejected') { ? ? ? ? setTimeout(() => { ? ? ? ? ? try { ? ? ? ? ? ? let x = onRejected(this.reason) ? ? ? ? ? ? x instanceof MyPromise ? x.then(resolve, reject) : resolve(x) ? ? ? ? ? } catch (err) { ? ? ? ? ? ? reject(err) ? ? ? ? ? } ? ? ? ? }) ? ? ? } ? ? ? if (this.status === 'pending') { ? ? ? ? this.onFulfilledCallbacks.push(() => { // 將成功的回調函數(shù)放入成功數(shù)組 ? ? ? ? ? setTimeout(() => { ? ? ? ? ? ? let x = onFulfilled(this.value) ? ? ? ? ? ? x instanceof MyPromise ? x.then(resolve, reject) : resolve(x) ? ? ? ? ? }) ? ? ? ? }) ? ? ? ? this.onRejectedCallbacks.push(() => { // 將失敗的回調函數(shù)放入失敗數(shù)組 ? ? ? ? ? setTimeout(() => { ? ? ? ? ? ? let x = onRejected(this.reason) ? ? ? ? ? ? x instanceof MyPromise ? x.then(resolve, reject) : resolve(x) ? ? ? ? ? }) ? ? ? ? }) ? ? ? } ? ? }) ? } } // 測試 function p1() { ? return new MyPromise((resolve, reject) => { ? ? setTimeout(resolve, 1000, 1) ? }) } function p2() { ? return new MyPromise((resolve, reject) => { ? ? setTimeout(resolve, 1000, 2) ? }) } p1().then(res => { ? console.log(res) // 1 ? return p2() }).then(ret => { ? console.log(ret) // 2 })
5、異步控制并發(fā)數(shù)
function limitRequest(urls = [], limit = 3) { ? return new Promise((resolve, reject) => { ? ? const len = urls.length ? ? let count = 0 ? ? // 同時啟動limit個任務 ? ? while (limit > 0) { ? ? ? start() ? ? ? limit -= 1 ? ? } ? ? function start() { ? ? ? const url = urls.shift() // 從數(shù)組中拿取第一個任務 ? ? ? if (url) { ? ? ? ? axios.post(url).then(res => { ? ? ? ? ? // todo ? ? ? ? }).catch(err => { ? ? ? ? ? // todo ? ? ? ? }).finally(() => { ? ? ? ? ? if (count == len - 1) { ? ? ? ? ? ? // 最后一個任務完成 ? ? ? ? ? ? resolve() ? ? ? ? ? } else { ? ? ? ? ? ? // 完成之后,啟動下一個任務 ? ? ? ? ? ? count++ ? ? ? ? ? ? start() ? ? ? ? ? } ? ? ? ? }) ? ? ? } ? ? } ? }) } // 測試 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 = Child // 測試 let xm = new Child('xiaoming', 12)? console.log(xm.name) // xiaoming console.log(xm.age) // 12 xm.eat() // xiaoming is eating
ES6繼承:
class Parent { ? constructor(name) { ? ? this.name = name ? } ? eat() { ? ? console.log(this.name + ' is eating') ? } } class Child extends Parent { ? constructor(name, age) { ? ? super(name) ? ? this.age = age ? } } // 測試 let xm = new Child('xiaoming', 12)? console.log(xm.name) // xiaoming console.log(xm.age) // 12 xm.eat() // xiaoming is eating
7、數(shù)組排序
sort 排序:
// 對數(shù)字進行排序,簡寫 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.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 ? ? ? } ? ? } ? ? // 每次遍歷結束,都能找到一個最大值,放在數(shù)組最后 ? } ? return arr } //測試 console.log(bubbleSort([2, 3, 1, 5, 4])) // [1, 2, 3, 4, 5]
8、數(shù)組去重
Set 去重:
const newArr = [...new Set(arr)] // 或 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 參數(shù)
URLSearchParams 方法:
// 創(chuàng)建一個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、事件總線 | 發(fā)布訂閱模式
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]) { ? ? ? // 創(chuàng)建副本,如果回調函數(shù)內繼續(xù)注冊相同事件,會造成死循環(huán) ? ? ? 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)
以上就是工作或求職中最常見的手寫功能
到此這篇關于分享10個常見的前端手寫功能的文章就介紹到這了,更多相關端手寫功能內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
JS實現(xiàn)線性表的鏈式表示方法示例【經(jīng)典數(shù)據(jù)結構】
這篇文章主要介紹了JS實現(xiàn)線性表的鏈式表示方法,簡單講解了線性表鏈式表示的原理并結合實例形式分析了js針對線性表鏈式表示的創(chuàng)建、插入、刪除等節(jié)點操作技巧,需要的朋友可以參考下2017-04-04仿google adsense顏色選擇器代碼,從中易廣告聯(lián)盟程序提取
仿google adsense顏色選擇器代碼,從中易廣告聯(lián)盟程序提取...2007-11-11利用uni-app和uView實現(xiàn)多圖上傳功能全過程
最近在使用uniapp開發(fā)的微信小程序中使用了圖片上傳功能,下面這篇文章主要給大家介紹了關于利用uni-app和uView實現(xiàn)多圖上傳功能的相關資料,文中通過實例代碼介紹的非常詳細,需要的朋友可以參考下2023-03-03Json對象和字符串互相轉換json數(shù)據(jù)拼接和JSON使用方式詳細介紹(小結)
JSON(JavaScript Object Notation) 是一種輕量級的數(shù)據(jù)交換格式.這篇文章主要介紹了Json對象和字符串互相轉換json數(shù)據(jù)拼接和JSON使用方式詳細介紹(小結)的相關資料,需要的朋友可以參考下2016-10-10