Programmatically setting third statement of for loop - javascript

Wondering if there is by any chance to programmatically setting third statement of forloop
var conditionProgrammatically = 'i++';//or 'x--'
for (var i = 0; i < 10; conditionProgrammatically) {
console.log(i)
}

You can use any expression you want there including calling a function. You just need to be careful of scope. So, for example, this works:
var conditionProgramatically = () => i++ ;
for (var i = 0; i < 10; conditionProgramatically()) {
console.log(i)
}
But it depends on the fact that var i is in a scope shared by the function. This, however, doesn't work:
var conditionProgramatically = () => i++ ;
for (let i = 0; i < 10; conditionProgramatically()) {
console.log(i)
}
Because let is scoped to the block and not available.
Of course you can share an object which is mutable by passing it as an argument like:
fn = (o) => o.i += 1
for (let o = {i:0}; o.i < 10; fn(o)) {
console.log(o.i)
}
This allows you to use let, but is a little hard on the eyes.
All said, it's probably going to be easier to make your logic fit in a simple expression rather than calling a function. You can still perform some logic, though:
for (let i = 0; Math.abs(i) < 10; i = Math.random() > .65 ? i -1: i + 1) {
console.log(i)
}

You can set a variable and then operate with this variable according to your needs.
(remember that i-- is equivalent to i -= 1).
BTW, be careful because you would also have to change the condition, if not you will end up in an infinite loop. In your case, I would use abs()
var step = 1; // or var step = -1;
for (var i = 0; abs(i) < 10; i += step) {
console.log(i)
}

Usually, in functional programmings (like python and javascript), we can use dictionary (or objects) to store functions.
var myFunctions = {
"a": function (i) { return i + 1 },
"b": function (i) { return i - 3 }
};
Then, we can set the condition as the key to the dictionary:
myCondition = "a"; // this will set condition to increment by 1
Here is your for loop:
for (i = 0; i < n; i = myFunctions[myCondition](i)) {
// whatever
}

Related

Using var in functions with multiple loops?

