event assignment using improper iterator value [duplicate] - javascript

This question already has answers here:
JavaScript closure inside loops – simple practical example
(44 answers)
Closed 7 years ago.
I am attempting to create a closure in a loop in order to set an event listener with my iterator value. However, the value in setCookie is not properly being set. What am I doing wrong here?
obj = document.getElementsByClassName("class");
l = obj.length;
i = 0;
function addEv() {
while (i < l) {
obj[i].addEventListener("click", listener.bind(null, i));
function listener(index) {
setCookie("item", index, 24)
}
i++;
}
}

UPDATED ANSWER
On way to capture iterating values is by using immediately invoked functions to capture the value in a closure.
while (i < l) {
obj[i].addEventListener("click", (function(index) {
return function(ev) {
//setCookie("item", index, 24)
console.log(index)
};
}(i)));
i++;
}
This example will wrap the iterating i as index.

Related

Why and how does this piece of Javascript Code print 6 five times on execution? [duplicate]

This question already has answers here:
JavaScript closure inside loops – simple practical example
(44 answers)
Closed 5 years ago.
This is the piece of code,which on execution gives 6,five times in one second intervals. Can someone please explain this to me in detail?
for (var i=1; i<=5; i++) {
setTimeout( function timer(){
console.log( i );
}, i*1000 );
}
The variable i is part of the closure of the timer function.
When the timer function is executed,
it will print the value of i at that time.
By the time that happens, the loop has already completed,
and so the value of i is 6.
For all 5 executions of timer,
so you see 6 printed 5 times.
What else would you expect?
If you want to print a count from 1, 2, ..., 5 with timeouts in between,
then pass the current concrete value of i into the closure,
using an immediately evaluated function expression:
for (var i = 1; i <= 5; i++) {
setTimeout(function (value) {
return function timer() {
console.log(value);
}
}(i), i*1000);
}
Another equivalent variation:
for (var i = 1; i <= 5; i++) {
(function () {
var value = i;
setTimeout(function timer() {
console.log(value);
}, i*1000);
})()
}
Notice in both examples that the variable value is defined in the closure of the inner function, and a different instance of value is created in every iteration of the for loop,
with the value of i at the time of the iteration.
This is the crucial difference from the code in the question.

alternative to for loop [duplicate]

This question already has answers here:
How can I convert the "arguments" object to an array in JavaScript?
(21 answers)
Closed 7 years ago.
How do I use .forEach instead of a for loop?
'use strict';
var score = (function(){
function updateScore() {
for(var i = 0; i < arguments.length; i++) {
this.score += arguments[i];
}// I want to use .forEach here instead of for loop.
return this.score;
}
return {
update: updateScore
}
})();
var soccer = {
name: 'Soccer',
score: 0
}
score.update.apply(soccer, [1,2,3])
console.log(soccer.score)
this will log 6.
I tried this
function updateScore() {
arguments.forEach((args, i) => {
this.score += args[i];
};
return this.score;
};
Error log: arguments.forEach is not a function
In modern (ES2015) JavaScript you can use Array.from():
Array.from(arguments).forEach((arg) => {
this.score += arg;
});
You don't need to use array indexing, as the .forEach() function passes each array element to the callback function as the first argument. (It does pass the index as the second argument, but in this case you wouldn't need to use that.)
It's important to note that if the arguments object is passed out of a function (as it would be here), the overall function may be considered ineligible for optimization. That's because the arguments object has some weird properties that make it very hard for the optimizer to know what's going on and whether it's safe to make assumptions about how local variables change. If that's a concern, then the only option is to copy the arguments into a simple array with a for loop.

How to define an array of function, when the array is big [duplicate]

This question already has answers here:
JavaScript closure inside loops – simple practical example
(44 answers)
Closed 8 years ago.
I want to define an array of function so I tried the following code, but it does not has the intended result, because the i in the loop cannot be retrieved else where.
<script>
var f = []
for (var i=0; i<1000; i++){
f[i] = function(){
return i
}
}
console.log(f[3]);
</script>
There is the brute-force method to write 1000 lines of codes to define function, is there other ways?
In fact I met this problem in Java, Array of function pointers in Java
, so answers in both Java or JS would be helpful.
Use an immediately executed function inside the loop to create a scope, so that you can have one variable for each function instance:
var f = []
for (var i=0; i<1000; i++){
(function(i){
f[i] = function(){
return i;
}
})(i);
}
console.log(f[3]());
demo: http://jsfiddle.net/Guffa/rPKss/
It's a closure / scope issue, you have to lock in the value of i
<script>
var f = [];
for (var i=0; i<1000; i++){
(function(j) {
f[j] = function(){
return j;
}
})(i); // passing as argument to IIFE makes it local to that scope
}
console.log(f[3]);
</script>

