分享10个常见的JavaScript前端手写功能

目录

  • 1、防抖
  • 2、节流
  • 3、深拷贝
  • 4、手写 Promise
  • 5、异步控制并发数
  • 6、继承
  • 7、数组排序
  • 8、数组去重
  • 9、获取 url 参数
  • 10、事件总线 | 发布订阅模式

1、防抖
function debounce(fn, delay) {let timerreturn 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、节流
function throttle(fn, delay) {let last = 0 // 上次触发时间return (...args) => {const now = Date.now()if (now - last > delay) {last = nowfn.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 objif (obj instanceof Date) return new Date(obj)if (obj instanceof RegExp) return new RegExp(obj)if (cache.get(obj)) return cache.get(obj) // 如果出现循环引用,则返回缓存的对象,防止递归进入死循环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
class MyPromise {constructor(executor) {this.status = 'pending' // 初始状态为等待this.value = https://www.it610.com/article/null // 成功的值this.reason = null // 失败的原因this.onFulfilledCallbacks = [] // 成功的回调函数存放的数组this.onRejectedCallbacks = [] // 失败的回调函数存放的数组let resolve = value => {if (this.status === 'pending') {this.status = 'fulfilled'this.value = https://www.it610.com/article/value; this.onFulfilledCallbacks.forEach(fn => fn()) // 调用成功的回调函数}}let reject = reason => {if (this.status === 'pending') {this.status = 'rejected'this.reason = reasonthis.onRejectedCallbacks.forEach(fn => fn()) // 调用失败的回调函数}}; try {executor(resolve, reject)} catch (err) {reject(err)}}then(onFulfilled, onRejected) {// onFulfilled如果不是函数,则修改为函数,直接返回valueonFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value// onRejected如果不是函数,则修改为函数,直接抛出错误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(() => { // 将成功的回调函数放入成功数组setTimeout(() => {let x = onFulfilled(this.value)x instanceof MyPromise ? x.then(resolve, reject) : resolve(x)})})this.onRejectedCallbacks.push(() => { // 将失败的回调函数放入失败数组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) // 1return p2()}).then(ret => {console.log(ret) // 2})


5、异步控制并发数
function limitRequest(urls = [], limit = 3) {return new Promise((resolve, reject) => {const len = urls.lengthlet count = 0// 同时启动limit个任务while (limit > 0) {start()limit -= 1}function start() {const url = urls.shift() // 从数组中拿取第一个任务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) // xiaomingconsole.log(xm.age) // 12xm.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) // xiaomingconsole.log(xm.age) // 12xm.eat() // xiaoming is eating


7、数组排序 sort 排序:
// 对数字进行排序,简写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.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.log(bubbleSort([2, 3, 1, 5, 4])) // [1, 2, 3, 4, 5]

【分享10个常见的JavaScript前端手写功能】
8、数组去重 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 参数 URLSearchParams 方法:
// 创建一个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、事件总线 | 发布订阅模式
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)

以上就是工作或求职中最常见的手写功能
到此这篇关于分享10个常见的前端手写功能的文章就介绍到这了,更多相关端手写功能内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

    推荐阅读