Quoting from the book JavaScript: The Definitive Guide
Unlike variables declared with let, it is legal to declare the same variable multiple times with var. And because var variables have function scope instead of block scope, it is actually common to do this kind of redeclaration. The variable i is frequently used for integer values, and especially as the index variable of for loops. In a function with multiple for loops, it is typical for each one to begin for(var i = 0; .... Because var does not scope these variables to the loop body, each of these loops is (harmlessly) re-declaring and re-initializing the same variable.
I am unable to understand what is meant in this paragraph. First I assumed the following would not have worked with second loop not iterating, because i would be function scoped:
(function foo() {
for (let i = 0; i < 2; i++) {
console.log(i);
}
for (let i = 0; i < 2; i++) {
console.log(i);
}
})();
but it prints
0
1
0
1
Then I assumed this would have printed 0 0 1 1 0 1, which is also not the case:
(function foo() {
for (var i = 0; i < 2; i++) {
console.log(i);
for (var i = 0; i < 2; i++) {
console.log(i);
}
}
})();
Can someone help me understand what is meant by
in a function of multiple for loops, var can be used harmlessly in each loop
and how it is different to let?
To me it looks like the opposite it true (which is also very confusing) where let can be used harmlessly in functions with multiple loops:
(function foo() {
for (let i = 0; i < 2; i++) {
console.log(i);
for (let i = 0; i < 2; i++) {
console.log(i);
}
}
})();
prints:
0
0
1
1
0
1
let variables are forbidden to be re-declared in the same scope, like the following:
let foo = 10;
let foo = 20;
var variables can be, though:
var foo = 10;
var foo = 20;
console.log('ok');
In your first snippet, variables declared with let in the header of a for loop only exist inside the loop; they're block scoped, and aren't scoped to the outer block or function:
(function foo() {
for (let i = 0; i < 2; i++) {
// console.log(i);
}
for (let i = 0; i < 2; i++) {
// console.log(i);
}
// doesn't exist out here:
console.log(i);
})();
The let is above create is in two different scopes, which is why it's not forbidden. Similarly:
(function foo() {
{
let i = 10;
}
{
let i = 20;
}
console.log('OK up until here');
// doesn't exist out here:
console.log(i);
})();
vars, in contrast, have function scope, not block scope - with your var version of the loop, since there's only one function, this:
(function foo() {
for (var i = 0; i < 2; i++) {
console.log(i);
for (var i = 0; i < 2; i++) {
console.log(i);
}
}
})();
is equivalent to
(function foo() {
var i; // the `i` variable exists at this level
for (i = 0; i < 2; i++) {
console.log(i);
for (i = 0; i < 2; i++) {
console.log(i);
}
}
})();
Then I assumed this would have printed 0 0 1 1 0 1, which is also not the case:
i gets initialized to zero at the beginning of each loop.
The first loop begins once, at the start of the function.
The second loop begins just after logging i, on each iteration of the outer loop.
(function foo() {
for (var i = 0; i < 2; i++) {
console.log(i);
for (var i = 0; i < 2; i++) {
console.log(i);
}
}
})();
outer loop initialization: 0 is assigned to i
0 gets logged
inner loop initialization: 0 is assigned to i
0 gets logged
inner loop increments i to 1 and starts again
inner loop: 1 gets logged
inner loop increments i to 2 and breaks, since i < 2 is no longer fulfilled
outer loop increments i to 3 and breaks, since i < 2 is no longer fulfilled
So you get 0 0 1 logged.
What the article probably means by
in a function of multiple for loops, var can be used harmlessly in each loop
is that it can work if both loops are on the same level of the function, eg
for (...) {
}
for (...) {
}
It definitely won't work with nested loops, if both loops use the same variable, because there's only ever a single i variable, rather than one for each loop.
where let can be used harmlessly in functions with multiple loops:
For nested loops, yes, since variables declared with let are unique to each loop, rather than being shared across the whole containing function.

How does the let keyword work in this code? [duplicate]

I understand that let prevents duplicate declarations which is nice.
let x;
let x; // error!
Variables declared with let can also be used in closures which can be expected
let i = 100;
setTimeout(function () { console.log(i) }, i); // '100' after 100 ms
What I have a bit of difficulty grasping is how let applies to loops. This seems to be specific to for loops. Consider the classic problem:
// prints '10' 10 times
for (var i = 0; i < 10; i++) { process.nextTick(_ => console.log(i)) }
// prints '0' through '9'
for (let i = 0; i < 10; i++) { process.nextTick(_ => console.log(i)) }
Why does using let in this context work? In my imagination even though only one block is visible, for actually creates a separate block for each iteration and the let declaration is done inside of that block ... but there is only one let declaration to initialize the value. Is this just syntactic sugar for ES6? How is this working?
I understand the differences between var and let and have illustrated them above. I'm particularly interested in understanding why the different declarations result in different output using a for loop.
Is this just syntactic sugar for ES6?
No, it's more than syntactic sugar. The gory details are buried in §13.6.3.9
CreatePerIterationEnvironment.
How is this working?
If you use that let keyword in the for statement, it will check what names it does bind and then
create a new lexical environment with those names for a) the initialiser expression b) each iteration (previosly to evaluating the increment expression)
copy the values from all variables with those names from one to the next environment
Your loop statement for (var i = 0; i < 10; i++) process.nextTick(_ => console.log(i)); desugars to a simple
// omitting braces when they don't introduce a block
var i;
i = 0;
if (i < 10)
process.nextTick(_ => console.log(i))
i++;
if (i < 10)
process.nextTick(_ => console.log(i))
i++;
…
while for (let i = 0; i < 10; i++) process.nextTick(_ => console.log(i)); does "desugar" to the much more complicated
// using braces to explicitly denote block scopes,
// using indentation for control flow
{ let i;
i = 0;
__status = {i};
}
{ let {i} = __status;
if (i < 10)
process.nextTick(_ => console.log(i))
__status = {i};
} { let {i} = __status;
i++;
if (i < 10)
process.nextTick(_ => console.log(i))
__status = {i};
} { let {i} = __status;
i++;
…
I found this explanation from Exploring ES6 book the best:
var-declaring a variable in the head of a for loop creates a single
binding (storage space) for that variable:
const arr = [];
for (var i=0; i < 3; i++) {
arr.push(() => i);
}
arr.map(x => x()); // [3,3,3]
Every i in the bodies of the three arrow functions refers to the same
binding, which is why they all return the same value.
If you let-declare a variable, a new binding is created for each loop
iteration:
const arr = [];
for (let i=0; i < 3; i++) {
arr.push(() => i);
}
arr.map(x => x()); // [0,1,2]
This time, each i refers to the binding of one specific iteration and
preserves the value that was current at that time. Therefore, each
arrow function returns a different value.
let introduces block scoping and equivalent binding, much like functions create a scope with closure. I believe the relevant section of the spec is 13.2.1, where the note mentions that let declarations are part of a LexicalBinding and both live within a Lexical Environment. Section 13.2.2 states that var declarations are attached to a VariableEnvironment, rather than a LexicalBinding.
The MDN explanation supports this as well, stating that:
It works by binding zero or more variables in the lexical scope of a single block of code
suggesting that the variables are bound to the block, which varies each iteration requiring a new LexicalBinding (I believe, not 100% on that point), rather than the surrounding Lexical Environment or VariableEnvironment which would be constant for the duration of the call.
In short, when using let, the closure is at the loop body and the variable is different each time, so it must be captured again. When using var, the variable is at the surrounding function, so there is no requirement to reclose and the same reference is passed to each iteration.
Adapting your example to run in the browser:
// prints '10' 10 times
for (var i = 0; i < 10; i++) {
setTimeout(_ => console.log('var', i), 0);
}
// prints '0' through '9'
for (let i = 0; i < 10; i++) {
setTimeout(_ => console.log('let', i), 0);
}
certainly shows the latter printing each value. If you look at how Babel transpiles this, it produces:
for (var i = 0; i < 10; i++) {
setTimeout(function(_) {
return console.log(i);
}, 0);
}
var _loop = function(_i) {
setTimeout(function(_) {
return console.log(_i);
}, 0);
};
// prints '0' through '9'
for (var _i = 0; _i < 10; _i++) {
_loop(_i);
}
Assuming that Babel is fairly conformant, that matches up with my interpretation of the spec.
Recently I got confused about this problem too. According to the above answers, here is my understanding:
for (let i=0;i<n;i++)
{
//loop code
}
is equivalent to
// initial
{
let i=0
}
// loop
{
// Sugar: For-Let help you to redefine i for binding it into current block scope
let i=__i_value_from_last_loop__
if (i<=n){
//loop code
}
i++
}
Let us see “let” and “var” with the setTimeout majorly asked in the interview.
(function timer() {
for (var i=0; i<=2; i++)
{ setTimeout(function clog() {console.log(i)}, i*1000); }
})();
(function timer() {
for (let i=0; i<=2; i++)
{ setTimeout(function clog() {console.log(i)}, i*1000); }
})();
Let's see in detail how this code executes in the javascript compiler.
The answer for “var” is “222” due to functional scope and for “let” is “012” because it is block scope.
Now let us look at what it looks like in detail when it Compiles for "var". (It's a little hard to explain over the code than in audio or video but I am trying my best to give you.)
var i = 0;
if(i <=2){
setTimeout(() => console.log(i));
}
i++; // here the value of "i" will be 1
if(i <=2){
setTimeout(() => console.log(i));
}
i++; // here the value of "i" will be 2
if(i <=2){
setTimeout(() => console.log(i));
}
i++; // here the value of "i" will be 3
After the code is executed finally it will print all the console.log where the value of "i" is 6. So the final output is: 222
In "let i" will be declared in every scope.
The import point to be noted here is "i" will get the value from the previous scope and not from the declaration. (Below code is just an example of how it looks like in the compiler and trying it wont work)
{
//Scope 1
{
let i;
i= 0;
if(i<=2) {
setTimeout(function clog() {console.log(i)};);
}
i++; // Here "i" will be increated to 1
}
//Scope 2
// Second Interation run
{
let i;
i=0;
// Even “i” is declared here i= 0 but it will take the value from the previous scope
// Here "i" take the value from the previous scope as 1
if(i<=2) {
setTimeout(function clog() {console.log(i)}; );
}
i++; // Here “i” will be increased to 2
}
//Scope 3
// Second Interation run
{
let i;
i=0;
// Here "i" take the value from the previous scope as 2
if(i<=2) {
setTimeout(function clog() {console.log(i)}; );
}
i++; // Here "i" will be increated to 3
}
}
So, it will print "012" value as per the block scope.

Can for-loops counter be const declaration in ES2015?

First, let can be used on loop counter declaration. This is also described in MDN.
for(let i = 0; i < 2; ++i) {
setTimeout(function(){
document.write(i + "<br>");
}, 0);
}
result:
0
1
Since let is used, a value of i can be changed in inside of for block.
for(let i = 0; i < 2; ++i) {
setTimeout(function(){
document.write(i + "<br>");
}, 0);
i = 123; // I want to block this assignment!!
}
So I considered using const instead of let.
for(const i = 0; i < 2; ++i) { // throws Assignment to constant variable
setTimeout(function(){
document.write(i + "<br>");
}, 0);
i = 123; // not here...
}
However, this code throws Assignment to constant variable. (in Chrome 53.0.2773.0).
I expected this loops twice like first let example, but make counter i writable in internal for-loop iteration only.
I think this is allowed because ECMA-262 §13.7.4.7 seems to mention this situation as If isConst is true, ..., but I cannot find this usage (or implementation status) in MDN and other sites. Kangax's table contains tests for let bindings, but not for const bindings.
I tested it (in Firefox) and the const binding works according to spec:
let i = 0;
for (const len = 3; i < len; i++) {
console.log(i);
}
// From https://kangax.github.io/compat-table/es6/#test-const
for (const baz = 0; false;) {}
// Yay, a const counter! ...uh
for (const counter = {i: 0}; counter.i < 3; counter.i++) {
console.log(counter.i);
}
It is probably not particularly useful though...
Why does it work?
The standard says:
5. For each element dn of boundNames do
a. If isConst is true, then
i. Perform loopEnv.CreateImmutableBinding(dn, true).
...where boundNames refers to the const binding. As you can see, the standard allows const 'loop counters', but doesn't say that you will be able to re-assign (increment) them later on (which, in fact, does not work).

JavaScript: replace param with a local variable?

If I have a funtion like this:
function xyz(b)
{
for(var i = 0; i < b.length; i++)
{
// do something with b items...
}
}
... wouldn't it be more memory-friendly if I were to assign b to a local variable inside of that function before working with its items?
function xyz(b)
{
var c = b;
for(var i = 0; i < c.length; i++)
{
// do something with c items...
}
}
In your example both b and c are local variables since they only exist in the function. So your code will actually be a bit less performant.
Side note - if you want your code to be more performant you should calculate c.length only once for the whole for loop. In your example you're calculating it for every iteration of the loop. Instead you can do as follows:
for (var i = 0, cLen = c.length; i < cLen; i++)
This way it calculates it only once before starting the loop.

Javascript - Variable updating

This is probably an easy question but it's late at night and I can't get my head round this.
here's my code
$(document).ready(function () {
var items = getNumber();
for (var i = 0; i < items.length; i++) {
var test = items[i].action;
test();
}
});
function getNumber()
{
var items = [];
for (var i = 0; i < 5; i++) {
var num = i * 10;
items.push({ id: i, number: num, action: function () { alert(i) } });
}
return items
}
Could someone explain to me why the alert output is always 5? I want the alert parameter to be the index at the time it is added to the array. It seems like it is being dynamic.
If you could also post a solution how i could get this to work i would be extremely thankful
This is a common issue with JavaScript variable scoping: new variables are only introduced in a new execution context and thus, in the problematic code, i is shared across all the action callbacks.
Anyway, here is the corrected code following the standard idiom:
function getNumber()
{
var items = [];
for (var i = 0; i < 5; i++) {
var num = i * 10;
items.push({
id: i, number: num,
// _i is a new variable for each callback
action: (function (_i) {
// use separate (one per callback) _i variable
return function () { alert(_i) }
})(i) // pass in current value for loop
});
}
return items
}
Alternatively, if one doesn't like all the nesting, it's fine to use a "named" function to perform the same task. The key to point is that the closure is created (and returned from) a new execution context so that a different variable is closed-over:
function getNumber()
{
function mkAction (i) {
return function () { alert(i) }
}
var items = [];
for (var i = 0; i < 5; i++) {
var num = i * 10;
items.push({
id: i, number: num,
action: mkAction(i)
});
}
return items
}
Another approach is to use Function.bind from ES5:
function getNumber()
{
var items = [];
for (var i = 0; i < 5; i++) {
var num = i * 10;
items.push({
id: i, number: num,
action: (function (_i) { alert(_i) }).bind(null, i)
});
}
return items
}
(Note that Function.bind can be emulated using a new execution context/closure even without native browser support. See the MDC documentation.)
See also:
JavaScript closure inside loops – simple practical example
Passing functions to setTimeout in a loop: always the last value?
How do JavaScript closures work?

Categories

Resources