Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why aren't prototype changes passed by reference to a method?

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.)

like image 234
David Andersson Avatar asked Mar 11 '13 10:03

David Andersson


1 Answers

jQuery(document).ready(function($) {
  Tracking.trackInputs($, _gaq);
});

Since the DOM is ready before GA is loaded, the reference of _gaq still points to an ordinary array, which is then perpetuated by the local symbol gaObject inside the trackInputs() function.

trackInputs: function ($, gaObject) {

After GA has loaded, the global symbol _gaq is replaced by the tracker, but gaObject still points to the old symbol.

You could use the "ready" feature of GA to call your trackInputs() function instead of using $(document).ready(...):

_gaq.push(function() {
    // when this runs, the tracker would have initialized;
    Tracking.trackInputs($, _gaq);
});
like image 111
Ja͢ck Avatar answered Oct 14 '22 05:10

Ja͢ck