javascript for loop closure - javascript

i think i sort of understand the concept of hoisting when var is used to initialize i in a for loop and the concept of function closure. but why does the following code do not display an error as the final value of i is 4 and arr[3] is the highest index for arr?
why isn't the i in arr[i] linked to the i declared in the for-loop: var i = 0 ? the point of this question is to understand the concept instead of getting the solution
var arr = [1, 2, 3, 4];
for (var i = 0; i<arr.length; i++){
console.log(arr[i])
}
compared to this, the output shows the final value of i (ie 3) three times. i know that let should be used here
var funcs = [];
for (var i = 0; i < 3; i++) {
funcs[i] = function() {
console.log("My value: " + i);
};
}
for (var j = 0; j < 3; j++) {
funcs[j]();
}

In the first snippet the highest value is 4 but the max array index is 3 And since your loop is using array index length as it's max value, it doesn't go beyond the max index.
To understand the second example you'll need understand the difference between var and let.
The most basic explanation is var scopped to it's immediate function body (in your case outside of for loop, the same scope as funcs array)
while let scopped inside the enclosure (inside the for loop) and it basically creates create a new variable with each iteration of the loop, while var reuses the same variable.

Related

Find k-th element of an array

i am trying to figure out how this code works.
function Kth_greatest_in_array(arr, k) {
for (let i = 0; i < k; i++) {
let max_index = i;
const tmp = arr[i];
for (let j = i + 1; j < arr.length; j++) {
if (arr[j] > arr[max_index]) {
max_index = j;
}
}
arr[i] = arr[max_index];
arr[max_index] = tmp;
}
return arr[k - 1];
}
console.log(Kth_greatest_in_array([1,2,6,4,5], 3))
As you can see the goal is to find third biggest value. But i don know how the second loop works. Could you explain it to me step by step. For example, what is the meaning of veriable j, especially why they typed let j = i +
This method basically performs an in-place sort of the arr array (but only "k" times - not a full sort), then returns the element (from the now partially sorted array) at index k-1.
The inner for loop looks through the remaining (unsorted) portion of the array to find the index of the item with the highest value:
if (arr[j] > arr[max_index]) {
max_index = j;
}
The specific statement you were asking about, let j = i + 1, means "declare a new variable, j, and set its initial value to one greater than the current iteration of the outer for loop". "j" is arbitrary and could be any valid identifier, it's just often used by convention when you have nested for loops since the outermost loop usually uses i (which is presumably short for "index").
At the end of each iteration of the outer for loop, these lines swap array elements so that this element is in the correct position:
arr[i] = arr[max_index];
arr[max_index] = tmp;
If you're curious to really understand the inner workings of a method like this, I'd highly encourage you to step through it with a debugger - it can be a great learning experience. Even just the tried-and-true method of sprinkling some temporary console.log statements can help illuminate what's going on (e.g., print the state of arr and max_index after every outer and inner loop iteration).

javascript choices for looping over select options

Recently I stole some javascript to select an option in a select element:
var el=document.getElementById('mySelect');
var opts=el.options;
for (var opt, j = 0; opt = opts[j]; j++) {
if (opt.value == 'Apple') {
el.selectedIndex = j;
break;
}
}
It works fine, but as I looked at it I realized it was different from what I would have written:
var el=document.getElementById('mySelect');
for (var j = 0; j < el.options.length; j++) {
if (el.options[j].value == 'Apple') {
el.selectedIndex = j;
break;
}
}
In looking at the first code, what stops the loop if 'Apple' is not found? Which one is 'better'?
In either case, the second expression determines if the loop should continue or stop. In yours,
for (var j = 0; j < el.options.length; j++) {}
it's straightforward, j will increment and as long as j is less than the options length it's true, and at some point it is equal to the length and then it stops. In the other one,
for (var opt, j = 0; opt = opts[j]; j++) {}
the difference is that they are declaring a variable opt and in the second expression set it to the (also incrementing) array index. At some point, j goes beyond the bounds of the array, and opts[j] is undefined. And since an equality expression in JS is the value of the right site, that expression is also undefined, which is falsy, and the loop stops.
As for which is better? Both work. But as you had to scratch your head and wonder about one of them, how much do you want to depend on code that's more difficult to read?
Long story short:
In your for loop var opt is executed once after which you have subsequent opt = data[i].
This is important because:
var opt, data = [1, 2]; // defined once
console.log(opt = data[0]) // 1
console.log(opt = data[1]) // 2
console.log(opt = data[2]) // undefined
When the for loop condition evaluates to falsy it will halt.
So in your case it does stop when opt = data[i] ends up being a falsy value - undefined
Which is at the point of i being 5 and data[i] becoming undefined and being assigned to opt ... which returns as a final result undefined.
You should also consider using this (if ES6 is an option):
var el=document.getElementById('mySelect');
el.selectedIndex = el.options.findIndex(x => x.value === 'Apple')
It is shorted and easier to read :).

