-1
function map(f, a) {
  for(var i in a) {
    a[i] = f(a[i]);
  }
}

var a = [0, 1, 2, 3];
map(function(x) { return x = x * x }, a);
console.log(a);

a: [0, 1, 4, 9]

However, If I change map(f, a) to:

function map(f, a) {
  for(var i in a) {
    f(a[i]);
  }
}
var a = [0, 1, 2, 3];
map(function(x) { return x = x * x }, a);
console.log(a);

a remains unchanged as: [0, 1, 2, 3]

I'm not sure what's happening here. It appears as if the interpreter considers a[i] as a reference to a property of the object a in map(f, a) but once passed into f it turns into a typeof number.

2
  • I expect a to change in the second example. Commented Dec 5, 2010 at 15:54
  • That would require pretty extreme call-by-reference - which doesn't exist in JS (just try var x = 0; function f(x) { x = 1 }; f(x)). You are throwing the result of f(a[i]) away! Commented Dec 5, 2010 at 16:08

4 Answers 4

1

Don't use for(... in...) on Arrays. Use a for loop.

Enumeration order with for in is NOT guaranteed. It will also iterate over properties (not the built ins but stuff that were set by JS code) and stuff on the prototype, and then all hell breaks loose.

Next thing, all values except for arrays and objects are pass by value (basically arrays and objects are also pass by value, but the value is a pointer to the object).

So you're not modifying the value inside the array in this case, but the local variable x which just happens to have them same value.

return x = x * x the assignment is superfluous.

Fixed version

function map(f, a) {
  for(var i = 0, l = a.length; i < l; i++) {
    a[i] = f(a[i]);
  }
}

var a = [0, 1, 2, 3];
map(function(x) { return x * x }, a);
console.log(a);
Sign up to request clarification or add additional context in comments.

7 Comments

Array.prototype.foo = 2; for(var i in [])console.log(i) > 'foo' - Updated my wording to state it more clear, everything that's enumerable will get listed.
I have no means of testing now but I'll take your word for it.
I used for in since I didn't modify a in any way.
@Sahil Your code will break as soon as someone else modifies Array.prototype, infamous example being the widely used Prototype.js library. Always use a for loop for arrays. It's more secure, and a thousand times faster too.
A thousand times faster? Really?
|
0

This call:

a[i] = f(a[i]);

is actually similar to this:

var arg = a[i];
var res = f(arg);
a[i] = res;

This is called "call-by-value", see here:

Comments

0

This is correct behavior, it's the value of a[i] is passed into f([i]), not the reference to it, so that x inside is a distinctly different variable/reference.

In the first version you're taking the result of that still different x (returned by the function) and assigning it to that array position afterwards...that's the only way to use that new value for something.

Comments

0

Arrays in JavaScript are Objects, and Objects are always reference types.

Up to the point where a exists inside the map function you have been passing it as an entire array which means that changes you make to a will be visible externally.

Once you de-reference the array with a[i] you have a variable of type Number which is not a reference type. Therefore, changes to x in your anonymous function will not be propagated back to the array, and are only visible inside the anonymous function itself.

1 Comment

That's what I assumed. Thanks.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.