This very good Stack Overflow answer gives an example of how closures can work counter intuitively when returning anonymous functions within a list.
function buildList(list) {
var result = [];
for (var i = 0; i < list.length; i++) {
var item = 'item' + 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]();
}
}
This code will return:
item2 undefined
item2 undefined
item2 undefined
My question is - how would you modify this code so that it returns the result we're expecting?
item0 1
item1 2
item2 3
Use an IIFE in the for loop in buildList and pass in the i.
This will ensure that the i passed in will remain enclosed in the IIFE closure and will not be changed by the i in the for loop
function buildList(list) {
var result = [];
for (var i = 0; i < list.length; i++) {
//the IIFE
(function(i) {
var item = 'item' + i;
result.push(function() {
console.log(item + ' ' + list[i])
});
})(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();
Related
So basically there are two sequences from I to j and j to k.
For example 3 to 5 and 5 to 2.
And we need to know the sum. 3 + 4 + 5 + 4 + 3 + 2.
And my code is not working.
var arr = [];
var sum = 0;
function pushIn(i, j, k){
for(var a = i; a < j; a++){
arr.push(a);
}
for(var a = j; a == k; a--){
arr.push(a);
}
for(var i = 0; i <arr.length;i++){
sum += arr[i];
}
}
}
I think the problem lies in your second for loop
Perhaps you should try this
for(var a = i; a < j; a++){
arr.push(a);
}
for(var a = j; a > k; a--){
arr.push(a);
}
for(var i = 0; i <arr.length;i++){
sum += arr[i];
}
Hope this helps
In for loop, second argument is a comparator- condition for executing the code block.
In your second loop, condition is never met hence it does not iterate at all.
In your case, I am assuming you want loop to be iterated unless it is less than or equals to k hence you need to make it >= so that condition will meet andloop will be iterated.
var arr = [];
var sum = 0;
function pushIn(i, j, k) {
for (var a = i; a < j; a++) {
arr.push(a);
}
for (var a = j; a >= k; a--) {
arr.push(a);
}
for (var i = 0; i < arr.length; i++) {
sum += arr[i];
}
return sum;
}
console.log(pushIn(3, 5, 2));
You may use .concat() and .reduce() to get the resultant value:
let reducer = (i, j, k) => [].concat(
Array.from({length: j - (i - 1)}, (_, index) => i + index),
Array.from({length: j - k}, (_, index) => j - (index + 1))
).reduce((r, c) => r + c, 0);
console.log(reducer(3, 5, 2));
Can someone please explain why does it console log out 10, 10, 10 instead of 9, 9, 9?
When it goes through for loop shouldn't it stop at 9?
var foo = [];
for (var i = 0; i < 10; i++) {
foo[i] = function() {
return i;
};
};
console.log(foo[0]());
console.log(foo[1]());
console.log(foo[2]());
Whenever any function which is using any variable from parent scope is executed, it gets that value of a variable which it is holding at the time of function execution. In your case i is already reached to 10 at the time of execution because of i++.
For getting expected result, you can add IIFE to it, which will hold the value of i in its scope.
var foo = [];
for (var i = 0; i < 10; i++) {
foo[i] = (function(i) {
return function() {
return i;
};
})(i);
};
console.log(foo[0]());
console.log(foo[1]());
console.log(foo[2]());
You can use this syntax which keeps the context
for (let i = 0; i < 10; i++) {
foo[i] = () => i;
};
console.log(foo[0]());
console.log(foo[1]());
console.log(foo[2]());
https://jsfiddle.net/bandpay/8zat3bnn/
Trying to pass i into the closure to make it local to avoid the closure issue of i remaining at 2.
var myFunctions = {};
for (var i = 0; i < 3; i++) { // let's create 3 functions
myFunctions[i] = function(i) { // and store them in myFunctions
console.log("My value: " + i); // each should log its value.
};
}
for (var j = 0; j < 3; j++) {
myFunctions[j](); // and now let's run each one to see
}
// > "My value: undefined
// > "My value: undefined
// > "My value: undefined
That is because the i in the function definition is what is expected as a parameter to the function
try this
var myFunctions = {};
for (var i = 0; i < 3; i++) { // let's create 3 functions
myFunctions[i] = function(i) { // and store them in myFunctions
console.log("My value: " + i); // each should log its value.
};
}
for (var j = 0; j < 3; j++) {
myFunctions[j](j); // pass j as parameter
}
The reason that's happening is because you've got i as a parameter of the functions, but then you don't pass a value.
There are three ways to fix this.
First, you can pass the value as a function parameter:
myFunctions[j](j);
The second way would be to use a function closure to store the value:
var myFunctions = {}
function generateValuePrintout (value) {
return function () {
console.log("My value: " + value);
};
}
for (var i = 0; i < 3; i++) {
myFunctions[i] = generateValuePrintout(i);
}
for (var j = 0; j < 3; j++) {
myFunctions[j]();
}
This is a cleaner way to do it than the first because it means you don't need to pass any value into the function for it to know about its closure.
The last way (my favourite) would be to store the value for the function using let (so that it is scoped correctly), and then call it without a parameter (remove the parameter from the function definition also):
for (var i = 0; i < 3; i++) { // let's create 3 functions
let closureValue = i;
myFunctions[i] = function() { // and store them in myFunctions
console.log("My value: " + closureValue); // each should log its value.
};
}
for (var j = 0; j < 3; j++) {
myFunctions[j](); // and now let's run each one to see
}
The output of each will be:
My value: 0
My value: 1
My value: 2
You are calling the function without passing the parameter it requires.
var myFunctions = {};
for (var i = 0; i < 3; i++) {
myFunctions[i] = function (i) {
console.log("My value: " + i);
};
}
for (var j = 0; j < 3; j++) {
myFunctions[j](j); // Passing the required parameter
}
// > "My value: 0
// > "My value: 1
// > "My value: 2
though parameter is not necessarry in javascript
but if you want value to be used which is in parameter that should be there
for (var j = 0; j < 3; j++) {
myFunctions[j]("name"+j); // and now let's run each one to see
}
output
My value: name0
My value: name1
My value: name2
I have a function
$rootScope.getCurrency = function() {
for (var i = 0; i < data.records.length; i++) {
for (var j = 0; j < data.records[i].columns.length; j++
if (data.records[i].columns[j].fieldname == "Currency") {
// Here I want to
// return the values
// if I do something like
return data.records[i].columns[j].value
// the loop exits on the first condition
// and does not iterate over completely. If I put
// return outside of the
// for loops it says i, j are undefined.
// How can I use the value outside of this loop
}
}
}
}
}
I have to use the returned value in my HTML for binding data.
My HTMl looks like:
ng-repeat i in someArray ng-if={{i.type==currency?getCurrency():''}}
Try pushing the values to an array:
$rootScope.getCurrency = function() {
var result = [];
for (var i = 0; i < data.records.length; i++) {
for (var j = 0; j < data.records[i].columns.length; j++
if (data.records[i].columns[j].fieldname == "Currency") {
// Add to array
result.push(data.records[i].columns[j].value);
}
}
}
return result;
}
I have an object literal similar to this:
var test = {
myFunc1: function () {
for (var i = 0, j = arguments.length; i < j; i++) {
alert('myFunc1: ' + arguments[i]);
}
},
myFunc2: function () {
for (var i = 0, j = arguments.length; i < j; i++) {
alert('myFunc2: ' + arguments[i]);
}
this.myFunc1.apply(this.myFunc2, arguments);
},
myFunc3: function () {
for (var i = 0, j = arguments.length; i < j; i++) {
alert('myFunc3: ' + arguments[i]);
}
this.myFunc2.apply(this.myFunc3, arguments);
}
}
When I call "myFunc3" I want it to call "MyFunc2" with all the arguments passed to "myFunc3". This is working as expected.
When I call "myFunc3" I want it to call "myFunc2" with all passed arguments, and I want myFunc1 to be called with all passed arguments that were originally passed to "myFunc3".
test.myFunc3('a', 'b');
Currently when I call this, I get 4 alert boxes, 2 for myFunc3 and 2 for myFunc2. Then I get an error:
Uncaught TypeError: Cannot call method 'apply' of undefined
If I need to pass a set of arguments more than one level deep in an object how can I do this? Is there a better more correct way to do this?
Here is a jsFiddle to demonstrate.
http://jsfiddle.net/taggedzi/fmPAQ/2/
Thanks in advance. Pure javascript only, no jquery please. Needs to work cross browser and platform.
That's because the this in myFunc2 is not anymore test but this.myFunc3. This was provided by your myFunc3 when calling this.myFunc2.apply(this.myFunc3, arguments);.
To visualize, your myFunc2 is doing:
(this.myFunc3).myFunc1(...);
//instead of
(this).myFunc1(...);
You should pass in only this so you are using the same this for the other functions.
this.myFunc2.apply(this, arguments);
Try this: it works. You should use this instead of this.FuncX
var test = {
myFunc1: function () {
for (var i = 0, j = arguments.length; i < j; i++) {
alert('myFunc1: ' + arguments[i]);
}
},
myFunc2: function () {
for (var i = 0, j = arguments.length; i < j; i++) {
alert('myFunc2: ' + arguments[i]);
}
this.myFunc1.apply(this, arguments);
},
myFunc3: function () {
for (var i = 0, j = arguments.length; i < j; i++) {
alert('myFunc3: ' + arguments[i]);
}
this.myFunc2.apply(this, arguments);
}
};
test.myFunc3('a', 'b');