Javascript Closures: Understanding difference between let and var with an example [duplicate]

This question already has answers here:
What is the difference between "let" and "var"?
(39 answers)
Closed 5 years ago.
Although i understand that let allows you to declare variables that are limited in scope to the block, I encountered a strange difference between let and var while using it with javascript closures. Here is my example:
With let:
function buildFunction() {
var arr = [];
for(var i = 0; i < 3; i++) {
let j = i; //Using let to assign j
arr.push(
function(){
console.log(j);
}
)
}
return arr;
}
var fs = buildFunction();
fs[0]();
fs[1]();
fs[2]();
The above code snippet outputs:
0
1
2
With var:
function buildFunction() {
var arr = [];
for(var i = 0; i < 3; i++) {
var j = i; //Using var to assign j
arr.push(
function(){
console.log(j);
}
)
}
return arr;
}
var fs = buildFunction();
fs[0]();
fs[1]();
fs[2]();
The above code snippet outputs the following:
2
2
2
My question is:
If U am using var inside a block and assigning it a value during execution, shouldn't it work exactly like let and store different copies of j in memory ?
Does javascript handle let and var differently within a closure ?
Any clarifications on this would be highly appreciated.
var scopes to the function; let scopes to a block of code.
In your example j is scoped to the function buildFunction() when you use var. This means your using the 'same' j in every function. When your for loop runs j is set to 0, then 1, then 2. When you then run the console logs your referencing the j that was set to 2, so you get 2 2 2.
When you use let, you scope j to that iteration of the for loop. This means every iteration has a 'different' j, and the console logs print what you expect them to print.
If you were using ES5 and needed to use var, you could replicate the let effect (have it print 0 1 2), by wrapping your original anonymous function with a self-executing function and passed j in as an argument. This would create a new scope for j in the self-executing function whose value is the value of j in the current iteration.
function buildFunction() {
var arr = [];
for(var i = 0; i < 3; i++) {
var j = i; //Using var to assign j
arr.push(
//self executing function
(function(j) { //j here is scoped to the self executing function and has the value of j when it was called in the loop. Any changes to j here will not affect the j scope outside this function, and any changes to j outside this function will not affect the j scoped inside this function.
//original function
return function() {
console.log(j);
}
})(j) //call with the value of j in this iteration
)
}
return arr;
}
var fs = buildFunction();
fs[0]();
fs[1]();
fs[2]();

Javascript closure with for loop

