01-addEventListener事件监听的传参调用问题
发布于 2022年 04月 11日 05:58
前端问题小记-01 addEventListener
今天学习手写防抖函数的时候遇到了一个小问题
//为鼠标移动事件添加防抖函数
box.addEventListener('mousemove', debounce(move, 300))
// 自定义防抖函数
function debounce(fun, delay) {
console.log(this); //window
}
防抖函数中,当在addEventListener中想调用函数时带参 就应该考虑到一个问题 就是this指向 , 此时直接在自定义函数中打印this,发现this指向的为全局的window
//为鼠标移动事件添加防抖函数
box.addEventListener('mousemove', debounce)
// 自定义防抖函数
function debounce(fun, delay) {
console.log(this); //box
}
而防抖函数中,我们需要的this是需要指向调用函数的元素对象的(即上述的this指向)
- [首先要弄明白他们的区别]
//这里相当于是 调用了debounce函数 然后将运行结果传入 作为参数
//此时addEventListener 事件 与debounce函数的执行 没有必然联系
// 所以很多时候会出现 设定的事件还没有触发 就已经将函数debounce中的代码执行了
//函数内this的指向也将继续指向全局的 window
① box.addEventListener('mousemove', debounce(move, 300))
//直接将debounce函数作为参数传入
② box.addEventListener('mousemove', debounce)
// <---相当于--->
③ box.addEventListener('mousemove',function debounce(){
console.log(this) //box
})
①写法并不适用于传参 当然可以直接使用 ③写法进行参数传递 但是debounce作为可复用的封装函数 明显也是不合适的
很简单的一种解决方案 ---闭包
box.addEventListener('mousemove', debounce(move, 300))
function debounce(move, 300){
//因为上述事件监听使用的是执行debounce 的返回结果
//所以只要将所有需要的内容 封装到一个函数 就会作为参数返回给addEventListener中
return function(){
//再尝试在这里调用this
consoloe.log(this) //box
}
}
这样子想要的效果就达成了-------
补)--------防抖函数
<style>
#box{
width: 100%;
height: 300px;
font-size: 35px;
line-height: 300px;
text-align: center;
background-color: aqua;
}
</style>
<div id="box">
</div>
var box = document.querySelector('#box');
let count = 0;
box.addEventListener('mousemove', debounce(move, 300,true))
function debounce(fun, delay, immediate) {
// !!!! timeout一定要定义在回调函数外面
let timeout;
return function () {
let context = this;
let args = arguments;
clearTimeout(timeout)
if (immediate) {
let callNow = !timeout;
timeout = setTimeout(() => {
timeout = null;
}, delay);
if (callNow) fun.apply(context, args);
} else {
timeout = setTimeout(function () {
fun.apply(context,args)
}, delay);
}
}
}
function move(e){
box.innerHTML = count++;
}
带注释版
var box = document.querySelector('#box');
let count = 0;
function move(e) {
// 修改前 this 指向 window
// console.log(this);
//修改了fun的this指向后 这里的 this 已经指向 box 对象
// console.log(this);
//修改前 e的值为undefind
// console.log(e);
box.innerHTML = count++;
}
//移动鼠标时会不断的执行move函数
// box.addEventListener('mousemove',move)
// 自定义防抖函数
box.addEventListener('mousemove', debounce(move, 1000,true))
// box.addEventListener('mousemove', debounce)
// box.onmousemove = debounce(move, 300);
//underscore防抖函数
// box.addEventListener('mousemove',_.debounce(move,300))
// fun 需要进行防抖的函数 delay 延迟的时间 immediate是否立即指向 (true/false)
function debounce(fun, delay, immediate) {
// console.log(immediate);
// immediate为true时会忽略delay的延迟事件
// !!!! timeout一定要定义在回调函数外面
let timeout;
// console.log(this);
// addEventListener接受的第二个参数为函数 所以必须返回一个函数 (形成了闭包)
return function () {
// console.log(this);
let context = this;
//arguments中包含了 事件对象
// console.log(arguments);
let args = arguments;
// console.log(args);
// 函数 fun 在延迟 delay 毫秒后执行
clearTimeout(timeout); //清除计时对象
//判断是否立即执行
if (immediate) {
// console.log(timeout);
//鼠标移动的瞬间 timeout的值为undefined
let callNow = !timeout;
//callNow会在移动的一瞬间变为 true
// console.log(callNow);
timeout = setTimeout(() => {
//将延时器变量置空
timeout = null;
}, delay)
// console.log(timeout);
// 立即执行
if (callNow) fun.apply(context, args);
} else {
//不会立即执行
timeout = setTimeout(function () {
// console.log(args);
//执行fun函数 改变fun执行上下文this的指向
fun.apply(context,args) //第一个参数 改变fun函数this指向 第二个参数 函数所需要的参数(数组类型)
// fun();
}, delay);
}
}
}
欢迎一起交流