十個(gè)常見的前端手寫功能,你全都會嗎?
作者:CRMEB
萬丈高樓平地起,地基打的牢,才能永遠(yuǎn)立于不敗之地。今天給大家?guī)淼氖?0個(gè)常見的 JavaScript 手寫功能,重要的地方已添加注釋。有的是借鑒別人的,有的是自己寫的,如有不正確的地方,歡迎多多指正。
萬丈高樓平地起,地基打的牢,才能永遠(yuǎn)立于不敗之地。今天給大家?guī)淼氖?0個(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 (typeof obj !== 'object') return obj // 普通類型,直接返回
- if (obj === null) return obj
- if (cache.get(obj)) return cache.get(obj) // 防止循環(huán)引用,程序進(jìn)入死循環(huán)
- if (obj instanceof Date) return new Date(obj)
- if (obj instanceof RegExp) return new RegExp(obj)
- // 找到所屬原型上的constructor,所屬原型上的constructor指向當(dāng)前對象的構(gòu)造函數(shù)
- let cloneObj = new obj.constructor()
- 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、實(shí)現(xiàn) Promise
- class MyPromise {
- constructor(executor) { // executor執(zhí)行器
- this.status = 'pending' // 等待狀態(tài)
- this.value = null // 成功或失敗的參數(shù)
- this.fulfilledCallbacks = [] // 成功的函數(shù)隊(duì)列
- this.rejectedCallbacks = [] // 失敗的函數(shù)隊(duì)列
- const that = this
- function resolve(value) { // 成功的方法
- if (that.status === 'pending') {
- that.status = 'resolved'
- that.value = value
- that.fulfilledCallbacks.forEach(myFn => myFn(that.value)) //執(zhí)行回調(diào)方法
- }
- }
- function reject(value) { //失敗的方法
- if (that.status === 'pending') {
- that.status = 'rejected'
- that.value = value
- that.rejectedCallbacks.forEach(myFn => myFn(that.value)) //執(zhí)行回調(diào)方法
- }
- }
- try {
- executor(resolve, reject)
- } catch (err) {
- reject(err)
- }
- }
- then(onFulfilled, onRejected) {
- if (this.status === 'pending') {
- // 等待狀態(tài),添加回調(diào)函數(shù)到成功的函數(shù)隊(duì)列
- this.fulfilledCallbacks.push(() => {
- onFulfilled(this.value)
- })
- // 等待狀態(tài),添加回調(diào)函數(shù)到失敗的函數(shù)隊(duì)列
- this.rejectedCallbacks.push(() => {
- onRejected(this.value)
- })
- }
- if (this.status === 'resolved') { // 支持同步調(diào)用
- console.log('this', this)
- onFulfilled(this.value)
- }
- if (this.status === 'rejected') { // 支持同步調(diào)用
- 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、異步控制并發(fā)數(shù)
- function limitRequest(urls = [], limit = 3) {
- return new Promise((resolve, reject) => {
- const len = urls.length
- let count = 0 // 當(dāng)前進(jìn)行到第幾個(gè)任務(wù)
- const start = async () => {
- const url = urls.shift() // 從數(shù)組中拿取第一個(gè)任務(wù)
- if (url) {
- try {
- await axios.post(url)
- if (count == len - 1) {
- // 最后一個(gè)任務(wù)成功
- resolve()
- } else {
- count++
- // 成功,啟動下一個(gè)任務(wù)
- start()
- }
- } catch (e) {
- if (count == len - 1) {
- // 最后一個(gè)任務(wù)失敗
- resolve()
- } else {
- count++
- // 失敗,啟動下一個(gè)任務(wù)
- start()
- }
- }
- }
- }
- // 啟動limit個(gè)任務(wù)
- while (limit > 0) {
- start()
- limit -= 1
- }
- })
- }
- // 測試
- 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
- 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、數(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 去重
- 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 參數(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á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)
以上就是工作或求職中最常見的手寫功能,你是不是全都掌握了呢
責(zé)任編輯:華軒
來源:
今日頭條