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:

  1. for (var i = 0; i < 3; ++i) {
  2. var iteration = i;
  3. setTimeout(function() { console.log(iteration); }, i*1000);
  4. }
  5. // logs 2, 2, 2 -- NOT 0, 1, 2
  6. // 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:

  1. function sillyFunction() {
  2. var count = 0;
  3. for (var x in y) {
  4. // "count" could be declared here, but don't do that.
  5. count++;
  6. }
  7. console.log(count + ' items in y');
  8. }
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:

  1. if (x) {
  2. function foo() {}
  3. }

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:

  1. if (x) {
  2. var foo = function() {};
  3. }

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.provides first, goog.requires second. Separate providesfrom requires with an empty line.
  • Sort the entries alphabetically (uppercase first).
  • Don't wrap goog.provide and goog.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:

  1. goog.provide('namespace.MyClass');
  2. goog.provide('namespace.helperFoo');
  3. goog.require('an.extremelyLongNamespace.thatSomeoneThought.wouldBeNice.andNowItIsLonger.Than80Columns');
  4. goog.require('goog.dom');
  5. goog.require('goog.dom.TagName');
  6. goog.require('goog.dom.classes');
  7. 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:

  1. goog.provide('namespace.MyClass');

Not this:

  1. goog.provide('namespace.MyClass');
  2. goog.provide('namespace.MyClass.CONSTANT');
  3. goog.provide('namespace.MyClass.Enum');
  4. goog.provide('namespace.MyClass.InnerClass');
  5. goog.provide('namespace.MyClass.TypeDef');
  6. goog.provide('namespace.MyClass.staticMethod');

Members on namespaces may also be provided:

  1. goog.provide('foo.bar');
  2. goog.provide('foo.bar.CONSTANT');
  3. goog.provide('foo.bar.method');
9.4.4.2 Aliasing with goog.scope

WARNING: goog.scope is deprecated. New files should not use goog.scopeeven 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):

  1. goog.scope(function() {
  2. var Button = goog.ui.Button;
  3. Button = function() { ... };
  4. ...

Names must be the same as the last property of the global that they are aliasing.

  1. goog.provide('my.module.SomeType');
  2. goog.require('goog.dom');
  3. goog.require('goog.ui.Button');
  4. goog.scope(function() {
  5. var Button = goog.ui.Button;
  6. var dom = goog.dom;
  7. // Alias new types after the constructor declaration.
  8. my.module.SomeType = function() { ... };
  9. var SomeType = my.module.SomeType;
  10. // Declare methods on the prototype as usual:
  11. SomeType.prototype.findButton = function() {
  12. // Button as aliased above.
  13. this.button = new Button(dom.getElement('my-button'));
  14. };
  15. ...
  16. }); // 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.