Reputation: 47
First of all I am not too familiar with javascript. Actually I try to avoid it if possible but the following behaviour bugs me:
The following is a minimal example:
I have a 2 dimensional Array (an array of objects) and I decided to split the decomposition of it into two functions, for better readability. The first function (foo(), foo2()) seperates the different objects, and the second function (bar()) goes through an individual object (in this case just prints the components).
To loop through the array I use a for loop. Now I expected (coming from c++) no difference between useing foo() and foo2(). Since bar() is a seperate function with a seperate scope (or is it??). BUT useing foo() I get the output: 123 Useing foo2() I get the desired: 123456
Now the only difference is the name of the index within the loop. foo() uses the same index as bar() foo2() uses a different index as bar()
So what am I missing here?
Now I understand that giving an object / array to a function will result in a call by reference. But why does this affect the index of the loop?
I have the suspicion that javascript passes both "a" and "i" rather than passing the object behind "a[i]" to bar(a[i]).
If so, why? And how can I avoid this?
// decompose the different objects
function foo(a){
for(i = 0; i < a.length; i++){
bar(a[i]);
}
}
// decompose the different objects
function foo2(a){
for(j = 0; j < a.length; j++){
bar(a[j]);
}
}
// decompose a single object
function bar(b){
for(i = 0; i < b.length; i++){
document.write(b[i]);
}
}
function start(){
//2-d array
var a = [
[1, 2, 3],
[4, 5, 6]
];
foo(a);//decompose the array -> unexpected behaviour
document.write("</br>");
foo2(a);//decompose the array -> expected behaviour
}
//run
start();
calling foo() I get the output: 123 expecting 123456
calling foo2() I get the desired output: 123456
edit: See for short answer @MrGeek, and for detailed Answer @Akrion Thank you both!
Lesson learned:
Upvotes: 1
Views: 70
Reputation: 18525
In a nut shell the issue you are experiencing comes from the fact that JS handles varaibles
which are not specifically declared in the execution scope with let
/var
as global.
function foo(a){
for(i = 0; i < a.length; i++){
bar(a[i]);
}
}
In the above function the variable i
is set to 0. JS would fist look in the current scope and try to find a
declaration of i
. If it can not it would go to the scope above it and if it is not defined there AND the use strict
is not set
it would define globally that variable. So now you have i
already defined globally by JS. Then:
function bar(b){
for(i = 0; i < b.length; i++){
document.write(b[i]);
}
}
In the bar
function we get i
again. This time however JS knows about it since it global defined it already.
So it uses the i
defined already and you get the picture form there.
The obvious fix for this is to block scope the i
with var i=0
or let i=0
.
In your scecific case var
would do just fine. Using var
would limit the scope of your i
just to that for loop
and JS would define i
in the foo
function and then it would define a new i
for the bar
function.
let
in this case would do the same and overall it can be used as a replacement for var
(although many would argue
about that and point out that both are still useful and let
is not a panacea and a total replacement of var
)
now let
is different from var
in a scenario like this:
var arr = []
for (var i = 1; i <= 3; i++) {
var obj = { print: () => console.log(i) }
arr.push(obj);
}
arr[0].print()
arr[1].print()
arr[2].print()
As you can see all of the print
functions print the same exact value 4
instead of what you would expect.
A simple change from var
to let
would give you the desired result:
var arr = []
for (let i = 1; i <= 3; i++) {
var obj = { print: () => console.log(i) }
arr.push(obj);
}
arr[0].print()
arr[1].print()
arr[2].print()
This is due to the fact that let preserves its lexical block scope
and thus when passed into the print
function
it keeps its current value where var
would be overwritten with the last actual value which in this case is 4.
So with if we simply change your code to use var (and print to the console):
// decompose the different objects
function foo(a) {
for (var i = 0; i < a.length; i++) {
bar(a[i]);
}
}
// decompose the different objects
function foo2(a) {
for (var j = 0; j < a.length; j++) {
bar(a[j]);
}
}
// decompose a single object
function bar(b) {
for (var i = 0; i < b.length; i++) {
console.log(b[i]);
}
}
function start() {
//2-d array
var a = [
[1, 2, 3],
[4, 5, 6]
];
foo(a); //decompose the array -> expected behaviour
foo2(a); //decompose the array -> expected behaviour
}
//run
start();
You will see that we get the expected result. let
would do the same :)
Upvotes: 1
Reputation: 22776
Your indices are global variables, declare them with let
or var
to make them local to their corresponding functions and avoid this problem:
// decompose the different objects
function foo(a) {
for (let i = 0; i < a.length; i++) {
bar(a[i]);
}
}
// decompose the different objects
function foo2(a) {
for (let j = 0; j < a.length; j++) {
bar(a[j]);
}
}
// decompose a single object
function bar(b) {
for (let i = 0; i < b.length; i++) {
document.write(b[i]);
}
}
function start() {
//2-d array
var a = [
[1, 2, 3],
[4, 5, 6]
];
foo(a); //decompose the array -> expected behaviour
document.write("</br>");
foo2(a); //decompose the array -> expected behaviour
}
//run
start();
Upvotes: 3