Javascript Global Context Assignment

I understand the problems with global scope and javascript variables and their general inadvisability; and that you will find them everywhere. The following (in the 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 a variable without var anywhere in the code: your variable is assigned to the root (window) object.

One method that I see a lot (e.g. setting up Google Analytics) is this:

 var _gaq = _gaq || []; 

... and I follow the arguments that if _gaq was declared, use this unless you create it as an array. This allows sloppy encoding not to overwrite any values ​​already assigned to the _gaq global variable.

I do not understand why this is causing the error:

 _gaq = _gaq || []; 

They look equivalent to me: _gaq should take the value _gaq or be initialized as an array. But he gives out a control error - my question is: why are they different?

+6
source share
4 answers

You can never read variables that have not been declared, and what you are trying to use in the expression _gaq || [] _gaq || [] in the latter case.

In this case

  _gaq = _gaq || []; 

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

Here is a step-by-step explanation of what happens in this case:

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

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

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

LeftHandSideExpression - _gaq , AssignmentExpression - _gqa || [] _gqa || [] .

So, _qaq is evaluated _qaq , which leads to an unsolvable reference , since the _gaq variable _gaq not declared. This rating does not cause an error.

Then evaluated _gqa || [] _gqa || [] . This is LogicalORExpression and is described in Section 11.11 as LogicalORExpression || LogicalANDExpression LogicalORExpression || LogicalANDExpression . In this case, the LogicalORExpression , on the left, _gaq and LogicalANDExpression , the right, 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 link because _gaq not declared. So, let's see what GetValue does (defined in section 8.7.1 , V is the value passed to GetValue )

1. If Type(V) 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, in the third step of this procedure, a ReferenceError is raised, which, in turn, is executed evaluating the right-hand side of the job, and an error occurs here.

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

This line:

 var _gaq = _gaq || []; 

actually

 var _gaq; _gaq = _gaq || []; 

due to something called hoisting [MDN] . This means that when evaluating _gaq it will not lead to an unsolvable link, but a link with the value undefined .

(If the _gaq variable _gaq already declared (and potentially has a value), then var _gaq will have no effect.)


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

 window._gaq = window._gaq || []; 
+6
source

If _gaq right of = not previously declared with var , it will throw a reference error. You are trying to reference a variable that does not exist. Magic only works for assigning non-existent variables.

It's just like saying x = y + 1 ; the problem is not a non-existent x , but a non-existent y .

+5
source

This will cause an error because the variable was not found in the context chain of the current execution context. Access to a variable that cannot be resolved will result in an error.

 _gaq = _gaq || []; 

This, on the other hand, will be attempted by _gac, trying to find it as a member of the window object, which turns out to be a global holder object. The difference in this case is that it does not window._gaq error, but window._gaq will return undefined because the property was not found in the window object.

 _gaq = window._gaq || []; 

So, since the global context object is a window (when it comes to browsers), if _gaq is defined, these two statements will have the same effect. The difference will be noticed when _gaq is not defined, and accessing it using a window object may have the advantage of not receiving an error.

+1
source

Hoisting is at the heart of the concept, and in practice it is often difficult. Variables are defined at the top of the scope, while assignment still occurs where it is defined.

In this case, var _gaq = _gaq variable is actually defined before the actual line of code for the assignment is executed. This means that when the assignment is made, the variable is already in the window area. Without var, before _gaq no rise occurs, and thus _gaq does not yet exist when a job is executed that causes a reference error.

If you want to see this in action, you can check when the _gaq variable is added to the window object with the following:

 function printIsPropOnWindow(propToCheck) { for (prop in window) { if (prop == propToCheck) { console.warn('YES, prop ' + prop + ' was on the window object'); return; } } console.warn('NO, prop ' + propToCheck + ' was NOT on the window object'); } try { var _gaq = function() { printIsPropOnWindow("_gaq"); return a; }(); } catch (ex) { printIsPropOnWindow("_gaq"); } _gaq = "1"; printIsPropOnWindow("_gaq"); 

If you try this once as is and once with var before removing _gaq, you will see very different results, because it has _gaq and the other does not.

+1
source

All Articles