如何在JS函数中创建变量闭包?



假设一个网页上可以存在多个滑块,并且每个滑块都需要自己的用户交互状态,例如触摸开始x位置和当前是否正在处理滑动的布尔值。

我试图重用一段代码来处理滑动和鼠标拖动事件,但每个滑动条都应该有自己的值initalX和isInSwipe:

function slideInteractions(selector, callback) {
return function() {
let initialX = 0;
let isInSwipe = false;
document.querySelector(selector).addEventListener('mousedown', (ev) => {
startInteraction(ev);
ev.preventDefault();
});
document.querySelector(selector).addEventListener('touchstart', startInteraction);
document.addEventListener('mouseup', (ev) => {
endInteraction(ev, callback);
});
document.addEventListener('touchend', (ev) => {
endInteraction(ev, callback);
});
};
}

对于页面上的每个滑动条,我将像这样使用它,传递幻灯片容器和在接受交互时调用的回调:

slideInteractions('.project-slides', moveProject)();

我认为,由于initialX和isInSwipe是在函数中定义的返回,那么这将创建一个闭包在他们之上,以便每次调用slideInteractions()将创建自己的这些变量的副本,但似乎它们超出了作用域,一旦函数返回。

是否有一种方法可以解决这个问题,保持变量在闭包内正常运行?

编辑

变量在startInteraction()endInteraction()中使用,但这些函数根本没有真正看到这些变量:它们在这些函数中未声明,这就是我困惑的地方,因为我认为这些函数可以访问"closed"变量没有?

要在函数之间共享变量,它们必须在相同的块范围内。因此,这意味着你必须在定义变量的同一块中定义startInteraction和endInteraction函数。

function slideInteractions(selector, callback) {
return function() {
let initialX = 0;
let isInSwipe = false;
function startInteraction(e,) {
initialX = e.clientX;
}
function endInteraction(e, callback) { 
console.log(initialX, e.clientX);
if (callback) callback(initialX);
}
document.querySelector(selector).addEventListener('mousedown', (ev) => {
startInteraction(ev);
ev.preventDefault();
});
document.querySelector(selector).addEventListener('touchstart', startInteraction);
document.addEventListener('mouseup', (ev) => {
endInteraction(ev, callback);
});
document.addEventListener('touchend', (ev) => {
endInteraction(ev, callback);
});
};
}

另一个选择是传递一个对象。函数可以在块作用域之外。

function startInteraction(e, data) {
data.initialX = e.clientX;
}
function endInteraction(e, data, callback) { 
console.log(data.initialX, e.clientX);
if (callback) callback(data);
}
function slideInteractions(selector, callback) {
return function() {
const data = {
initialX: 0,
isInSwipe: false,
};
document.querySelector(selector).addEventListener('mousedown', (ev) => {
startInteraction(ev, data);
ev.preventDefault();
});
document.querySelector(selector).addEventListener('touchstart', startInteraction);
document.addEventListener('mouseup', (ev) => {
endInteraction(ev, data, callback);
});
document.addEventListener('touchend', (ev) => {
endInteraction(ev, data, callback);
});
};
}
一个更好的解决方案是创建一个类

class ClickyThing {
initialX = 0
isInSwipe = false
constructor(elem, callback) {
this.elem = elem;
this.callback = callback;
this.bindEvents();
}
bindEvents() {
this.elem.addEventListener('mousedown', (evt) => {
evt.preventDefault();
this.startInteraction(evt);
});
document.addEventListener('mouseup', (evt) => {
this.endInteraction(evt);
});
}
startInteraction(evt) {
this.isInSwipe = true;
this.initialX = evt.clientX;
}
endInteraction(evt) {
if (!this.isInSwipe) return;
console.log(this.initialX, evt.clientX);
if (this.callback) this.callback(this.initialX, evt.clientX);
this.isInSwipe = false;
}
}
document.querySelectorAll(".test").forEach((elem) => {
const clickyThing = new ClickyThing(elem, () => console.log('click'));
});
.test {
width: 100px; height: 100px; margin: 10px; background-color: #CCC;
}
<div class="test"></div>
<div class="test"></div>
<div class="test"></div>

最新更新