How To Control Variable Hoisting? [duplicate]

This question already has answers here:
JavaScript closure inside loops – simple practical example
(44 answers)
Closed 10 years ago.
I have a problem with binding functions from an array to some elements, but i get this error after i click on any element with class "class".
Uncaught TypeError: Property '4' of object function (){alert(1)},function (){alert(2)},function (){alert(3)},function (){alert(4)} is not a function
var c = [
function(){alert(1)},
function(){alert(2)},
function(){alert(3)},
function(){alert(4)}
];
function test(b){
for(var i = 0; i < b.length; i++){
$('.class').eq(i).bind('click', function(){
b[i]();
});
}
}
test(c);
I think that the variable i keep its last value.
any solution or explanation will be really appreciated.
Thanks in advance.
You need a closure - in js variable scope is determined by functions. With jQuery, just use .each:
$('.class').each(function(i) {
$(this).on('click', function(){
alert(i+1);
});
});
However, if you really need to have an array of distinct functions you just can bind the functions themselves directly as handlers:
var elems = $('.class');
for (var i=0;i<b.length;i++) {
elems.eq(i).on('click', b[i]);
}
Your function array only has 4 items, so b[4] is undefined. Instead of referencing b inside your click handler, make it the click handler itself:
elems.eq(i).on('click', b[i]);
By doing this the value of i will be examined each iteration through the loop, instead of when the element is clicked (at which point its value should always be 4).
The problem is, in your loop the functions you are setting as click handlers are all sharing the same i value. After the loop, that i value will be 4, so each function will try to call b[4], which doesn't exist.
You need to create a new scope for each of the functions, so that the i values are all different.
for(var i = 0; i < b.length; i++){
(function(i){
$('.class').eq(i).bind('click', function(){
b[i]();
});
}(i));
}
You can also just set the function from the array as the click handler directly.
for(var i = 0; i < b.length; i++){
$('.class').eq(i).bind('click', b[i]);
}

How are local variables referenced in closures? [duplicate]

This question already has answers here:
JavaScript closure inside loops – simple practical example
(44 answers)
Closed 8 years ago.
I am reading an article (JavaScript Closures for Dummies) and one of the examples is as follows.
function buildList(list) {
var result = [];
for (var i = 0; i < list.length; i++) {
var item = 'item' + list[i];
result.push( function() {alert(item + ' ' + list[i])} );
}
return result;
}
function testList() {
var fnlist = buildList([1,2,3]);
// using j only to help prevent confusion - could use i
for (var j = 0; j < fnlist.length; j++) {
fnlist[j]();
}
}
testList();
When testList is called, an alert box that says "item3 undefined". The article has this explanation:
When the anonymous functions are called on the line fnlist[j](); they all use the same single closure, and they use the current value for i and item within that one closure (where i has a value of 3 because the loop had completed, and item has a value of 'item3').
Why does item have a value of 'item3'? Doesn't the for loop end when i becomes 3? If it ends shouldn't item still be 'item2'? Or is the variable item created again when testList calls the functions?
You're close...
Why does item have a value of 'item3'? Doesn't the for loop end when i becomes 3?
Yes.
If it ends shouldn't item still be
'item2'?
Nope. This example is a little tricky. During the last iteration of the loop, i is 2, but it references the 3rd element of the list array, which is 3. In other words, item == 'item' + list[2] == 'item3'
Or is the variable item created again when testList calls the functions?
No, you were almost right the first time. I think you just missed that item[2] has the value of 3.
The for loop within buildList completes before you do the following:
for (var j = 0; j < fnlist.length; j++) {
fnlist[j]();
}
... therefore, by that time (when you call each function), the variable item will be whatever was last assigned to it (i.e. "item3"), and i will be 3 (as a result of the last i++ operation), and list[3] is undefined.
It's all to do with the fact that the loop completes before you call the closure'd function. To prevent this, you could create a new closure, like so:
function buildList(list) {
var result = [];
for (var i = 0; i < list.length; i++) {
var item = 'item' + list[i];
result.push(
(function(item, i){
// Now we have our own "local" copies of `item` and `i`
return function() {
console.log(item + ' ' + list[i])
};
})(item, i)
);
}
return result;
}
I think the point you are missing is that list[i] is underfined because i is 3, and list is only defined for 0..2.
The list variable is stored in closure as you say.
Actually you can access the list variable, but you are trying to access list[3]. After all, the i variable is also stored as a closure and it's value is 3 when the console.log function is called.
The loop ends when i becomes 3, but the "item" variable stored in the closure, and displayed by alert, is set to
var item = 'item' + list[i];
the text 'item' + the value at list[2]. The third list item is 3, so the text is item3

Categories

Resources