问题
I'm learning some ES6
features and of course came across the let
keyword and its new scope (differs from var
)
and I came across an example about the tricky scope of var
and its hoisting.
but I can't fully understand why I get this result:
var events = ['click', 'dblclick', 'keydown', 'keyup'];
for (var i = 0; i < events.length; i++) {
var event = events[i];
document.getElementById('btn').addEventListener(event, function() {
document.getElementById('result').innerHTML = 'event: ' + event;
});
}
<button id="btn">Click Me!</button>
<span id="result"></span>
I understand that var event
is hoisted outside the for
loop but why is it getting the last event ('keyup') in the array every iteration of the loop?
Is the addEventListener
function asynchronous and by the time its attaching the event the value of the event changes?
回答1:
In your example all the events are being registered, but the inner event
variable and the outer event
variables are different as there is no block level scope and but function level scope.
These 4 events 'click', 'dblclick', 'keydown', 'keyup' are all registered but as the value of event at the end becamse keyup
so 'event: ' + event;
will always be event: keyup
.
You can use IIFE ( immediately-invoked function expression ), it is a JavaScript design pattern which produces a lexical scope using JavaScript's function scoping.
var events = ['click', 'dblclick', 'keydown', 'keyup'];
for (var i = 0; i < events.length; i++) {
var event = events[i];
(function(event) {
document.getElementById('btn').addEventListener(event, function() {
document.getElementById('result').innerHTML = 'event: ' + event;
});
})(event);
}
<button id="btn">Click Me!</button>
<span id="result"></span>
Trying to explain:
Try clicking it, ev
is keyup
and see the behaviour after 4 seconds
.
var events = ['click', 'dblclick', 'keydown', 'keyup'];
for (var i = 0; i < events.length; i++) {
var event = events[i];
var ev = events[i];
document.getElementById('btn').addEventListener(event, function() {
document.getElementById('result').innerHTML = 'event: ' + ev;
});
setTimeout(function(){
ev = "Look it is changed now, see the scoping!";
},4000);
}
<button id="btn">Click Me!</button>
<span id="result"></span>
回答2:
There is indeed something "asynchronous" (sort of) happening, as the two event
occurrences are not evaluated at the same time.
document.getElementById('btn').addEventListener(event, function() {
document.getElementById('result').innerHTML = 'event: ' + event;
});
The first event
is evaluated right away in the loop, so get the 4 values.
The second event
id evaluated when the anonymous function containing it is executed, i.e. when the event fires. At that moment, the JS engine goes back to the event
var that it has somehow kept a reference to (that's a closure), and finds the last value assigned to it.
来源:https://stackoverflow.com/questions/34554790/javascript-hoisting-var-vs-let