This question already has answers here:
Explanation of `let` and block scoping with for loops
(5 answers)
let keyword in the for loop
(3 answers)
Closed 2 years ago.
I am quite confused about the closing behaviour of functions declared inside of for loops, in particular about variables defined in initializers:
function createFunctions(){
const functions = []
for(let i = 0; i < 5; i++)
functions.push(() => i);
return functions;
}
const results = createFunctions().map(m => m())
// results: [0, 1, 2, 3, 4]
vs
function createFunctions(){
const functions = []
let i;
for(i = 0; i < 5; i++)
functions.push(() => i);
return functions;
}
const results = createFunctions().map(m => m())
// results: [5, 5, 5, 5, 5]
Since the anonymous arrow function declared in the for loop captures its scope, I would expect both cases to yield [5, 5, 5, 5, 5], since at call time, i's value is 5. The first result however seems to suggest that on each iteration through the loop, i is a different variable. However, if you repeat the test but the initialized variable is an object instead of a number:
function createFunctions(){
const functions = []
for(let obj = {}, i = 0; i < 5; i++)
functions.push(() => obj);
return functions;
}
const results = createFunctions().map(m => m())
// results: [{}, {}, {}, {}, {}]; results[0] === results[1]: true
we can see that all elements in the returned array are referentially equal, and so are not different variables. So it seems that the way that functions close over variables declared in the for loop initializer changes depending on whether the variable is a primitive or not, which sounds preposterous to me.
What am I missing?
So, I went through all the trouble of writing this question, and while researching I found the answer somewhat by accident, so I thought I'd switch this to a self-answer question so that if others encounter the same behaviour, it may be easier to find.
I found the answer here
In loops, you get a fresh binding for each iteration if you
let-declare a variable. The loops that allow you to do so are: for,
for-in and for-of.
Which explains why the objects are equal: we get a new variable which gets assigned to the value of the previous one at the end of the loop. In the case of the object, the new variable is pointing to the same object, which explains the referential equality. It also explains why the first case yields [0, 1, 2, 3, 4]: numbers are values, not references, unlike objects.
Related
This question already has answers here:
JavaScript closure inside loops – simple practical example
(44 answers)
Closed 2 years ago.
const array = [1, 2, 3, 4];
for (var index = 0; index < array.length; index++) {
setTimeout(() => {
console.log("I am at index " + index);
});
}
When I use the "var" keyword in the for loop I get the output
I am at index 4
I am at index 4
I am at index 4
I am at index 4
But When I use the "let" keyword in the for loop,
const array = [1, 2, 3, 4];
for (let index = 0; index < array.length; index++) {
setTimeout(() => {
console.log("I am at index " + index);
});
}
I am at index 0
I am at index 1
I am at index 2
I am at index 3
Can anyone tell me what's happening here?
The answer to your question requires you to understand
Hoisting in JS
Hoisting Diff in var and let type
Scope Diff in var and let type
Clousures
var type variable has a function/script scope and due to hoisting its declaration will move up out of the for loop, so it will come into the global scope and on every iteration, it is getting changed and all three closure will share the single variable i from the global lexical scope, whereas if we replace var with let then it will not move outside the for loop being a block-scoped, every iteration is a new block and there will be three different lexical scope for three timeout callback and a shared global scope(which has nothing from our code).
I was testing a few pieces of code out, and two statements that should return the same value return different values.
const arr = [3, 'foo', { bar: 'baz' }, false, 4, 5];
for(var i = 0; i < arr.length; i++) {
i = arr[i]
console.log(i)
}
returns [3, 4, 5], while
const arr = [3, 'foo', { bar: 'baz' }, false, 4, 5];
for(var i = 0; i < arr.length; i++) {
console.log(arr[i])
}
returns [3, 'foo', { bar: 'baz' }, false, 4, 5]
Why?
This is a great example of both the flexibility and pitfalls of imperative programming.
tl;dr you shouldn't be modifying i in you're for loops, and you also shouldn't be using for loops. A better way to iterate over the array is:
arr.forEach(function(item, i) {
console.log(item)
})
Imperative Programming
Copied from Wikipedia: In computer science, imperative programming is a programming paradigm that uses statements that change a program's state.
What does this mean?
In simple terms, whenever you are modifying variables (mutation), you're programming imperatively. For example:
var x = 1;
x = 5;
console.log(x + 5); // you would get 10
However, this can lead to unexpected problems which we can see in your example which is explained pretty well in other answers.
This is why in general, it's a good idea to avoid it (at least in Javascript) even though imperative programming is so convenient. This doesn't mean we always avoid it. Sometimes, programs need to run as efficient as possible, and imperatively modifying variables is the only way to achieve that.
In most cases, we don't need that efficiency if we're iterating (looping) through only a few hundred or even tens of thousands of variables. Our computers are fast enough that the difference in performance is not worth it.
Functional Programming
Functional programming is a great alternative. In practice, it boils down to one simple rule - no side effects. Side effects simply mean mutation, i.e modifying variables.
This avoids bugs that would be introduced in imperative programming since you know that once you initialize a variable, it will always be the same.
Below, I'll outline a pseudo introduction to functional programming and how it relates to your example. If you want to learn more about it, you can find countless resources online. Here's a pretty good article about functional programming with Javascript.
Some examples of functional programming
const x = 5; // we use const do denote x is constant and can't be changed ever
// x = 5 would give us an error!
const y = x + 5
// for (const i = 0; i < 10; ++i) is not functional since i is modified!
How should we loop without for loops? Recursion!
It's almost always good to avoid using for loops in the form of for (var i ...). This is because those loops are based on imperative programming, which we want to avoid if possible.
How do we print all the values of an array?
function printValues(arr, i = 0) {
if (i == arr.length) return; // exit when we reach the end
console.log(arr[i]); // let's call console.log
printValues(arr, i + 1); // do the same thing with the next index of the array
}
What if instead of just printing, we want to double every element in the array and double it? Instead of rewriting the same function, we can generalize this:
function forEach(arr, method, i = 0) {
if (i == arr.length) return;
method(arr[i], i)
return forEach(arr, method, i + 1)
}
// notice that forEach looks exactly like printValues, except now, we can pass in anything we would have passed into a normal for loop
forEach([1,2,3,4,5], function(item, i) {
const x = i * 2
console.log(x);
})
// now back to OP's example, we can use this forEach function
forEach(arr, function (item, i) {
console.log(item)
})
Of course, Javascript has these functions built-in and they are also optimized. So, you can just use the built-in forEach:
arr.forEach(function(item, i) {
console.log(item)
// or console.log(arr[i]);
})
There are other functions to use too other than forEach, these include:
map
filter
reduce
There are more, but these are the fundamental building blocks for iteration on arrays in functional Javascript and are must-haves in your toolkit!
I think second snippet explains itself only the first one needs explanation.
First of all there is no sense to write i = arr[i] inside a loop. It changes the looping number of the element at corresponding index. Which means anything unexpected could happen
What happened in your case is below
In first iteration i is 0 then its changed to 3 due to i = arr[i].
In second iteration i is 4 because last value of i is incremented due to i++. Due to i = arr[i] i becomes 4 the arr[4]
In third iteration i becomes 5 due to increment i++ and due i = arr[i] it remains 5.
In fourth iteration it becomes 6 and loop ends because i < arr.length becomes false
const arr = [3, 'foo', { bar: 'baz' }, false, 4, 5];
for(var i = 0; i < arr.length; i++) {
console.log(`Initial i = ${i}`)
i = arr[i];
console.log(`Final i = ${i}`);
console.log('\n')
}
This question already has answers here:
Imperative vs Declarative code [closed]
(3 answers)
Closed 4 years ago.
I have a vague understanding of what does it mean "provide immutability" when talking about using map over for loop. Because calling Array.prototype.map on array does not prevent you from altering original array. Let's say we have single threaded asynchronous program. In these circumstances is there an "immutability" benefit that map provides over for loop?
I can imagine the following code:
function A() {
const arr = [1, 2, 3, 4, 5]
const anotherArr = arr.map((e, i) => {
arr[i] = 'changed'
// here goes some asynchronous code
return e * 2
})
// now arr is ['changed', 'changed', 'changed', 'changed', 'changed']
// and anotherArr is [2, 3, 6, 8, 10]
}
function B() {
const arr = [1, 2, 3, 4, 5]
const anotherArr = []
for (let i = 0, len = arr.length; i < len; i++) {
anotherArr[i] = arr[i] * 2
arr[i] = 'changed'
// here goes some asynchronous code
}
// and again:
// now arr is ['changed', 'changed', 'changed', 'changed', 'changed']
// and anotherArr is [2, 3, 6, 8, 10]
}
I guess that probably in function B anotherArr is created manually before populating. Therefore something in the same scope can alter it before for loop is completed. So my question is: "Is there a benefit of using map over for loop in single threaded asynchronous environment in terms of safety"?
Editing
I rephrase my question: "Is there some kind of immutability that Array.prototype.map function
provides (because it belongs to functional paradigm)".
As I understand now the map function in javascript is not about immutability,
it is about hiding some implementation details (how resulting array is constructed).
So map function is a delclarative way of doing things. for loop is an imperative
way of doing things (which brings more freedom and more responsibility I guess).
map, reduce, filter, forEach, etc.
Methods above, in my mind, is nice for FP(functional programming).
Of course, you can use them instead of normal for loop,
but normal for loop is faster than them(https://hackernoon.com/javascript-performance-test-for-vs-for-each-vs-map-reduce-filter-find-32c1113f19d7).
So in my idea, if your don't care performance, use map, reduce, etc. is elegant and will get lesser and more readable code.
I'm working through Eloquent Javascript and I'm having trouble understanding something. Perhaps I've missed something along the way. This is the solution given for chapter 5 (higher-order functions), exercise 1, which takes the elements in the different arrays and puts them all in a single array:
var arrays = [[1, 2, 3], [4, 5], [6]];
console.log(arrays.reduce(function(flat, current) {
return flat.concat(current);
}, []));
My problem is: I have absolutely no clue why the arguments "flat" and "current" work in this situation. The entire chapter reads through assuming the reader understands what's going on here but I have absolutely no idea why this works. It doesn't appear that "flat" and "current" are defined anywhere. Another short example is this one where the author explains how the reduce method works (problem area in bold):
function reduce(array, combine, start) {
var current = start;
for (var i = 0; i < array.length; i++)
current = combine(current, array[i]);
return current;
}
**console.log(reduce([1, 2, 3, 4], function(a, b) {
return a + b;
}, 0));**
Where in the world did "a" and "b" come from and why does this piece of code work? Any help would be much appreciated, thank you.
flat and current don't need to be declared anywhere, they are parameters to the anonymous function that is passed to Array.reduce.
One way to illustrate this is to modify your second example to use Array.reduce directly using an anonymous function with parameters a and b. Look at this:
[1, 2, 3, 4].reduce(function(a, b) {
console.log("a: " + a + " b: " + b);
return a + b;
});
The console will now show:
a: 1 b: 2
a: 3 b: 3
a: 6 b: 4
10
What's happening is that the anonymous function(a, b) {...} is called with (1, 2), which returns 3, which is passed in again (3, 3) which returns 6, which is passed in as the first argument (6, 4), which returns the final answer 10.
Another illustration is to use a second argument to Array.reduce, say 10, to see what's going on. That 10 is used as the initialValue. So:
[1, 2, 3, 4].reduce(function(a, b) {
console.log("a: " + a + " b: " + b);
return a + b;
}, 10);
The trace is:
a: 10 b: 1
11 b: 2
13 b: 3
16 b: 4
20
You can work out how that happened.
Yes, reduce can be a little confusing in the beginning. It is a native function that takes two parameters. One is a callback function and the other one is any value.
The idea is that in the callback function you can use the values of the array one at a time to process a result. To do that it iterates over the array values and passes them to the callback function you defined one at a time and for every loop it takes the value of the last loop and passes it as well.
Let's say you want to sum all numbers in an array:
//your array
var numbers = [4,7,3];
//your initial value
var initialValue = 0;
//your function
function sum(iteratingValue, arrayValue) {
return iteratingValue + arrayValue;
}
var result = numbers.reduce(sum, initialValue);
Now, you can name your callback function parameters whatever you like, a and b, start and finish, fish and duck. It won't matter, reduce will pass the values in the same order.
Here is the definition by MDN:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce
reduce executes the callback function once for each element present in the array, excluding holes in the array, receiving four arguments: the initial value (or value from the previous callback call), the value of the current element, the current index, and the array over which iteration is occurring.
First, let us touch the core of the question. Let's suppose you have a function, like this:
function func1(myfunc, arr) {
//Do something, which will result in having variables called param1 and param2
myfunc(param1, param2);
}
Let's see what happens. func1 takes myfunc and arr as parameters. myfunc will be passed when func1 is called. arr is an array.
Now, let's suppose you call it this way:
func1(function(a, b) {
//do something with a and b
}, [1, 2, 3, 4]);
You are calling func1, by passing a function and an array. The array is obvious, so let's see the function. The function expects two parameters and will do something with them. You do not have to define a or b when you call func1, since it is func1's internal job to create and initialize them. So, func1 will do its internal things and call your function passed as a parameter. Now, let's see your example:
var arrays = [[1, 2, 3], [4, 5], [6]];
console.log(arrays.reduce(function(flat, current) {
return flat.concat(current);
}, []));
Here, you call arrays.reduce (which is very similar to func1 in the general description). You pass a function and an array. Again, the array is obvious, but the question is, how flat and current are defined. The answer is that it is arrays.reduce's internal job to create and initialize them. As about the reduce prototype function, you can read more about it here.
After I asked this question and realized that Backbone.Collections are strictly for Backbone.Models, I'm a little disappointed.
What I was hoping for:
Make underscore's methods more object oriented:
_.invoke(myCollection, 'method'); ==> myCollection.invoke('method');
I'll admit, minor difference, yet still it seems nice.
What problems will I run into if I use Backbone.Collection for non-Backbone.Models?
Are there any existing implementations, or a simple way to make a generic underscore collection class?
While you can't use a Backbone Collection without using models, I came up with a clever way to mix in underscore into the Array prototype:
// This self-executing function pulls all the functions in the _ object and sticks them
// into the Array.prototype
(function () {
var mapUnderscoreProperty = function (prp) {
// This is a new function that uses underscore on the current array object
Array.prototype[prp] = function () {
// It builds an argument array to call with here
var argumentsArray = [this];
for (var i = 0; i < arguments.length; ++i) {
argumentsArray.push(arguments[i]);
}
// Important to note: This strips the ability to rebind the context
// of the underscore call
return _[prp].apply(undefined, argumentsArray);
};
};
// Loops over all properties in _, and adds the functions to the Array prototype
for (var prop in _) {
if (_.isFunction(_[prop])) {
mapUnderscoreProperty(prop);
}
}
})();
Here is an example of how to use the new Array prototypes:
var test = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
console.log(test.filter(function (item) {
return item > 2 && item < 7;
})); // Logs [3, 4, 5, 6]
console.log(test); // Logs the entire array unchanged
This solution might add more to the Array prototype than is actually useful, but it gets you the bulk of the functions. Another solution would be to only add functions that have an iterator argument, but this is a start for you.