Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Javascript Global Scope Assignment

I understand the issues with global scope and javascript variables and their general undesirability; and that you find them everywhere. The following (in a browser) is equivalent:

var foo = 3; // foo === 3, window.foo === 3
bazz = 10; // bazz === 10, window.bazz === 10

Declaring a variable with the var keyword in the global scope is the same as declaring it without a var anywhere in the code: your variable is assigned to the root (window) object.

One technique I see a lot (e.g. setting up google analytics) is this:

var _gaq = _gaq || [];

... and I follow the reasoning that if _gaq has been declared use that, if not create it as an array. It allows careless coding not to overwrite any values already assigned to the global variable _gaq.

What I don't understand is why this throws an error:

_gaq = _gaq || [];

They look equivalent to me: _gaq should take the value of _gaq or be initialised as an array. But it throws a reference error - my question is: why are they different?

like image 423
Party Ark Avatar asked Mar 13 '13 14:03

Party Ark


1 Answers

You can never read variables which have not been declared, and that's what you are trying with the expression _gaq || [] in the last case.

In this case

 _gaq = _gaq || [];

_qaq has not been declared before and when the right hand side (_gaq || []) is evaluated, it throws the error.

Here is step by step explanation of what is going on in this case:

The assignment operator is described in section 11.13.1 of the specification:

The production AssignmentExpression : LeftHandSideExpression = AssignmentExpression is evaluated as follows:

1. Let lref be the result of evaluating LeftHandSideExpression.
2. Let rref be the result of evaluating AssignmentExpression.
...

LeftHandSideExpression is _gaq, the AssignmentExpression is _gqa || [].

So first _qaq is evaluated, which results in an unresolvable reference, since the variable _gaq is not declared. This evaluation does not throw an error.

Then _gqa || [] is evaluated. This is a LogicalORExpression and is described in section 11.11 as LogicalORExpression || LogicalANDExpression. In this case, LogicalORExpression, the left hand side, is _gaq and LogicalANDExpression, the right hand side, is [].
The expression is evaluated as follows:

1. Let lref be the result of evaluating LogicalORExpression.
2. Let lval be GetValue(lref).
...

We already know that lref will be an unsolvable reference because _gaq was not declared. So lets have a look what GetValue is doing (defined in section 8.7.1, V is the value passed to GetValue):

1. If Type(V) is not Reference, return V.
2. Let base be the result of calling GetBase(V).
3. If IsUnresolvableReference(V), throw a ReferenceError exception.
...

As you can see, a ReferenceError error is thrown in the third step of this procedure, which in turn gets executed by evaluating the right hand side of the assignment, and this is where the error is thrown.

So, why does this not happen with var _gaq = _gaq || []; ?

This line:

var _gaq = _gaq || [];

is actually

var _gaq;
_gaq = _gaq || [];

because of something called hoisting [MDN]. That means when _gaq is evaluated, it will not result in an unresolvable reference, but a reference with value undefined.

(If the variable _gaq is already declared (and potentially has a value), then var _gaq won't have any effect.)


If you want to create _gaq globally from inside a function, do it explicitly by referring to window:

window._gaq = window._gaq || [];
like image 115
Felix Kling Avatar answered Oct 12 '22 01:10

Felix Kling