Passing by Value vs Passing by Reference in JavaScript
A thing that I've heard desktop application developers say about web developers is that web developers don't know what phrases passing by value and passing by reference mean. I'm sure most of us, web developers, know what those mean. If you don't, you should learn them ASAP!
To recap, passing by value means that the value that you pass to a function gets cloned and independent, and that changes to that value made within the function are not reflected on the original value. On the other hand, passing by reference means that the object passed to a function is linked to the one that is passed, so the original is affected by any changes that happen in the function.
I've done some testing to see when the values are passed by value, and when they are passed by reference in JavaScript, and here's what I've found out.
tl;dr
Literal values are always passed by value. Object properties are always passed by reference (including array members, because arrays in JavaScripts are objects with numerical properties).
Now the long version.
Suppose we have a few values assigned to a bunch of variables.
var int = 1; var str = 'a'; var obj = { int: 1, str: 'a' }; var arr = [1,2,3,4];
Now, suppose that we have a function like this:
function passme(passed) { passed = 'foo'; }
What this does is change the value of the incoming argument passed to 'foo'. If we run int through this, interesting thing happens:
passme(int); alert(int);
The value of int before and after calling passme is still 1, and it will not change to 'foo'. The exact same thing applies to all other variables, inclluding obj, and arr. They are all passed in by value.
So how do we get the new value assigned to the variable? We have to return it:
function passme(passed) { passme = 'foo' return passme; }
And then, we can assign the return value:
int = passme(int); // int is now === 'foo'
What would you expect to happen if we did this:
var obj1 = { a: 'foo', b: 'bar' }; var obj2 = {}; function cloneAndAddC(o) { o.c = 'baz'; return o; } obj2 = cloneAndAddC(obj1);
Honestly, I expected obj2 to have proerty c, and obj1 to not have it. But what happens in reality is that both have property c. (You can see a demo of failed object cloning in JSFiddle).
To explain it:
[obj1][.property] | \------------> by reference | \------> by value
So while the actual object is passed by value (if you assign something else to it, you won't kill the original), but the properties of the object are passed in by reference (if you modify a property, original property is modified). I hope that makes sense.
So, what if an object's property is an object? I bet you can't guess unless you've done tried this before! So check this out:
var obj1 = { a: 'foo' }; var obj2 = { a: obj1 }; function modme(o) { o.a.a = 'bar'; // supposedly this is obj1.a o.a = 'bar'; // o.a should be obj2.a, meaning it's obj1 } modme(obj2);
So what most of us though this would do is: 1. change obj1.a to 'bar', and then change obj1 itself to 'bar'. In fact, something else happens which is also very logical (and I seriously envy anyone who got this without peeking or knowing in advance): obj1.a is 'bar'```, but instead ofobj1itself turning into'bar'with the second statement,obj2.abecomes'bar', andobj1`` is left intact. (There is a demo for this on JSFiddle, too.)
So now, let's go back to our cloneAndAddC example for a second. How do we make that one work? Before you can do that, you first need to add a bit of boilerplate code:
// For browsers that don't support ECMA5 if (!Object.keys) { Object.keys = function(obj) { var keys = []; for (key in obj) { if (obj.hasOwnProperty(key)) { keys.push(key); } } return keys; }; }
With the above snippets, all non-ECMA5-compliant browsers will get the Object.keys function which can be used like this:
var obj1 = {a: 1, b:2}; Object.keys(obj1); // => ['a', 'b']
Up next, another piece of boilerplate:
if (!Array.prototype.forEach) { Array.prototype.forEach = function(callback, thisObject) { for (var i = this.length, l = this.length; i; --i) { if (!thisObject) { callback(this[l - i]); } else { callback.apply(thisObject, [this[l-i]]); } } }; }
So, you now have access to forEach, which loops thrhough array members and executes a callback function on each element.
Finally, we can define our clone function:
function clone(obj) { var clone = {}; Object.keys(obj).forEach(function(key) { clone[key] = obj[key]; }); return clone; }
The above does shallow copying. You could do some type detection and do recursive cloning. In fact, that's just a few more lines at most, but I'll leave that to you.
Now, we can make our clone thingy work:
var obj1 = { a: 'foo', b: 'bar' }; var obj2 = {}; function cloneAndAddC(o) { var cloned = clone(o); cloned.c = 'baz'; return cloned; } obj2 = cloneAndAddC(obj1);
Now wasn't that fun? You can see it in action on JSFiddle. Happy hacking!
UPDATE: You don't really have to write the boilerplate code. There's this cool little thingy called es5-shim (ECMAScript 5 shim) by Kris Kowal. Just plug it in, and you're ready to rock. You can see the last example using this shim on JSFiddle.















