9.4 Exceptions for legacy platforms
9.4.1 Overview
This section describes exceptions and additional rules to be followed whenmodern ECMAScript 6 syntax is not available to the code authors. Exceptions tothe recommended style are required when ECMAScript 6 syntax is not possible andare outlined here:
- Use of
var
declarations is allowed - Use of
arguments
is allowed - Optional parameters without default values are allowed
9.4.2 Use var
9.4.2.1 var declarations are NOT block-scoped
var
declarations are scoped to the beginning of the nearest enclosingfunction, script or module, which can cause unexpected behavior, especially withfunction closures that reference var
declarations inside of loops. Thefollowing code gives an example:
for (var i = 0; i < 3; ++i) {
var iteration = i;
setTimeout(function() { console.log(iteration); }, i*1000);
}
// logs 2, 2, 2 -- NOT 0, 1, 2
// because `iteration` is function-scoped, not local to the loop.
9.4.2.2 Declare variables as close as possible to first use
Even though var
declarations are scoped to the beginning of the enclosingfunction, var
declarations should be as close as possible to their first use,for readability purposes. However, do not put a var
declaration inside a blockif that variable is referenced outside the block. For example:
function sillyFunction() {
var count = 0;
for (var x in y) {
// "count" could be declared here, but don't do that.
count++;
}
console.log(count + ' items in y');
}
9.4.2.3 Use @const for constants variables
For global declarations where the const
keyword would be used, if it wereavailable, annotate the var
declaration with @const instead (this is optionalfor local variables).
9.4.3 Do not use block scoped functions declarations
Do not do this:
if (x) {
function foo() {}
}
While most JavaScript VMs implemented before ECMAScript 6 support functiondeclarations within blocks it was not standardized. Implementations wereinconsistent with each other and with the now-standard ECMAScript 6 behavior forblock scoped function declaration. ECMAScript 5 and prior only allow forfunction declarations in the root statement list of a script or function andexplicitly ban them in block scopes in strict mode.
To get consistent behavior, instead use a var
initialized with a functionexpression to define a function within a block:
if (x) {
var foo = function() {};
}
9.4.4 Dependency management with goog.provide/goog.require
9.4.4.1 Summary
WARNING: goog.provide
dependency management is deprecated. All new files,even in projects using goog.provide
for older files, should usegoog.module
. The following rules are forpre-existing goog.provide
files only.
- Place all
goog.provide
s first,goog.require
s second. Separate providesfrom requires with an empty line. - Sort the entries alphabetically (uppercase first).
- Don't wrap
goog.provide
andgoog.require
statements. Exceed 80 columnsif necessary. - Only provide top-level symbols.
goog.provide
statements should be grouped together and placed first. Allgoog.require
statements should follow. The two lists should be separated withan empty line.
Similar to import statements in other languages, goog.provide
andgoog.require
statements should be written in a single line, even if theyexceed the 80 column line length limit.
The lines should be sorted alphabetically, with uppercase letters coming first:
goog.provide('namespace.MyClass');
goog.provide('namespace.helperFoo');
goog.require('an.extremelyLongNamespace.thatSomeoneThought.wouldBeNice.andNowItIsLonger.Than80Columns');
goog.require('goog.dom');
goog.require('goog.dom.TagName');
goog.require('goog.dom.classes');
goog.require('goog.dominoes');
All members defined on a class should be in the same file. Only top-levelclasses should be provided in a file that contains multiple members defined onthe same class (e.g. enums, inner classes, etc).
Do this:
goog.provide('namespace.MyClass');
Not this:
goog.provide('namespace.MyClass');
goog.provide('namespace.MyClass.CONSTANT');
goog.provide('namespace.MyClass.Enum');
goog.provide('namespace.MyClass.InnerClass');
goog.provide('namespace.MyClass.TypeDef');
goog.provide('namespace.MyClass.staticMethod');
Members on namespaces may also be provided:
goog.provide('foo.bar');
goog.provide('foo.bar.CONSTANT');
goog.provide('foo.bar.method');
9.4.4.2 Aliasing with goog.scope
WARNING: goog.scope
is deprecated. New files should not use goog.scope
even in projects with existing goog.scope
usage.
goog.scope
may be used to shorten references to namespaced symbols incode using goog.provide
/goog.require
dependency management.
Only one goog.scope
invocation may be added per file. Always place it inthe global scope.
The opening goog.scope(function() {
invocation must be preceded by exactly oneblank line and follow any goog.provide
statements, goog.require
statements,or top-level comments. The invocation must be closed on the last line in thefile. Append // goog.scope
to the closing statement of the scope. Separate thecomment from the semicolon by two spaces.
Similar to C++ namespaces, do not indent under goog.scope
declarations.Instead, continue from the 0 column.
Only make aliases for names that will not be re-assigned to another object(e.g., most constructors, enums, and namespaces). Do not do this (see below forhow to alias a constructor):
goog.scope(function() {
var Button = goog.ui.Button;
Button = function() { ... };
...
Names must be the same as the last property of the global that they are aliasing.
goog.provide('my.module.SomeType');
goog.require('goog.dom');
goog.require('goog.ui.Button');
goog.scope(function() {
var Button = goog.ui.Button;
var dom = goog.dom;
// Alias new types after the constructor declaration.
my.module.SomeType = function() { ... };
var SomeType = my.module.SomeType;
// Declare methods on the prototype as usual:
SomeType.prototype.findButton = function() {
// Button as aliased above.
this.button = new Button(dom.getElement('my-button'));
};
...
}); // goog.scope
9.4.4.3 goog.forwardDeclare
Prefer to use goog.requireType
instead of goog.forwardDeclare
to breakcircular dependencies between files in the same library. Unlike goog.require
,a goog.requireType
statement is allowed to import a namespace before it isdefined.
goog.forwardDeclare
may still be used in legacy code to break circularreferences spanning across library boundaries, but newer code should bestructured to avoid it.
goog.forwardDeclare
statements must follow the same style rules asgoog.require
and goog.requireType
. The entire block ofgoog.forwardDeclare
, goog.require
and goog.requireType
statements issorted alphabetically.