简体   繁体   English

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

[英]How to create closure over variables in a JS function?

Assuming multiple sliders can exist on a webpage and they each need their own state of user interactions like touch-start X-position and a boolean of whether it is currently processing a swipe or not..假设网页上可以存在多个滑块,并且它们每个都需要自己的 state 用户交互,例如触摸启动 X 位置和 boolean 当前是否正在处理滑动。

I'm trying to reuse a piece of code that handles swipe and mouse drag events but each slider should have its own value of initalX and isInSwipe:我正在尝试重用一段处理滑动和鼠标拖动事件的代码,但每个 slider 都应该有自己的 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);
            });
        };
    }

For each slider on the page I would use it like so, passing the slide container and the callback to call on accepted interaction:对于页面上的每个 slider,我会像这样使用它,传递幻灯片容器和回调以调用已接受的交互:

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

I thought that since initialX and isInSwipe are defined in the function being returned then that would create a Closure over them so that each call of slideInteractions() would create its own copy of those variables but it seems that they go out of scope once the function returns.我认为由于 initialX 和 isInSwipe 是在返回的 function 中定义的,那么这将在它们之上创建一个闭包,以便每次调用 slideInteractions() 都会创建这些变量的自己的副本,但是一旦 function,它们似乎是 scope 中的 go回报。

Is there a way I can fix this keep the variables live on properly within the Closure?有没有办法解决这个问题,让变量在闭包中正常运行?

EDIT编辑

The variables are used in startInteraction() and endInteraction() but those functions don't really see those variables at all: they are undeclared within those functions which is where my confusion is because I assume that those functions would have access to the "closed" variables no?这些变量在startInteraction()endInteraction()中使用,但这些函数根本看不到这些变量:它们在那些函数中未声明,这是我感到困惑的地方,因为我假设这些函数可以访问“已关闭” “变量没有?

To have the variable shared between the functions, they have to be in the same block scope. So that means you would have to define the startInteraction and endInteraction functions inside the same block as the variables are defined.要在函数之间共享变量,它们必须位于同一块 scope 中。这意味着您必须在定义变量的同一块内定义 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);
        });
    };
}

Another option is pass around an object. Functions can be outside of the block scope.另一种选择是绕过 object。函数可以在块 scope 之外。

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);
        });
    };
}

A better solution is to make a class更好的解决方案是制作一个 class

 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>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM