Javascript 多个具有相同函数的事件侦听器存储单独的局部变量?

Javascript 多个具有相同函数的事件侦听器存储单独的局部变量?,javascript,closures,event-listener,local-variables,Javascript,Closures,Event Listener,Local Variables,我对javascript中的闭包相当陌生(当然是在将闭包应用于事件处理程序时!),并试图掌握导致此代码行为的基本原则: 函数showClick(){ var=0 返回函数(e){ 咔哒声++ log(“单击”+“单击”+[e.clientX,e.clientY]) 返回点击 } } document.getElementById(“b1”).onclick=showClick() document.getElementById(“b2”).onclick=showClick() 记录我的点击次

我对javascript中的闭包相当陌生(当然是在将闭包应用于事件处理程序时!),并试图掌握导致此代码行为的基本原则:

函数showClick(){
var=0
返回函数(e){
咔哒声++
log(“单击”+“单击”+[e.clientX,e.clientY])
返回点击
}
}
document.getElementById(“b1”).onclick=showClick()
document.getElementById(“b2”).onclick=showClick()
记录我的点击次数

记录我的点击次数
您正在创建函数的多个实例。这些实例中的每一个都有自己的
点击次数
变量,这就是它们自己增加的原因。您有几种选择,其中包括:

保留对函数的引用:

function showClick() {
    var clicks = 0;
    return function(e) {
        clicks++
        console.log("click "+clicks+" at "+[e.clientX,e.clientY])
        return clicks
    }
}
var clickEvent = showClick(); // we instance the function only once
document.getElementById("b1").onclick=clickEvent; // and use it here
document.getElementById("b2").onclick=clickEvent; // and here
将变量保持在全局范围内

function showClick() {
    this.clicks = 0; // this === window here
    return function(e) {
        clicks++
        console.log("click "+clicks+" at "+[e.clientX,e.clientY])
        return clicks
    }
}
document.getElementById("b1").onclick=showClick();
document.getElementById("b2").onclick=showClick();

您将调用两次
showClick()
,并将每次调用的结果分配给相应的事件侦听器。下面是一个简单的方法:

函数showClick(){
var=0
返回函数(e){
咔哒声++
log(“单击”+“单击”+[e.clientX,e.clientY])
返回点击
}
}
var clickHandler1=showClick();
document.getElementById(“b1”).onclick=clickHandler1;
var clickHandler2=showClick();
document.getElementById(“b2”).onclick=clickHandler2;
下面是另一种看待它的方式:

document.getElementById(“b1”).onclick=(函数(){
var=0
返回函数(e){
咔哒声++
log(“单击”+“单击”+[e.clientX,e.clientY])
返回点击
}
})();
document.getElementById(“b2”).onclick=(function(){
var=0
返回函数(e){
咔哒声++
log(“单击”+“单击”+[e.clientX,e.clientY])
返回点击
}
})();
这些函数具有相同的函数定义,但它们不共享实例变量

如果要在两者之间共享变量,只需完全忘记工厂函数(注意,这不会调用函数,而是将函数分配给事件侦听器):

var clicks=0
功能显示单击(e){
咔哒声++
log(“单击”+“单击”+[e.clientX,e.clientY])
返回点击
}
document.getElementById(“b1”).onclick=showClick;
document.getElementById(“b2”).onclick=showClick;

你必须在“范围”中思考。调用
showClick()
时,有一个作用域。在这个范围内,它创建了一个变量,称为“clicks”。此变量在此函数和所有子函数/闭包中有效

该函数返回一个新函数,即所谓的闭包。此闭包保持对封装函数范围的访问。每次调用
showClick()
时,都会生成一个新的作用域,变量
在此作用域内单击。此外,还有创建并返回的闭包。作用域、此作用域内的变量和闭包不相同。每次你叫showClicks都会有不同的结果

这就是为什么这两个链接分别计算的原因

如果你想同时计算两次点击次数。。。有多种解决方案

  • 首先,您可以移除封口。你不需要它
  • 将闭包另存为showClick函数的上下文,并在每次调用showClick时使用它
  • 在全局范围内保存单击(不推荐)
编辑:与此处评论中的问题类似,有两种方法可以保存闭包并重新使用它:

1) “单例方法I”(保存闭包本身)

2) “单一方法II”(保存点击次数)

3) “上下文方法”(将闭包另存为工厂函数的上下文)


“这不会调用函数,而是将函数分配给事件侦听器。”这可能是我对事件处理程序中的内部函数如何工作的主要误解。谢谢你的澄清!示例2:您可以将clickcount保存为上下文,而不是使用匿名函数。要短一点示例3:
在全局范围内单击
不是一个好主意。这里是包装匿名函数保持作用域干净的正确位置。同样:您可以将clickcount保存为上下文以避免此“问题”。作为对我的回答的回应:完全忘记工厂函数,阅读正确的命名(链接在我对您的评论的回应中)。外部函数是工厂函数,内部函数是闭包。另一个来源:,你可以在我的另一个回复中找到三个:)@JoshuaK Re:“全局范围”最终,在全局范围内应该很少运行。我假设所有这些都将被包装在某种函数中。回复:结束命名,很抱歉,由于某种原因,我的脑海中出现了这种情况。你说得对。我已经删除了我对你答案的评论,但由于投票锁定,我无法更改我的投票,除非以某种方式编辑你的帖子。@TinyGiant没问题:)谢谢。这发生在我们当中最好的人身上。
this.clicks=0;//此===此处的窗口
不要将变量放入全局范围,不需要这样做。只需将它放在工厂函数上方的一个范围内。将“函数值”与公共变量链接对我来说非常有意义!但是,我不知道你可以这样使用“event”关键字,而不会有其他潜在的不良副作用。我想如果我用函数表达式“var showClick=function(){…}”来代替我原来的函数声明,它也同样有效。@JoshuaK当然,这只是为了说明目的。@JonathanStraus我刚刚意识到了
事件
变量名!我会改变它。。。不,它也不起作用:如果你那样调用它,你仍然会实例化这个函数两次。@JoshuaK第二个要点听起来像是我要采取的方法,但是你可以吗
function showClick() {
    if(showClick.closure) return showClick.closure;
    var clicks = 0;
    return showClick.closure = function(evt) {
        clicks++;
        console.log(clicks+' at '+evt.clientX+' / '+evt.clientY);
    }
}
document.getElementsByTagName('a')[0].onclick = showClick();
document.getElementsByTagName('a')[1].onclick = showClick();
function showClick() {
    showClick.clicks = showClick.clicks || 0;
    return function(evt) {
        showClick.clicks++;
        console.log(showClick.clicks+' at '+evt.clientX+' / '+evt.clientY);
    }
}
document.getElementsByTagName('a')[0].onclick = showClick();
document.getElementsByTagName('a')[1].onclick = showClick();
var showClick = (function showClick() {
    return this;
}).bind((function() {
    var clicks = 0;
    return function(evt) {
        clicks++;
        console.log(clicks+' at '+evt.clientX+' / '+evt.clientY);
    };
})());
document.getElementsByTagName('a')[0].onclick = showClick();
document.getElementsByTagName('a')[1].onclick = showClick();