I gave up on this after doing some research. My question is below:
From the post, I understand the following code.
var funcs = {};
for (var i = 0; i < 3; i++) { // let's create 3 functions
funcs[i] = function() { // and store them in funcs
var item = "item" + i; // inside
console.log("item: " + item + ", i: " + i); // each should log its value.
};
}
for (var j = 0; j < 3; j++) {
funcs[j](); // and now let's run each one to see
}
Result:
item: item3, i: 3
item: item3, i: 3
item: item3, i: 3
But if I change the move var item = "item" + i outside of function(), the result will be different. Why is this?
var funcs = {};
for (var i = 0; i < 3; i++) { // let's create 3 functions
var item = "item" + i; // outside
funcs[i] = function() { // and store them in funcs
console.log("item: " + item + ", i: " + i); // each should log its value.
};
}
for (var j = 0; j < 3; j++) {
funcs[j](); // and now let's run each one to see
}
Result:
item: item2, i: 3
item: item2, i: 3
item: item2, i: 3
The key problem you're facing is that a closure capture variables by reference, not by value. For example:
var i = 0;
var f = function(){ i++; console.log(i); }
var g = function(){ i++; console.log(i); }
f(); g(); f(); g();
this will display 1, 2, 3, 4 because f and g are not capturing the value of the variable i, but the variable itself and therefore are both mutating the same variable.
A very common mistake is to create multiple closures in a loop forgetting that all of them will capture the same variable (the one used in the loop) and thus that when this loop variable is incremented all the already created closures will see it changing. For example in your code
for (var i = 0; i < 3; i++) {
funcs[i] = function() {
var item = "item" + i; // inside
console.log("item: " + item + ", i: " + i);
};
}
item is a variable local to the function but i instead comes from the outside and it's shared between all of them. This can be seen for example with
i = 42;
funcs[0](); funcs[1](); funcs[2](); // Displays 42 three times
i = 99;
funcs[0](); funcs[1](); funcs[2](); // Now they all display 99
In the second case of your question things are just slightly different because in
for (var i = 0; i < 3; i++) {
var item = "item" + i;
funcs[i] = function() {
console.log("item: " + item + ", i: " + i);
};
}
also the item variable is now shared as it's defined outside the function and the closure captures it. In the display you see the last value that's assigned to it.
The key point to remember that in Javascript a variable is local to the function, not local to the {...} block (like it's in C++ for example) and thus the discriminating factor is if the variables are declared inside the function or outside the function, not if they're declared inside or outside the loop.
To overcome this common problem and have a separate index for each of the closures created in a loop often you will see code like
var f = (function(i){return function(){ ... };})(i);
where a function is created and immediately called to return another function.
The reason is that the only way to create a separate new variable is a function, leaving us with the pattern of creating a function that returns a function: the useful closure (the inner) in this case will capture a variable that is different for each of the iterations (because it's a parameter of the outer).
Changing your code to
for (var i = 0; i < 3; i++) {
funcs[i] = (function(i){return function() {
var item = "item" + i;
console.log("item: " + item + ", i: " + i);
};})(i);
}
Will work as expected (i.e. each function will now have its own i value).
EDIT
In current Javascript all the above applies to var variables (that are function scoped) but you can also use let variable that are instead block scoped.
This means the code can now be simplified and for example with:
lambdas = [];
for (let i=0; i<10; i++) {
let j = i*i; // Variable j is block scoped
lambdas.push(function(){ return j; });
}
console.log(lambdas[3]()); // ==> output 9
console.log(lambdas[5]()); // ==> output 25
(with var j = i*i; instead you'll get 81 for both)
In the first case the item variable doesn't exist until the function is run, at which point it is local to each function and gets set equal to "item" + i. And i is 3 because it is declared in the outer scope and 3 is what it was when the loop ended.
In the second case, the item variable exists in the functions' containing scope - the same scope as i - and it has been set to "item2" during the last iteration of the first loop. Inside that loop i had the values 0, 1, 2 - it only got set to 3 when the loop ended.

JavaScript: TypeError with array of functions

So I began to mess around with JavaScript today, and I came across a funny case where it seems convinced that something is a function while also convinced that it's not a function. This code illustrates the issue:
var arr1 = Array(1)
for (i = 0; i < arr1.length; i++) {
arr1[i] = function(n) { return n + i }
}
var arr2 = Array(1)
for (j = 0; j < arr2.length; j++) {
arr2[j] = function(n) { return arr1[j](n) }
}
typeof arr2[0] // "function"
arr2[0](2) // TypeError: Property '1' of object [object Array] is not a function
From here, you can assign a variable to arr2[0], and the error persists. I'm not sure if the closures or arrays are necessary to replicate this.
Is there something off with my code, or is this just one of those JavaScript oddities? This isn't something I particularly need an answer to, but it's a little silly so I'd like to know if there's a reason for it.
This actually does have a bit to do with closures.
First, take this code:
for (j = 0; j < arr2.length; j++) {
arr2[j] = function(n) { return arr1[j](n) }
}
The variable j initially holds the value 0, thus arr2[0] is set to a reference to a function.
Next, j is incremented, and now has a value of 1. This terminates the loop.
Now, when the function is called:
return arr1[j](n)
Due to closures, j still has its final value of 1. Which is an invalid index in that array.
One more thing to point out. A for loop doesn't create a new closure, so if you're expecting the anonymous function to enclose the value of j within that iteration, that assumption is wrong. There will be a single instance of j within the entire function that j was declared in.
I think you are trying to make use of closure inside the loop, but it doesn't work like that.
If you want to work with the value of i and j in each step of the loop, you have make make use of an anonymous function as shown below.
var arr1 = Array(1)
for (i = 0; i < arr1.length; i++) {
arr1[i] = (function(i){
return function(n) {
return n + i
};
})(i);
}
var arr2 = Array(1)
for (j = 0; j < arr2.length; j++) {
arr2[j] = (function(j){
return function(n) {
return arr1[j](n)
}
})(j);
}
typeof arr2[0]
arr2[0](2)
Demo: Fiddle
It's not doing anything weird.
You're just trying to something that isn't a function (the value of arr1[1], which is undefined).
It's probably safer to initialize arrays in the form:
var arr1 = [];
Or if you're using 'Array' you should initialize it like this:
var arr1 = new Array();

Categories

Resources