具有相同功能的多个事件侦听器存储单独的局部变量?
Multiple event listeners with same function storing separate local variables?
我对 javascript 中的闭包相当陌生(当然是在将它们应用于事件处理程序时!),并试图掌握导致此代码行为的基本原则:
function showClick() {
var clicks = 0
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()
<button id="b1">Record my Clicks</button> <!--only stores click history of b1, disregarding b2-->
<button id="b2">Record MY Clicks</button> <!--only stores click history of b2, disregarding b1-->
我可以看到 "clicks" 的值在我每次触发事件时都被更新和存储(大概在事件处理函数 return 中?),但这样做只是为了相应的按钮被点击。这些值是如何存储的,为什么它们不在两个事件处理程序之间共享?如果我想在两个按钮之间共享 "clicks" 的值,是否需要在两者的范围之外分配一个变量?
您正在创建函数的多个实例。这些实例中的每一个都有自己的 clicks
变量,这就是它们自行增加的原因。您有多种选择,其中包括:
保留对函数的引用:
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()
两次,并将每次调用的结果分配给其各自的事件侦听器。这是查看它的简单方法:
function showClick() {
var clicks = 0
return function(e) {
clicks++
console.log("click "+clicks+" at "+[e.clientX,e.clientY])
return clicks
}
}
var clickHandler1 = showClick();
document.getElementById("b1").onclick=clickHandler1;
var clickHandler2 = showClick();
document.getElementById("b2").onclick=clickHandler2;
这是另一种看待它的方式:
document.getElementById("b1").onclick=(function() {
var clicks = 0
return function(e) {
clicks++
console.log("click "+clicks+" at "+[e.clientX,e.clientY])
return clicks
}
})();
document.getElementById("b2").onclick=(function() {
var clicks = 0
return function(e) {
clicks++
console.log("click "+clicks+" at "+[e.clientX,e.clientY])
return clicks
}
})();
这些函数具有相同的函数定义,但它们不共享实例变量。
如果你想在两者之间共享变量,只需完全忘记工厂函数(注意这不会调用函数,它将函数分配给事件监听器):
var clicks = 0
function showClick(e) {
clicks++
console.log("click "+clicks+" at "+[e.clientX,e.clientY])
return clicks
}
document.getElementById("b1").onclick=showClick;
document.getElementById("b2").onclick=showClick;
你必须思考'scopes'。当您调用 showClick()
时,有一个范围。在此范围内,它创建了一个变量,称为 'clicks'。此变量在此函数和所有子函数/闭包中有效。
函数 returns 一个新函数,一个所谓的闭包。这个闭包保持对封装函数范围的访问。每次当您调用 showClick()
时,都会生成一个新范围,并且在该范围内会生成变量 clicks
。此外还有创建和返回的闭包。作用域、这个作用域内的变量和闭包是不一样的。每次调用 showClicks 都不一样。
这就是为什么它们分别计算两个链接的原因。
如果您想同时计算两次点击...有多种解决方案。
- 首先,您可以取下封口。你不需要它。
- 将闭包保存为 showClick 函数的上下文,并在每次调用 showClick 时使用它
- 在全局范围内保存点击(不推荐)
编辑:就像这里的评论中所问的那样,有两种保存闭包并重用它的方法:
1) "Singleton-Approach I"(保存闭包本身)
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();
2) "Singleton-Approach II"(保存点击次数)
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();
3) "Context-Approach"(将闭包保存为工厂函数的上下文)
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();
我对 javascript 中的闭包相当陌生(当然是在将它们应用于事件处理程序时!),并试图掌握导致此代码行为的基本原则:
function showClick() {
var clicks = 0
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()
<button id="b1">Record my Clicks</button> <!--only stores click history of b1, disregarding b2-->
<button id="b2">Record MY Clicks</button> <!--only stores click history of b2, disregarding b1-->
我可以看到 "clicks" 的值在我每次触发事件时都被更新和存储(大概在事件处理函数 return 中?),但这样做只是为了相应的按钮被点击。这些值是如何存储的,为什么它们不在两个事件处理程序之间共享?如果我想在两个按钮之间共享 "clicks" 的值,是否需要在两者的范围之外分配一个变量?
您正在创建函数的多个实例。这些实例中的每一个都有自己的 clicks
变量,这就是它们自行增加的原因。您有多种选择,其中包括:
保留对函数的引用:
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()
两次,并将每次调用的结果分配给其各自的事件侦听器。这是查看它的简单方法:
function showClick() {
var clicks = 0
return function(e) {
clicks++
console.log("click "+clicks+" at "+[e.clientX,e.clientY])
return clicks
}
}
var clickHandler1 = showClick();
document.getElementById("b1").onclick=clickHandler1;
var clickHandler2 = showClick();
document.getElementById("b2").onclick=clickHandler2;
这是另一种看待它的方式:
document.getElementById("b1").onclick=(function() {
var clicks = 0
return function(e) {
clicks++
console.log("click "+clicks+" at "+[e.clientX,e.clientY])
return clicks
}
})();
document.getElementById("b2").onclick=(function() {
var clicks = 0
return function(e) {
clicks++
console.log("click "+clicks+" at "+[e.clientX,e.clientY])
return clicks
}
})();
这些函数具有相同的函数定义,但它们不共享实例变量。
如果你想在两者之间共享变量,只需完全忘记工厂函数(注意这不会调用函数,它将函数分配给事件监听器):
var clicks = 0
function showClick(e) {
clicks++
console.log("click "+clicks+" at "+[e.clientX,e.clientY])
return clicks
}
document.getElementById("b1").onclick=showClick;
document.getElementById("b2").onclick=showClick;
你必须思考'scopes'。当您调用 showClick()
时,有一个范围。在此范围内,它创建了一个变量,称为 'clicks'。此变量在此函数和所有子函数/闭包中有效。
函数 returns 一个新函数,一个所谓的闭包。这个闭包保持对封装函数范围的访问。每次当您调用 showClick()
时,都会生成一个新范围,并且在该范围内会生成变量 clicks
。此外还有创建和返回的闭包。作用域、这个作用域内的变量和闭包是不一样的。每次调用 showClicks 都不一样。
这就是为什么它们分别计算两个链接的原因。
如果您想同时计算两次点击...有多种解决方案。
- 首先,您可以取下封口。你不需要它。
- 将闭包保存为 showClick 函数的上下文,并在每次调用 showClick 时使用它
- 在全局范围内保存点击(不推荐)
编辑:就像这里的评论中所问的那样,有两种保存闭包并重用它的方法:
1) "Singleton-Approach I"(保存闭包本身)
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();
2) "Singleton-Approach II"(保存点击次数)
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();
3) "Context-Approach"(将闭包保存为工厂函数的上下文)
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();