Why aren't prototype changes passed by reference to a method?
google-analytics, javascript, jquery
Solution
Google Analytics initially uses a _gaq [object Array]. Passing an array to a function is in JavaScript passing an object, thus by reference.
You initial presumption is incorrect. Javascript always use pass by value. Pass by value. PASS BY VALUE.
you are passing a reference to an object by value. The reference is passed BY VALUE. At the end of the day, you have many references to the same underlying array, since each time a reference is passed by value you have a new copy of the reference, but the object never changes.
Ordinarily what the Google Analytics script does is it changes the _gaq array into a _gaq object and alters the push prototype for the object to have it make requests to the Google Analytics servers once a new call is pushed to the object.
Where does it do that?
However, as can be seen in the comment above, the trackInputs() method prints the 'original' array.
Of course it does.
var _gaq = _gaq || [];
_gaq.push(['_setAccount', _gAAccount]);
_gaq.push(['_trackPageview']);
define and array and put things in it.
jQuery(document).ready(function($) {
Tracking.trackInputs($, _gaq);
});
pass that array by value into a method
var Tracking = {
trackInputs: function ($, gaObject) {
...
inputs.bind('change', function () {
gaObject.push(['_trackPageview', '/virtual/input']);
...
});
}
};
create a closure around gaObject, which is a reference to the original array, and use it in the change handler. It's not the same reference as `_gaq`, its a copy of that reference, but it points to the same array.
Problem
Google Analytics initially uses a `_gaq` `[object Array]`. Passing an array to a function is in JavaScript passing an object, thus by reference. (Edit: As pointed out in answers, the reference is passed by value. See https://stackoverflow.com/a/5314911/120521 for more details on reference/value passing in JavaScript.) The code below uses jQuery to wait for the DOM to load, then attach a `change` event which will send a virtual pageview to Google Analytics once the user changes `<input/>` field. ``` var _gaq = _gaq || []; _gaq.push(['_setAccount', _gAAccount]); _gaq.push(['_trackPageview']); (function() { var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true; ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js'; var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s); })(); var Tracking = { trackInputs: function ($, gaObject) { var inputs = $('#signUp').find('input'); inputs.bind('change', function () { gaObject.push(['_trackPageview', '/virtual/input']); console.log(gaObject); // Outputs: // [Array[2], Array[1], Array[2]] // i.e. _setAccount, _trackPageview, // and _trackPageview calls. }); } }; jQuery(document).ready(function($) { Tracking.trackInputs($, _gaq); }); // ... DOM begins below ``` However, as can be seen in the comment above, the `trackInputs()` method prints the 'original' array. Ordinarily what the Google Analytics script does is it changes the `_gaq` array into a `_gaq` object and alters the push prototype for the object to have it make requests to the Google Analytics servers once a new call is pushed to the object. Why isn't this alteration also passed by reference into `trackInputs()`? I realise the loaded Google Analytics script will (or will it?) occur after the `Tracking.trackInputs()` definition, so the browser might not understand it is now an `[object object]` but will persist in thinking it was the original `[object Array]`. But, then it is not a reference anymore, is it? (Using the `_gaq` object globally (not passing it to the method at all) would solve the problem, but I want to understand why this doesn't work.)