分享10個(gè)常見的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ā)時(shí)間
? 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)引用,則返回緩存的對象,防止遞歸進(jìn)入死循環(huán)
? let cloneObj = new obj.constructor() // 使用對象所屬的構(gòu)造函數(shù)創(chuàng)建一個(gè)新對象
? 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) // false4、手寫 Promise
class MyPromise {
? constructor(executor) {
? ? this.status = 'pending' // 初始狀態(tài)為等待
? ? this.value = null // 成功的值
? ? this.reason = null // 失敗的原因
? ? this.onFulfilledCallbacks = [] // 成功的回調(diào)函數(shù)存放的數(shù)組
? ? this.onRejectedCallbacks = [] // 失敗的回調(diào)函數(shù)存放的數(shù)組
? ? let resolve = value => {
? ? ? if (this.status === 'pending') {
? ? ? ? this.status = 'fulfilled'
? ? ? ? this.value = value;
? ? ? ? this.onFulfilledCallbacks.forEach(fn => fn()) // 調(diào)用成功的回調(diào)函數(shù)
? ? ? }
? ? }
? ? let reject = reason => {
? ? ? if (this.status === 'pending') {
? ? ? ? this.status = 'rejected'
? ? ? ? this.reason = reason
? ? ? ? this.onRejectedCallbacks.forEach(fn => fn()) // 調(diào)用失敗的回調(diào)函數(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ù),直接拋出錯(cuò)誤
? ? 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(() => { // 將成功的回調(diào)函數(shù)放入成功數(shù)組
? ? ? ? ? setTimeout(() => {
? ? ? ? ? ? let x = onFulfilled(this.value)
? ? ? ? ? ? x instanceof MyPromise ? x.then(resolve, reject) : resolve(x)
? ? ? ? ? })
? ? ? ? })
? ? ? ? this.onRejectedCallbacks.push(() => { // 將失敗的回調(diào)函數(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
? ? // 同時(shí)啟動(dòng)limit個(gè)任務(wù)
? ? while (limit > 0) {
? ? ? start()
? ? ? limit -= 1
? ? }
? ? function start() {
? ? ? const url = urls.shift() // 從數(shù)組中拿取第一個(gè)任務(wù)
? ? ? if (url) {
? ? ? ? axios.post(url).then(res => {
? ? ? ? ? // todo
? ? ? ? }).catch(err => {
? ? ? ? ? // todo
? ? ? ? }).finally(() => {
? ? ? ? ? if (count == len - 1) {
? ? ? ? ? ? // 最后一個(gè)任務(wù)完成
? ? ? ? ? ? resolve()
? ? ? ? ? } else {
? ? ? ? ? ? // 完成之后,啟動(dòng)下一個(gè)任務(wù)
? ? ? ? ? ? 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 eatingES6繼承:
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 eating7、數(shù)組排序
sort 排序:
// 對數(shù)字進(jìn)行排序,簡寫 const arr = [3, 2, 4, 1, 5] arr.sort((a, b) => a - b) console.log(arr) // [1, 2, 3, 4, 5] // 對字母進(jìn)行排序,簡寫 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++) {
? ? // 從第一個(gè)元素開始,比較相鄰的兩個(gè)元素,前者大就交換位置
? ? 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
? ? ? }
? ? }
? ? // 每次遍歷結(jié)束,都能找到一個(gè)最大值,放在數(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)建一個(gè)URLSearchParams實(shí)例 const urlSearchParams = new URLSearchParams(window.location.search); // 把鍵值對列表轉(zhuǎn)換為一個(gè)對象 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)建副本,如果回調(diào)函數(shù)內(nèi)繼續(xù)注冊相同事件,會(huì)造成死循環(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)以上就是工作或求職中最常見的手寫功能
到此這篇關(guān)于分享10個(gè)常見的前端手寫功能的文章就介紹到這了,更多相關(guān)端手寫功能內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
JavaScript判斷數(shù)據(jù)類型有幾種方法及區(qū)別介紹
這篇文章主要介紹了JavaScript判斷數(shù)據(jù)類型有幾種方法及區(qū)別介紹,本文給大家分享多種方法通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-09-09
JS實(shí)現(xiàn)當(dāng)前頁居中分頁效果的方法
這篇文章主要介紹了JS實(shí)現(xiàn)當(dāng)前頁居中分頁效果的方法,涉及javascript操作頁面元素與樣式的相關(guān)技巧,需要的朋友可以參考下2015-06-06
javascript獲取url上某個(gè)參數(shù)的方法
獲取url上的某個(gè)參數(shù)的方法有很多,在本文為大家介紹下使用javascript是如何實(shí)現(xiàn)的,感興趣的朋友不要錯(cuò)過2013-11-11
js中substr,substring,indexOf,lastIndexOf的用法小結(jié)
本篇文章主要是對js中substr,substring,indexOf,lastIndexOf的用法進(jìn)行了總結(jié)介紹,需要的朋友可以過來參考下,希望對大家有所幫助2013-12-12
Javascript 正則表達(dá)式實(shí)現(xiàn)為數(shù)字添加千位分隔符
在項(xiàng)目中做貨幣轉(zhuǎn)換的時(shí)候經(jīng)常需要可以實(shí)現(xiàn)自動(dòng)格式化輸入的數(shù)字,自動(dòng)千位分隔符,在網(wǎng)上也看到一些其他網(wǎng)友的實(shí)現(xiàn)的代碼,感覺都不是太滿意,于是自己研究了下,分享給大家。2015-03-03

