手写节流和防抖
什么是节流和防抖
- 节流:在预设值内,事件发生多次,函数只执行一次;即两次函数被触发的事件间隔至少为预设值
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>10</title>
</link>
</head>
<body>
<button id='mybtn'>click me</button>
<script>
const throttle = (fn, delay) => {
let last = 0
return () => {
let now = new Date().getTime()
// 如果离上次执行时间过小,不执行
if (now - last < delay) return
// 执行函数,并修改上一次执行的时间
else {
last = now
fn()
return
}
}
}
document.getElementById('mybtn').addEventListener(
'click',
throttle((e) => {
console.log('clicked!')
}, 2000)
)
</script>
</body>
</html>
- 防抖:Debounce连续快速触发事件时,只有在间隔时间大于delay时才执行。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>10</title>
</link>
</head>
<body>
<button id='mybtn'>click me</button>
<script>
const debounce = (fn, delay) => {
let timeoutID;
return () => {
clearTimeout(timeoutID);
timeoutID = setTimeout(fn, delay);
}
}
document.getElementById('mybtn').addEventListener('click', debounce(e => {
console.log('clicked!');
}, 1000))
</script>
</body>
</html>
移动端的click事件就是使用了防抖,当touchStart后300ms内没有再次touchstart事件触发,这次点击才会触发click事件的监听函数(移动端点击事件会延迟300ms)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>10</title>
</link>
</head>
<body>
<button id='mybtn'>click me</button>
<script>
document.getElementById('mybtn').addEventListener('click', function () {
console.log("click!");
})
document.getElementById('mybtn').addEventListener('touchstart', function () {
console.log("touchstart!");
})
</script>
</body>
</html>
手写bind/call/apply
bind/call/apply可以改变非箭头函数中this的指向,其语法是:
fn.call(target, arg1, arg2, ...)
fn.apply(target, [arg1, arg2, ...])
fn.bind(target)
当没有target参数时,会将this指向全局对象(window/global)
关于this的指向可以看JavaScript:this指向和箭头函数的part 2:当调用一个对象上的方法时,该方法中的this指向这个对象。
所以这三个的思路基本一致,在target上添加上方法fn,调用之后再删除这个方法即可。
call
Function.prototype.myCall = function (obj, ...args) {
let target = obj || (typeof window === 'undefined' ? global : window)
target.func = this // myCall函数的调用是:func.mycall(obj,...args),所以myCall中的this指向func
target.func(...args) // func中this指向obj
delete target.f
}
let a = {
name: 'brynn',
test: function (m, n) {
console.log(this)
console.log(m, n)
},
}
let b = {
name: 'brown',
}
a.test('test1', 'test2')
a.test.myCall(b, 'test1', 'test2')
a.test.call(b, 'test1', 'test2')
a.test.myCall(null, 'test1', 'test2')
a.test.call(null, 'test1', 'test2')
apply
Function.prototype.myApply = function (obj, args) {
let target = obj || (typeof window === 'undefined' ? global : window)
target.func = this // myCall函数的调用是:func.mycall(obj,...args),所以myCall中的this指向func
target.func(...args) // func中this指向obj
delete target.f
}
let a = {
name: 'brynn',
test: function (m, n) {
console.log(this)
console.log(m, n)
},
}
let b = {
name: 'brown',
}
a.test(['test1', 'test2'])
a.test.myApply(b, ['test1', 'test2'])
a.test.apply(b, ['test1', 'test2'])
a.test.myApply(null, ['test1', 'test2'])
a.test.apply(null, ['test1', 'test2'])
bind
Function.prototype.myBind = function (obj) {
let target = obj || (typeof window === 'undefined' ? global : window)
target.func = this // myCall函数的调用是:func.mycall(obj,...args),所以myCall中的this指向func
return (...args) => {
// console.log('查看', args, ...args)
target.func(...args) // func中this指向obj
delete target.f
}
}
let a = {
name: 'brynn',
test: function (m, n) {
console.log(this)
console.log(m, n)
},
}
let b = {
name: 'brown',
}
a.test('test1', 'test2')
a.test.myBind(b)('test1', 'test2')
a.test.bind(b)('test1', 'test2')
a.test.myBind(null)('test1', 'test2')
a.test.bind(null)('test1', 'test2')