I've run into this error multiple times now, and wondering if there's a way to have the code auto control for it?
var length = some_array.length
for (var i = 0; i < length; i ++) {
someMethod(some_array[i].name)
}
function someMethod(name) {
var length = other_array.length
for (var i = 0; i < length; i ++ ) {
// some code
}
}
The code above doesn't run properly because the inner i and length carry over to the outer level code. So to actually make it work, I have to manually set different variables to manage the scoping. I.e., something like the below works:
var length = some_array.length
for (var i = 0; i < length; i ++) {
someMethod(some_array[i].name)
}
function someMethod(name) {
var s_length = other_array.s_length
for (var s_i = 0; s_i < length; s_i ++ ) {
// some code
}
}
But of course, it's hard to always remember when an inner function has another loop in it. Wondering if there's a way to control it so that loop variables are always only constrained to THAT loop itself.
This is why let keyword is a better option than var- it keeps the scope to just the function or method that calls it.
The following snippet consoles -
0 / test 1 / 0 / test a
0 / test 1 / 1 / test b
1 / test 2 / 0 / test a
1 / test 2 / 1 / test b
ie: for each item of some_array - it calls someMethod and consoles the name of the first array as well as each iteration of the second array (other_array).
let some_array=[{id: '1', name: 'test 1'},{id: '2', name: 'test 2'}];
let other_array=[{id: 'a', name: 'test a'},{id: 'b', name: 'test b'}];
let length = some_array.length;
for (let i = 0; i < length; i ++) {
someMethod(i, some_array[i].name)
}
function someMethod(index, name) {
let length = other_array.length
for (let i = 0; i < length; i ++ ) {
console.log(index + ' / ' + name + ' / ' + i + ' / ' + other_array[i].name);
}
}
SCOPE
In JavaScript there are two types of scope:
Local scope
Global scope
JavaScript has function scope: Each function creates a new scope.
let allows you to declare variables that are limited in scope to the block.
Notice how var create a property on the global object but let is undefined.
var x = 'global';
let y = 'global';
console.log(this.x); // "global"
console.log(this.y); // undefined
Let's try it with your code.
let some_array = [{ name: 'Angel' }, { name: 'James' }]
let other_array = ['0','1'];
//let i is declared local
for (let i = 0; i < some_array.length; i++) {
someMethod(some_array[i].name) //"let" some_array variable is required here to the local "for loop" scope.
}
function someMethod(name) {
//let i is declared local here again so it's considered a new variable i.
for (let i = 0; i < some_array.length; i++) {
console.log(name)
}
}
For more examples about scope in Javascript
Related
So, in this code I have a string of 0's and 1's and the length of the string is 32, which will be split in 6 equal parts but the last part will have the length of 2 so I will add (4) 0's after that which will make its length 6. So I wrote a function that will add the remaining 0's which is padding(num).
And that function will be invoked in side the slicing(str) function.
But the code breaks when I try to do execute.
Any help?
Thanks.
// This code works.
function padding0s(num) {
let s = "";
for (i = 0; i < 6 - num; i++) {
s += "0";
}
return s;
}
function slicing(str) {
let k = 6;
let res = [];
let temp1 = 0;
let f = padding0s(2);
for (i = 0; i < str.length; ) {
res.push(str.slice(i, k));
i += 6;
k += 6;
if (res[temp1].length !== 6) {
res[temp1] += f;
}
temp1++;
}
console.log(res);
}
slicing("01000011010011110100010001000101");
// But this does not..
function padding0s(num) {
let s = "";
for (i = 0; i < 6 - num; i++) {
s += "0";
}
return s;
}
function slicing(str) {
let k = 6;
let res = [];
let temp1 = 0;
for (i = 0; i < str.length; ) {
res.push(str.slice(i, k));
i += 6;
k += 6;
if (res[temp1].length !== 6) {
let f = padding0s(res[temp1].length);
res[temp1] += f;
}
temp1++;
}
console.log(res);
}
slicing("01000011010011110100010001000101");
Always define variables before using them
Not doing so can result in undefined behaviour, which is exactly what is happening in your second case. Here is how:
for (i = 0; i < str.length; ) {...}
// ^ Assignment to undefined variable i
In the above for-loop, by using i before you define it, you are declaring it as a global variable. But so far, so good, as it doesn't matter, if not for this second problem. The real problem is the call to padding0s() in your loop. Let's look at padding0s:
function padding0s(num) {
...
for (i = 0; i < 6 - num; i++) {
s += "0";
}
}
This is another loop using i without defining it. But since i was already defined as a global variable in the parent loop, this loop will be setting its value. So in short, the value of i is always equal to 6 - num in the parent loop. Since your exit condition is i < str.length, with a string of length 32 the loop will run forever.
You can get around this in many ways, one of which you've already posted. The other way would be to use let i or var i instead of i in the parent loop. Even better is to write something like this (but beware that padEnd may not work on old browsers):
function slicing(str) {
return str.match(/.{1,6}/g).map((item) => {
return item.padEnd(6, "0");
});
}
console.log(slicing("01000011010011110100010001000101"));
I wrote a simple javascript code. My for loop iterates a "let" declared variable, i between 0 and 2. A function gets declared within the loop only when i == 2. The function has to return the value of i variable. When I call this function from outside the loop, the function returns the value of i = 2 (which is natural for a block scope variable i. However, when I rewrite the loop code as its non-loop equivalent code-block, the function (still called from outside the block) returns the vale of i = 3. What is going on?
"use strict";
var printNumTwo;
for (let i = 0; i < 3; i++) {
if (i === 2) {
printNumTwo = function() {
return i;
};
}
}
console.log(printNumTwo()); //returns 2
// loop equivalent
{
let i = 0;
i = 1;
i = 2;
printNumTwo = function() {
return i;
}
i = 3;
}
console.log(printNumTwo()); // returns 3
Your example is bad because your loop is not counting after 2. So If your loop looks like i <= 3:
for (let i = 0; i <= 3; i++) {
if (i === 2) {
printNumTwo = function() {
return i;
};
}
}
You would get exactly same result as your non-loop example and that's because of closure in javascript but return breaks for loop. Your function is saving reference to that variable from outside scope.
It's because you're actually setting the function to return the value 3 because of the non-loop environment. You should change the loop a little, adding another variable, but first make your function look like this:
printNumTwo = function() {
return num;
}
And in your simulated loop:
i = 2;
num = i;
printNumTwo = function() {
return num;
}
i = 3;
In your non loop based code, printNumTwo is not executed at the same point of its declaration and so the value of i is updated before it is executed so the value 3 is returned.
{
let i = 0;
i = 1;
i = 2;
printNumTwo = function () {
return i;
}
i = 3;
}
console.log(printNumTwo());
but if you run the following code, it should print 2 since it is executed before value if i is set to 3
{
let i = 0;
i = 1;
i = 2;
printNumTwo = (function() {
console.log(i);
})()
i = 3;
}
Note: return in for loop breaks the further execution of the loop, so even if your first code had i <= 3 as its breaking condition, it will return 2.
for (let i = 0; i <= 3; i++) {
if (i === 2) {
printNumTwo = function() {
return i;
};
}
}
console.log(printNumTwo())
"use strict";
var printNumTwo;
for (let i = 0; i < 3; i++) {
printNumTwo = function (i) {
// when references 'i' in this function, 'i' goes to the global scope.
return i;
};
// set the value 3 for 'i' in the global scope
i = 3;
}
console.log(printNumTwo()); // return 3;
try this
"use strict";
var printNumTwo;
for (let i = 0; i < 3; i++) {
printNumTwo = function (i) {
return i;
}.bind(null, i); // you set the current value as parameter = 0
i = 3; // i = 3 and break loop
}
console.log(printNumTwo()); // return 0;
try this
"use strict";
var printNumTwo;
for (let i = 0; i < 3; i++) {
let i = 0;
i = 1;
i = 2;
printNumTwo = function (i) {
return i;
}.bind(null, i); // you set the current value as parameter = 2
i = 3; // i = 3 and break loop
}
console.log(printNumTwo()); // return 2;
I appreciate all the answers I got to my question. All pointing to the case of how a function, when called, handles the environments in which it was both called and created. I read this useful explanation in the book "Eloquent JavaScript" and think it would be good to share it,
"A good mental model is to think of function values as containing both the code in their body and the environment in which they are created. When called, the function body sees the environment in which it was created, not the environment in which it is called."
~ Eloquent_JavaScript/Closure
I have a javascript function as below :
function modifyx(xvalue) {
val = 1;
for (x = 0; x < 10; x++) {
val = val + 1;
}
return val;
}
And the main snippet passes a variable named x to the above function as below:
for (x = 0; x < 10; x++) {
console.log(modifyx(x));
}
The expected output should be "11" printed 10 times but instead it prints one time. The function call changes the value of x eventhough i am not modifying the passed value. The x inside the function has it's own scope. Yet it gets modified. Any help on this would be highly appreciated.
The variable x in your code is global. When your method modifyx(xvalue) returns for the first time the value of x is already 11 which is used in the for loop for the second iteration. Thus it fails to execute the method further.
Use let x in the declaration in for (x = 0; x < 10; x++) to create a unique execution environment.
function modifyx(xvalue) {
val = 1;
for (x = 0; x < 10; x++) {
val = val + 1;
}
return val;
}
for (let x = 0; x < 10; x++) {
console.log(modifyx(x));
}
Just put var in both places in for loop
function modifyx(xvalue) {
val = 1;
for (var x = 0; x < 10; x++) {
val = val + 1;
}
return val;
}
for (var x = 0; x < 10; x++) {
console.log(modifyx(x));
}
because its taking x as global variable in your code.
MDSN documentation on variable scopes in JavaScript:
JavaScript has two scopes: global and local. A variable that is
declared outside a function definition is a global variable, and its
value is accessible and modifiable throughout your program. A variable
that is declared inside a function definition is local. It is created
and destroyed every time the function is executed
When using the var keyword, the variable declaration is pulled to the top of it's scope (the function).
In your example code, you are using a for loop without defining var x. When you use for (x = 0..., the browser assumes you meant for (var x = 0... so it creates a global variable called x.
Inside your function modifyx, you are setting the global x to 11 by the end of it's execution. This causes the outer loop to run only once because x is no longer less than 10 after the first loop.
Here is an example of what's going on with comments:
function modifyx(xvalue) {
val = 1;
for (x = 0; x < 10; x++) { // sets the global 'x' to 11 after loop finishes
val = val + 1;
}
return val;
}
// 'x' is being defined as a global variable
for (x = 0; x < 10; x++) {
console.log(modifyx(x)); // modifyx sets 'x' to 11
// x is now 11, loop will now quit
}
A simple solution is to use the let keyword in both loops, thus restricting the scope.
for (let x = 0; x < 10; x++) { ... }
My Inner loop seems to work fine, but once the inner loop is complete i expect the first loop to start again but it doesn't and it's not clear to me why...
if(search)
{
// loop through Revenue Arrangements
for (var x = 0; search != null && x < search.length; x++)
{
var revenue_arrangement = nlapiLoadRecord('revenuearrangement', search[x].getValue(columns[0]));
nlapiLogExecution('DEBUG', 'Revenue Arrangement Internal ID', search[x].getValue(columns[0]));
var count = revenue_arrangement.getLineItemCount('revenueelement');
for (var x = 1; x <= count; x++)
{
var rev_element_id = revenue_arrangement.getLineItemValue('revenueelement', 'transactionline', x);
if(rev_element_id)
{
nlapiLogExecution('DEBUG', 'Element Internal ID', rev_element_id);
}
}
nlapiLogExecution('DEBUG', 'End of Inner Loop);
}
}
your both loops (inner and outer) are using the same variable as counter (x)
Use different counter variables for both
if(search)
{
// loop through Revenue Arrangements
for (var x = 0; search != null && x < search.length; x++)
{
var revenue_arrangement = nlapiLoadRecord('revenuearrangement', search[x].getValue(columns[0]));
nlapiLogExecution('DEBUG', 'Revenue Arrangement Internal ID', search[x].getValue(columns[0]));
var count = revenue_arrangement.getLineItemCount('revenueelement');
for (var y = 1; y <= count; y++)
{
var rev_element_id = revenue_arrangement.getLineItemValue('revenueelement', 'transactionline', y);
if(rev_element_id)
{
nlapiLogExecution('DEBUG', 'Element Internal ID', rev_element_id);
}
}
nlapiLogExecution('DEBUG', 'End of Inner Loop);
}
}
You're reusing the x variable and the nested iteration affects the outer one. This was stated clearly in other answer, but let's extend this to point to two aspects of javascript you might be not aware of, but you should:
Javascript uses block operators {} but it does not implement block
scope. This means that a for loop does not create a new variable
scope. This is significant if you come from c/c++ background.
See more:
http://doctrina.org/JavaScript:Why-Understanding-Scope-And-Closures-Matter.html
(also see for closures, that is how outer scope affects nested function scopes).
Javascript hoists variable declarations to the beginning of a
function scope. This means both of var x are effectively declared
in the very beginning of the snippet included in the question (or
possibly even earlier).
See more:
http://www.w3schools.com/js/js_hoisting.asp
Extra var hoisting example which shows how bizzare can be its effects: http://www.adequatelygood.com/JavaScript-Scoping-and-Hoisting.html
The above means that your code (simplified) of this:
var search = ["a","b","c","d"], count;
if(true) {
for (var x = 0; x < search.length; x++){
count = 2;
for (var x = 1; x <= count; x++){
console.log("innner", x)
}
console.log("outer", x)
}
}
is the same as this:
var search = ["a","b","c","d"], count, x = 1;
if(true) {
for (; x < search.length; x++){
count = 2;
for (; x <= count; x++){
console.log("innner", x)
}
console.log("outer", x)
}
}
That means you don't only have two loops that affect each other. You're also ignoring the 0 element of the first array (the var x = 1 in nested loop overrides var x = 0 in the outer).
Both snippets will output:
innner 1
innner 2
outer 3
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.