一、防抖
1. 认识防抖和节流函数
- 防抖和节流的概念其实最早并不是出现在软件工程中,防抖是出现在电子元件中,节流出现在流体流动中
- 而JavaScript是事件驱动的,大量的操作会触发事件,加入到事件队列中处理
- 而对于某些频繁的事件处理会造成性能的损耗,我们就可以通过防抖和节流来限制事件频繁的发生
2. 认识防抖debounce函数

- 当事件触发时,响应的函数并不会立即触发,而是会等待一定的时间
- 当事件密集触发时,函数的触发会被频繁的推迟
- 只有等待了一段时间也没有事件触发,才会真正的执行响应函数
3. 防抖的应用场景
- 输入框中频繁的输入内容,搜索或者提交信息
- 频繁的点击按钮,触发某个事件
- 监听浏览器滚动事件,完成某些特定操作
- 用户缩放浏览器的resize事件
4. 防抖函数实现
<input type="text">
<button>取消</button>
function hydebounce(fn, delay) {
let timer = null
const _debounce = () => {
if (timer) clearTimeout(timer)
timer = setTimeout(() => {
fn()
timer = null
}, delay);
}
return _debounce
}
const inputEl = document.querySelector("input")
let counter = 1
inputEl.oninput = hydebounce(function() {
console.log(`发送网络请求${counter++}`, this.value)
}, 3000)
function hydebounce(fn, delay, immediate = true) {
let timer = null
let isInvoke = false
const _debounce = function(...args) {
return new Promise((resolve, reject) => {
try {
if (timer) clearTimeout(timer)
let res = undefined
if (immediate && !isInvoke) {
res = fn.apply(this, args)
resolve(res)
isInvoke = true
return
}
timer = setTimeout(() => {
res = fn.apply(this, args)
resolve(res)
timer = null
isInvoke = false
}, delay);
} catch (error) {
reject(error)
}
})
}
_debounce.cancel = function() {
if (timer) clearTimeout(timer)
timer = null
isInvoke = false
}
return _debounce
}
二、节流
1. 认识节流throttle函数

- 当事件触发时,会执行这个事件的响应函数
- 如果这个事件被频繁触发,那么节流函数会按照一定的频率来执行函数
- 不管在这个中间有多少次触发这个事件,执行函数的频率总是固定的
2. 节流函数的应用场景
- 监听页面的滚动事件
- 鼠标移动事件
- 用户频繁点击按钮操作
- 游戏中的一些设计
3. 节流函数的实现
<button>按钮</button>
<input type="text">
function hythrottle(fn, interval) {
let startTime = 0
const _throttle = function() {
const nowTime = new Date().getTime()
const waitTime = interval - (nowTime - startTime)
if (waitTime <= 0) {
fn()
startTime = nowTime
}
}
return _throttle
}
const inputEl = document.querySelector("input")
let counter = 1
inputEl.oninput = hythrottle(function() {
console.log(`发送网络请求${counter++}:`, this.value);
}, 1000)
function hythrottle(fn, interval, { leading = true, trailing = false } = {}) {
let startTime = 0
let timer = null
const _throttle = function(...args) {
return new Promise((resolve, reject) => {
try {
const nowTime = new Date().getTime()
if (!leading && startTime === 0) {
startTime = nowTime
}
const waitTime = interval - (nowTime - startTime)
if (waitTime <= 0) {
if (timer) clearTimeout(timer)
const res = fn.apply(this, args)
resolve(res)
startTime = nowTime
timer = null
return
}
if (trailing && !timer) {
timer = setTimeout(() => {
const res = fn.apply(this, args)
resolve(res)
startTime = new Date().getTime()
timer = null
}, waitTime);
}
} catch (error) {
reject(error)
}
})
}
_throttle.cancel = function() {
if (timer) clearTimeout(timer)
startTime = 0
timer = null
}
return _throttle
}
三、深拷贝
const info = {
name: "why",
age: 18,
friend: {
name: "kobe"
},
running: function() {}
}
const obj = JSON.parse(JSON.stringify(info))
function deepCopy(originValue, map = new WeakMap) {
if (typeof originValue === "Symbol") {
return Symbol(originValue.description)
}
if (isObject(originValue)) {
return originValue
}
if (originValue instanceof Set) {
const newSet = new Set()
for (const setItem of originValue) {
newSet.add(deepCopy(setItem, map))
}
return newSet
}
if (typeof originValue === "function") {
return originValue
}
if (map.get(originValue)) {
return map.get(originValue)
}
const newObj = Array.isArray(originValue) ? [] : {}
map.set(originValue, newObj)
for (const key in originValue) {
newObj[key] = deepCopy(originValue[key], map)
}
const symbolKeys = Object.getOwnPropertySymbols(originValue)
for (const symbolKey of symbolKeys) {
newObj[Symbol(symbolKey.description)] = deepCopy(originValue[symbolKey], map)
}
return newObj
}
const set = new Set(["abc", "cba", "nba"])
const s1 = Symbol()
const s2 = Symbol()
const info = {
name: "why",
age: 18,
friend: {
name: "kobe",
address: {
name: "洛杉矶",
detail: "斯坦基普中心"
}
},
set: set,
running: function() {
console.log("running~");
},
symbolKey: Symbol(),
[s1]: "aaa",
[s2]: "bbb"
}
info.self = info
const newObj = deepCopy(info)
四、事件总线
class HYEventBus {
constructor() {
this.eventMap = {}
}
on(eventName, eventFn) {
let eventFns = this.eventMap[eventName]
if (!eventFns) {
eventFns = []
this.eventMap[eventName] = eventFns
}
eventFns.push(eventFn)
}
off(eventName, eventFn) {
let eventFns = this.eventMap[eventName]
if (!eventFns) return
for (let i = 0; i < eventFns.length; i++) {
const fn = eventFns[i]
if (fn === eventFn) {
eventFns.splice(i, 1)
break
}
}
if (eventFns.length === 0) {
delete this.eventMap[eventName]
}
}
emit(eventName, ...args) {
let eventFns = this.eventMap[eventName]
if (!eventFns) return
eventFns.forEach(fn => {
fn(...args)
})
}
}
const eventBus = new HYEventBus()
eventBus.on("navclick", () => {
console.log("navclick listener 01");
})
const navBtnEl = document.querySelector(".nav-btn")
navBtnEl.onclick = function() {
eventBus.emit("navclick")
}