This question already has answers here:
JavaScript closure inside loops – simple practical example
(44 answers)
Closed 7 years ago.
As a javascript beginner, I thought I realized the Closure feature until seeing the trap from the sample code below. I tried to modify the code to pop up the result which many C/C++/C#/Java programmers expect as,
"item1 1"
"item2 2"
"item3 3"
After one hour struggling, I still can't do that. Could any javascript master teach me how to modify it ? Thank you.
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();
The idea is to create a closure inside which it will be fixed.
You can nest the for content inside an immediate function :
function buildList(list) {
var result = [];
for (var i = 0; i < list.length; i++) {
(function(k){// k will equals i
var item = 'item' + list[k];
result.push( function() {alert(item + ' ' + list[k])} );
})(i);// i in argument
}
return result;
}
function testList() {
var fnlist = buildList([1,2,3]);
for (var j = 0; j < fnlist.length; j++) fnlist[j]();
}
testList();
It is the same as writing an other function :
function doStuff(k,arr,arr2){
var item = 'item' + arr[k];
arr2.push( function() {alert(item + ' ' + arr[k])});
}
function buildList(list) {
var result = [];
for (var i = 0; i < list.length; i++) doStuff(i,list,result);
return result;
}
function testList() {
var fnlist = buildList([1,2,3]);
for (var j = 0; j < fnlist.length; j++) fnlist[j]();
}
testList();
With the recent forEach(value, index, array) method the closure is automatically provided :
function buildList(list) {
var result=[];
list.forEach(function(e){
result.push(function(){alert('item'+e+' '+e)});
});
return result;
}
buildList([1,2,3]).forEach(function(e){e();});
Rather cool.
Using ECMAScript 6 let (thanks elad.chen)
let can replace var to scope i inside functions in the loop : no more need to create a closure as in (1) and (2). You will just have to move the item+list[i] part inside the function. However let is available in strict mode only, and not in all browsers yet (it still misses firefox for example)
'use strict'
function buildList(list) {
var result = [];
for(let i=0;i<list.length;i++) {
result.push(function(){alert('item'+list[i]+' '+list[i])});
}
return result;
}
function testList() {
var fnlist = buildList([1,2,3]);
for (var j = 0; j < fnlist.length; j++) fnlist[j]();
}
testList();
Not master by any means, just learning, too:
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]);
}
testList();
This could be good chance for IIFE usage. Demo: http://jsfiddle.net/zh02a69j/2/
In the moment when you call your first function by fnlist[j](), your i value is last incremented value. Your function which push results to array 'see' just that value, since it is not executed immediately, in loop - it is defined, but not executed. You can make it execute immediatelly (as IIFE), in loop, then script returns expected output.
If i made some mistake (to all previous and future downvoters:)) - please guide me, too.
Related
im trying to bubble sort some information (by asking users which they prefer instead of compare)
when i use settimeout in callback function it only runs once at the first level of for loop.
(s changes by clicking on images
this is the main function we pass it an array as argument
function bubble_Sort(a)
{
var swapp;
var n = a.length-1;
var x=a;
do {
swapp = false;
for (var i=0; i < n; i++)
{
s=0;
limage.setAttribute('src','images/'+x[i]+'.jpg');
rimage.setAttribute('src','images/'+x[i+1]+'.jpg');
console.log('images changed');
check(i,function(){
if (s==0)
{
var temp = x[i];
x[i] = x[i+1];
x[i+1] = temp;
swapp = true;
}
});
}
n--;
} while (swapp);
return x;
}
and here is check function (callback to wait for user to click on one of images)
var check=function(p,callback){
setTimeout(function(){console.log('this is Timeout');},3000);
callback();
}
you don't need a set-timeout function to implement such functionality, your code has to wait for a click (asynchronous) event to continue it's computation. can get what you want with the help of generator functions.
here's a simple working sample, the code is mostly self descriptive, hope it helps!
// a generator function
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/yield
function* bubbleSort(items) {
var length = items.length;
for (var i = 0; i < length; i++) {
for (var j = 0; j < (length - i - 1); j++) {
firstImageIndex = j;
secondImageIndex = j + 1;
updateUI(); //magic happens here
shouldBeSwapped = yield i; // wait for user to select a picture (no set timeout here)
if (shouldBeSwapped) {
let tmp = items[j];
items[j] = items[j + 1];
items[j + 1] = tmp;
}
}
}
updateUI(); // final update
}
Codepen Link Here !
Today I try to solve a problem on codewars,it requires me give the permutations of a given string.
Firstly,I try to use a recursion function looks like:
function permutate(str) {
var result = [];
if (str.length == 1) {
return [str]
} else {
var preResult = permutate(str.slice(1));
for (var j = 0; j < preResult.length; j++) {
for (var k = 0; k < preResult[j].length + 1; k++) {
var temp = preResult[j].slice(0, k) + str[0] + preResult[j].slice(k);
result.push(temp);
}
}
return result;
}
}
After I click the attemp button,the OJ tells me there is an error caused by heap out memory.Because my function called with a long string:"abcdefghijkl".
Secondly,I rewrite my function by using loop.just like:
function perm(str) {
let result = [],tempArr = [];
let subStr = str;
while (subStr.length !== 0) {
if (result.length === 0) {
result.push(str[0]);
} else {
for (let i = 0; i < result.length; i++) {
let item = result[i];
let itemLen = item.length;
for (let j = 0; j < itemLen+1; j++) {
let temp = item.slice(0, j) + subStr[0] + item.slice(j);
tempArr.push(temp);
}
}
result = tempArr;
tempArr = [];
}
subStr = subStr.slice(1);
}
return result;
}
It works when the given string is short.But still cause Error.
So,I want to know why cause this error and if there is a permutation algorithm can run in Node(v6.11.0) without memory error?
I searched a lot and tried many methods,but nothing works.So I ask my first question on stackoverflow,hoping you can give me some help.Thanks!
Try the module https://github.com/miguelmota/permutations, or even try to use the code from the module
In addition to previous answer as a possible try out, you can try to increase process max memory limit size, for example with node --max-old-space-size=8192 which is in bytes, the node process will run with extended 8GB memory limit.
var createShoutOuts = function(numbers_list){
var shoutOuts = [];
for (var j = 0; j < numbers_list.length; j++) {
shoutOuts.push(function() {
var shout_out = 'This is shout out number ' + (j+1);
console.log(shout_out + '. The number is ' + numbers_list[j]);
});
}
return shoutOuts;
};
var performShoutOuts = function(user_numbers){
var readyForShout = createShoutOuts(user_numbers);
for (var i = 0; i < readyForShout.length; i++) {
readyForShout[i]();
};
};
performShoutOuts([2,4,8]);
I created the above to teach myself closures. The output is always:
'This is shout out number 4. The number is undefined'
I understand that it will always say number 4 because the anonymous functions that are being pushed in to the shoutOuts array have a reference to the j variable, not a copy of the value of the j variable. Therefore by the time the anonymous functions are called by readyForShout[i](); the for loop has already run and i has the value 4.
What I don't understand is why is it saying undefined? Because it appears to me as though the array that is passed in to performShoutOuts should be stored in the closure and should therefore be accessible when readyForShout[i](); is executed.
What have I missed?
Try using a closure for real:
var createShoutOuts = function(numbers_list){
var shoutOuts = [];
for (var j = 0; j < numbers_list.length; j++) {
shoutOuts.push((function (j) {
return function () {
var shout_out = 'This is shout out number ' + (j+1);
console.log(shout_out + '. The number is ' + numbers_list[j]);
};
})(j));
}
return shoutOuts;
};
var performShoutOuts = function(user_numbers){
var readyForShout = createShoutOuts(user_numbers);
for (var i = 0; i < readyForShout.length; i++) {
readyForShout[i]();
};
};
performShoutOuts([2,4,8]);
I understand there is no block level scope for var i ; however, in the following code, d.push(); is executed after the initialisation of i, why i here remains undefined?
var d = [];
for (var i = 0; i < 3; i++) {
d.push(function(i) {
console.log('iterator: ' + i);
});
}
d[0]();//undefined
Any thoughts will be appreciated
You can push an argument-bound version of the anonymous function by using .bind() thereby ensuring the value of the first argument.
var d = [];
for (var i = 0; i < 3; i++) {
d.push((function(i) {
console.log('iterator: ' + i);
}).bind(this, i));
}
d[0](); // iterator: 0
d[1](); // iterator: 1
d[2](); // iterator: 2
Here's my solution:
var d = [];
function newFunction(i) {
var k = i;
return function() {
console.log('iterator: ' + k);
}
}
for (var i = 0; i < 3; i++) {
d.push(
newFunction(i)
);
}
d[0]();
d[1]();
This works because when you create the new function, it creates a variable scope that doesn't change with your for loop. It saves the state of i on each iteration so that when you call them again, it runs with the i it was originally declared with.
I'm trying to add functions into an array. These have to be named 'opdracht1' through to 'opdracht10'.
Though, I cannot figure out how to give it a name.
var opdrachtArray = [];
for (i = 0; i < 10; i++) {
opdrachtArray.push(function() {func(i); });
}
It adds the functions but as I said earlier I cannot find out how to add a name.
Also, am I later just able to define the functions and call them when I need them?
Name your functions by placing them on the window object:
for (i = 0; i < 10; i++) {
f = function() { func(i); }); // but see below
window['opdracht' + i] = f
opdrachtArray.push(f);
}
However you have a more basic problem. All your functions close over i and therefore func is always going to be called with the value of i after the loop finishes, in other words, 10. One solution is:
function make_func(i) {
return function() {
return func(i);
};
}
then
for (i = 0; i < 10; i++) {
f = make_func(i);
window['opdracht' + i] = f;
opdrachtArray.push(f);
}
Or
for (i = 0; i < 10; i++) {
(function(i) {
var f = function() { func(i); };
window['opdracht' + i] = f
opdrachtArray.push(f);
}(i));
}
or just use func.bind(null, i), which does approximately the same thing.
for (i = 0; i < 10; i++) {
f = func.bind(null, i);
window['opdracht' + i] = f;
opdrachtArray.push(f);
}
If you want each function to be assigned to a name, use dictionary, not array:
var opdrachtDict = {};
for (i = 0; i < 10; i++) {
opdrachtDict["opdracht"+i]=func.bind(null,i);
}
function func(i){
alert(i);
}
opdrachtDict["opdracht3"](); //ok, lets call it and see if you think its still a wrong answer ;)...
You could store the name and function in an object and push each object to the array:
var opdrachtArray = [];
for (i = 0; i < 10; i++) {
var name = 'opdracht' + (i+1);
opdrachtArray.push({name : name, callback : function() {func(i); }});
}
Well a function defined in the global scope just ends up being a property of self/window anyways. i.e.:
function mr_function(){return 5;}
self["mr_function"];
window["mr_function"];
Both (property of self / window) reference the function we defined. I guess you could name your function that way if you're careful. Or assign them to some other object's property if you'd rather not make them global.