Google的Javascript风格指南
5 语言特性
JavaScript包含许多可疑(甚至危险)的特性。本节描述哪些功能可以使用,哪些不可以使用,以及对它们的使用的任何附加限制。
5.1 局部变量声明
5.1.1 const和let的使用
使用const或者let声明所有的局部变量。默认情况下使用const,除非需要重新给变量赋值。不能使用var关键字。
5.1.2 每次只声明一个变量
每次只声明一个局部变量;不使用a = 1, b = 2;这种方式。
5.1.3 仅在需要时声明,并且尽快初始化
局部变量通常不会在其包含的块或类似块的构造的开始处声明。相反,局部变量被声明在它们第一次使用的地方附近(在合理范围内),从而以最小化它们的作用域。
5.1.4 根据需要声明类型
如果没有其它JSDoc,可以在声明上方的行中添加JSDoc类型注释,或者在变量名之前内联添加注释。
例子:
const /** !Array<number> */ data = [];
/**
* Some description.
* @type {!Array<number>}
*/
const data = [];
不允许混合内联和JSDoc样式:编译器将只处理第一个JSDoc,内联注释将丢失。
/** Some description. */
const /** !Array<number> */ data = [];
提示:在很多情况下,编译器可以推断出模板化的类型,但不能推断出它的参数。当初始化文字或构造函数调用不包含模板参数类型的任何值(例如,空数组、对象、映射或集合),或者变量在闭包中被修改时,尤其如此。局部变量类型注释在这些情况下特别有用,因为不这样的话编译器将推断模板参数为未知。
5.2 数组文法
5.2.1 使用拖拽逗号
const values = [
'first value',
'second value',
];
5.2.2 不要使用可变数组的构造函数
如果添加或者删除参数,则构造函数很容易出错。使用文法来代替。
不允许:
const a1 = new Array(x1, x2, x3);
const a2 = new Array(x1, x2);
const a3 = new Array(x1);
const a4 = new Array();
除了第三种情况外,其它与预期一致:如果x1是整数,那么a3就是一个大小为x1的数组,其中所有元素都是未定义的。如果x1是其他任何数字,则抛出异常,如果是其他任何数字,则抛出单元素数组。
相反,应该这样写:
const a1 = [x1, x2, x3];
const a2 = [x1, x2];
const a3 = [x1];
const a4 = [];
在适当时候,允许使用新数组(长度)来显式地分配给定长度的数组:new Array(length)。
5.2.3 非数值性质
不要在数组上定义或使用非数值属性(长度除外),使用map或者object来代替。
5.2.4 析构
可以在赋值的左侧使用数组文字来执行析构(例如从单个数组或iterable中解包多个值时)。可以包含最后一个rest元素(在…之间没有空格)和变量名)。如果元素是不使用的,就应该忽略它们。
const [a, b, c, ...rest] = generateResults();
let [, b,, d] = someArray;
也可以对函数参数使用析构(注意,需要一个参数名,但是忽略了它)。如果析构数组参数是可选的,则始终指定[]为默认值,并在左侧提供默认值:
/** @param {!Array<number>=} param1 */
function optionalDestructuring([a = 4, b = 2] = []) { … };
不允许:
function badDestructuring([a, b] = [4, 2]) { … };
提示:对于将多个值打包或者解包到函数的参数或返回中,尽可能选择对象析构而不是数组析构,因为它允许命名单个元素并为每个元素指定不同的类型。
5.2.5 传播算子(Spread operator)
数组文法中可以包含传播算子操作符(…),以将一个或者多个其它迭代的元素压平。应该使用扩展操作符,而不是更为笨拙的Array.prototype。在(…)后面没有空格。
示例:
[...foo] // preferred over Array.prototype.slice.call(foo)
[...foo, ...bar] // preferred over foo.concat(bar)
5.3 对象文法
5.3.1 使用拖拽逗号
当在final属性和右大括弧之间有换行符时,在后面加上逗号。
5.3.2 不要使用Object的构造函数
虽然Object不具有和Array一样的问题,但是为了保持一致性,仍然不允许使用Object的构造函数。使用文法({} or {a: 0, b: 1, c: 2}) 进行代替。
5.3.3 不要混合使用含引号和不含引号的关键字
Object文法既可以表示结构(里面的关键字不含引号),也可以表示字典(里面的关键字含引号)。但是不要在一个Object里面混合使用这两种关键字。
不允许:
{
width: 42, // struct-style unquoted key
'maxWidth': 43, // dict-style quoted key
}
这种规定也扩展到将属性名传递给函数,比如hasOwnProperty。特别是,这样做会破坏编译后的代码,因为编译器无法重命名/混淆字符串文字。
不允许:
/** @type {{width: number, maxWidth: (number|undefined)}} */
const o = {width: 42};
if (o.hasOwnProperty('maxWidth')) {
...
}
上述方式最好被实现如下:
/** @type {{width: number, maxWidth: (number|undefined)}} */
const o = {width: 42};
if (o.maxWidth != null) {
...
}
5.3.4 计算属性名
允许计算属性名(例如,{[‘key’ + foo(): 42}): 42}),它们被认为是字典样式(引用)的键(例如除非计算的属性是一个符号(例如,[symbol .iterator])。枚举值也可以用于计算的键,但不应与非枚举键在同一文本中混合使用。
5.3.5 方法速记
可以使用方法简写({method(){…}})在对象文字上定义方法,以代替紧跟在函数或箭头函数文字后面的冒号。
例子:
return {
stuff: 'candy',
method() {
return this.stuff; // Returns 'candy'
},
};
请注意,在方法简写或函数中,this指的是对象文字本身,而在箭头函数中,this指的是对象文字之外的范围。
例子:
class {
getObjectLiteral() {
this.stuff = 'fruit';
return {
stuff: 'candy',
method: () => this.stuff, // Returns 'fruit'
};
}
}
5.3.6 简写属性
在Object文法中,允许简写属性。
示例:
const foo = 1;
const bar = 2;
const obj = {
foo,
bar,
method() { return this.foo + this.bar; },
};
assertEquals(3, obj.method());
5.3.7 析构
对象析构模式可用于赋值的左侧,以执行析构并从单个对象解包多个值。
被析构的对象也可以用作函数参数,但是应该尽可能地保持简单:一个单级的未引用的简写属性。更深层次的嵌套和计算属性不能用于参数析构。在析构参数的左边指定任何默认值({str = ‘some default’} ={},而不是{str} = {str: ‘some default’}),如果析构对象本身是可选的,那么它必须默认为{}。析构参数的JSDoc可以被赋予任何名称(名称是未使用的,但是编译器需要它)。
例子:
/**
* @param {string} ordinary
* @param {{num: (number|undefined), str: (string|undefined)}=} param1
* num: The number of times to do something.
* str: A string to do stuff to.
*/
function destructured(ordinary, {num, str = 'some default'} = {})
禁止:
/** @param {{x: {num: (number|undefined), str: (string|undefined)}}} param1 */
function nestedTooDeeply({x: {num, str}}) {};
/** @param {{num: (number|undefined), str: (string|undefined)}=} param1 */
function nonShorthandProperty({num: a, str: b} = {}) {};
/** @param {{a: number, b: number}} param1 */
function computedKey({a, b, [a + b]: c}) {};
/** @param {{a: number, b: string}=} param1 */
function nontrivialDefault({a, b} = {a: 2, b: 4}) {};
析构也可以用于goog.require语句,在这种情况下不能被包装:整个语句占用一行,不管它有多长(见3.6 goog.require和goog.requireType语句)。
5.3.8 枚举
枚举是通过向对象文字添加@enum注释来定义的。在enum定义之后,不能将其他属性添加到它。枚举必须是常量,并且所有枚举值必须是深度不可变的。
/**
* Supported temperature scales.
* @enum {string}
*/
const TemperatureScale = {
CELSIUS: 'celsius',
FAHRENHEIT: 'fahrenheit',
};
/**
* An enum with two options.
* @enum {number}
*/
const Option = {
/** The option used shall have been the first. */
FIRST_OPTION: 1,
/** The second among two options. */
SECOND_OPTION: 2,
};
5.4 类
5.4.1 构造函数
构造函数是可选的。在设置任何字段或以其它方式访问它之前,子类构造函数必须调用super()。接口应该在构造函数中声明非方法属性。
5.4.2 域
在构造函数中设置一个具体对象的所有字段(即除了方法之外的所有属性)。从不使用@const重新分配的字段(这些字段不需要是深度不可变的)。使用适当的可见性注释(@private、@protected、@package)注释非公共字段,并以下划线结束所有@private字段的名称。字段从不设置在具体类的原型上。
例子:
class Foo {
constructor() {
/** @private @const {!Bar} */
this.bar_ = computeBar();
/** @protected @const {!Baz} */
this.baz = computeBaz();
}
}
提示:在构造函数完成后,不应该向实例中添加或从实例中删除属性,因为这极大地阻碍了VMs的优化能力。如果有必要,以后初始化的字段应该在构造函数中显式地设置为undefined,以防止以后的形状更改。向对象添加@struct将检查未声明的属性是否被添加/访问。默认情况下,类会添加这个。
5.4.3 计算属性
计算属性只能在属性为符号的类中使用。不允许使用字典样式的属性(即引用或计算的非符号键,如5.3.3 不要混合使用被应用和不被应用的关键字中所定义的,不要混合引用和非引用键)。一个[Symbol.iterator]方法应该在任何逻辑上可迭代的类中被定义。除此之外,Symbol应该被谨慎使用。
提示:小心使用任何其他内置的符号(例如,Symbol.isConcatSpreadable),因为编译器不会填充它们,因此在旧的浏览器中不能工作。
5.4.4 静态方法
在不影响可读性的情况下,最好使用模块本地函数而不是私有静态方法。
静态方法应该只在基类本身上调用。静态方法不应该在包含动态实例的变量上调用,动态实例可以是构造函数,也可以是子类的构造函数(如果这样做了,必须用@nocollapse来定义),而且不能直接在没有定义方法本身的子类上调用。
不允许:
class Base { /** @nocollapse */ static foo() {} }
class Sub extends Base {}
function callFoo(cls) { cls.foo(); } // discouraged: don't call static methods dynamically
Sub.foo(); // Disallowed: don't call static methods on subclasses that don't define it themselves
5.4.5 老式的类声明
虽然ES6类是首选的,但是在某些情况下,ES6类可能是不可行的,例如:
- 如果存在或将存在子类,包括创建子类的框架,则不能立即更改为使用ES6类的语法。如果这样一个类要使用ES6语法,则需要修改所有不使用ES6类语法的下游子类。
- 框架在调用超类构造函数之前需要一个已知的this值,因为具有ES6超类的构造函数在调用超类返回之前不能访问this值的实例。
在所有其它方面,样式指南仍然适用于这段代码:let,const,默认参数,rest,以及箭头函数等都应该在适当的时候使用。
goog.defineClass允许类似于ES6类语法的类定义:
let C = goog.defineClass(S, {
/**
* @param {string} value
*/
constructor(value) {
S.call(this, 2);
/** @const */
this.prop = value;
},
/**
* @param {string} param
* @return {number}
*/
method(param) {
return 0;
},
});
另外,虽然所有新代码都应该首选goog.defineClass,但也允许使用更传统的语法。
/**
* @constructor @extends {S}
* @param {string} value
*/
function C(value) {
S.call(this, 2);
/** @const */
this.prop = value;
}
goog.inherits(C, S);
/**
* @param {string} param
* @return {number}
*/
C.prototype.method = function(param) {
return 0;
};
如果有超类的话,每个实例的属性应该在调用超类构造函数后在构造函数中定义。方法应该在构造函数的原型上定义。
正确定义构造函数原型层次结构比它第一次出现时更困难!因此,最好从the Closure Library 中使用goog.inherits。
5.4.6 不要直接操作原型
class关键字允许比定义prototype属性更清晰、更可读的类定义。普通的实现代码没有操作这些对象的业务,尽管它们对于5.4.5 老式的类声明定义的类仍然很有用。混合和修改内建对象的原型是被明确禁止的。
例外:框架代码(如聚合体或Angular)可能需要使用原型,而不应该使用更糟糕的变通方法来避免这样做。
5.4.7 Getters和Setters
不要使用JavaScript getter and setter properties。它们可能会让人感到惊讶,并且很难进行推理,而且在编译器中对它们的支持也很有限。相反,提供普通的方法即可。
例外:在某些情况下,定义getter或setter是不可避免的(例如,Angular和Polymer等数据绑定框架,或者为了与无法调整的外部api兼容)。仅在这些情况下,如果使用get和set简写方法关键字或Object.defineProperties(而不是Object.defineProperty,它会干扰属性的重命名)来定义getter和setter,则可以谨慎地使用它们。getter不能改变可观察状态。
不允许:
class Foo {
get next() { return this.nextId++; }
}
5.4.8 toString的重载
toString方法可能被覆盖,但是必须始终成功,并且不会有可见的副作用。
提示:特别要注意从toString调用其它方法,因为异常条件可能导致无限循环。
5.4.9 接口
接口可以用@interface或@record声明。使用@record声明的接口可以是显式的(即通过@implements),也可以是由类或对象文字隐式实现的。
接口上的所有非静态方法体必须是空块。字段必须在类构造函数中声明为未初始化的成员。
例子:
/**
* Something that can frobnicate.
* @record
*/
class Frobnicator {
constructor() {
/** @type {number} The number of attempts before giving up. */
this.attempts;
}
/**
* Performs the frobnication according to the given strategy.
* @param {!FrobnicationStrategy} strategy
*/
frobnicate(strategy) {}
}
5.4.10 抽象类
在需要的时候可以使用抽象类。抽象类和方法必须被标识为@abstract。不要使用goog.abstractMethod。具体请参见abstract classes and methods。
5.5 函数
5.5.1 顶层函数
顶层函数可以直接在导出对象上定义,也可以在本地声明。有关导出的更多信息,请参见3.3.3 goog.module导出。
示例:
/** @param {string} str */
exports.processString = (str) => {
// Process the string.
};
/** @param {string} str */
const processString = (str) => {
// Process the string.
};
exports = {processString};
5.5.2 嵌套函数和闭包
函数可以包含嵌套的函数定义。如果给函数起一个名字很有用,那么应该把它分配给一个本地常量。
5.5.3 箭头函数
箭头函数提供了简洁的函数语法,并简化了嵌套函数中this的作用域。 优先选择箭头函数而不是function关键字,特别是对于嵌套函数(请参见5.3.5 方法速记)。
优先使用箭头函数,而不是其他this范围划分方法,例如f.bind(this),goog.bind(f,this)和const self = this。 箭头函数对于调用回调函数特别有用,因为在绑定传递所有参数时,允许显式指定要传递给回调的参数。
箭头的左侧包含零个或多个参数。 如果只有一个未分解的参数,则参数周围的括号是可选的。 使用括号时,可以指定内联参数类型(请参见7.8 方法和函数注释)。
提示:即使对于单参数箭头函数也始终使用括号可以避免添加参数但忘记添加括号可能导致可解析的代码不再按预期工作的情况。
箭头的右侧包含函数的主体。 默认情况下,主体为block语句(零个或多个由花括号包围的语句)。 如果发生以下情况之一,则主体也可能是隐式返回的单个表达式:程序逻辑要求返回值,或者void运算符位于单个函数或方法调用之前(使用void确保返回undefined的值,防止泄漏值并传达意图)。 如果单一表达形式提高了可读性(例如,对于简短表达或简单表达),则它是首选。
例子:
/**
* Arrow functions can be documented just like normal functions.
* @param {number} numParam A number to add.
* @param {string} strParam Another number to add that happens to be a string.
* @return {number} The sum of the two parameters.
*/
const moduleLocalFunc = (numParam, strParam) => numParam + Number(strParam);
// Uses the single expression syntax with `void` because the program logic does
// not require returning a value.
getValue((result) => void alert(`Got ${result}`));
class CallbackExample {
constructor() {
/** @private {number} */
this.cachedValue_ = 0;
// For inline callbacks, you can use inline typing for parameters.
// Uses a block statement because the value of the single expression should
// not be returned and the expression is not a single function call.
getNullableValue((/** ?number */ result) => {
this.cachedValue_ = result == null ? 0 : result;
});
}
}
禁止:
/**
* A function with no params and no returned value.
* This single expression body usage is illegal because the program logic does
* not require returning a value and we're missing the `void` operator.
*/
const moduleLocalFunc = () => anotherFunction();
5.5.4 生成器(Generators)
生成器启用了许多有用的抽象,可以根据需要使用。
定义生成器函数时,请将*附加到function关键字(如果存在),并将其与函数名称隔开。 使用委派收益时,请在yield关键字上附加*。
例:
/** @return {!Iterator<number>} */
function* gen1() {
yield 42;
}
/** @return {!Iterator<number>} */
const gen2 = function*() {
yield* gen1();
}
class SomeClass {
/** @return {!Iterator<number>} */
* gen() {
yield 42;
}
}
5.5.5 参数和返回类型
函数参数和返回类型通常应使用JSDoc注释记录。 有关更多信息,请参见7.8 方法和函数注释。
5.5.5.1 默认参数
使用参数列表中的equals运算符可以允许使用可选参数。 可选参数必须在equals运算符的两侧都包含空格,其名称必须与必需参数完全相同(即,不以opt_开头),在其JSDoc类型中使用=后缀,紧随必需参数之后,并且不使用产生可观察侧的初始化程序效果。 具体功能的所有可选参数都必须具有默认值,即使该值未定义。 与具体功能相反,抽象和接口方法必须省略默认参数值。
例:
/**
* @param {string} required This parameter is always needed.
* @param {string=} optional This parameter can be omitted.
* @param {!Node=} node Another optional parameter.
*/
function maybeDoSomething(required, optional = '', node = undefined) {}
/** @interface */
class MyInterface {
/**
* Interface and abstract methods must omit default parameter values.
* @param {string=} optional
*/
someMethod(optional) {}
}
谨慎使用默认参数。 当存在少数不具有自然顺序的可选参数时,建议使用分解(如5.3.7 析构中所述)来创建可读的API。
注意:与Python的默认参数不同,可以使用返回新的可变对象(例如{}或[])的初始化程序,因为每次使用默认值时都会对初始化程序进行评估,因此不会在单个对象之间共享调用。
提示:尽管可以将包括函数调用在内的任意表达式用作初始化程序,但应使其尽可能简单。 避免使用暴露了共享可变状态的初始化程序,因为这样很容易在函数调用之间引入意外耦合。
5.5.5.2 Rest参数
使用rest参数而不是访问参数。 其余参数在其JSDoc中以…前缀键入。 rest参数必须是列表中的最后一个参数。 …和参数名称之间没有空格。 不要将rest参数命名为var_args。 切勿命名局部变量或参数参数,否则会混淆内置名称。
例:
/**
* @param {!Array<string>} array This is an ordinary parameter.
* @param {...number} numbers The remainder of arguments are all numbers.
*/
function variadic(array, ...numbers) {}
5.5.6 泛化
必要时,在函数或方法定义上方的JSDoc中使用@template TYPE声明通用函数和方法。
5.5.7 Spread操作
函数调用可以使用Spread运算符(…)。 当数组或可迭代程序解压缩为可变参数的多个参数时,请优先使用Spread运算符而不是Function.prototype.apply。 …之后没有空格。
例:
function myFunction(...elements) {}
myFunction(...array, ...iterable, ...generator());
5.6 String文法
5.6.1 使用单引号
普通字符串文字用单引号(’)而不是双引号(“)分隔。
提示:如果字符串包含单引号字符,请考虑使用模板字符串,以避免不得不对引号进行转义。
普通字符串文字不能跨越多行。
5.6.2 模板文法(Template literals)
在复杂的字符串连接上使用模板文字(以`分隔),尤其是在涉及多个字符串文字的情况下。 模板文字可能跨越多行。
如果模板文字跨越多行,则不需要遵循封闭块的缩进,尽管添加的空格可能无关紧要。
例:
function arithmetic(a, b) {
return `Here is a table of arithmetic operations:
${a} + ${b} = ${a + b}
${a} - ${b} = ${a - b}
${a} * ${b} = ${a * b}
${a} / ${b} = ${a / b}`;
}
5.6.3 无连续行
请勿在普通或模板字符串文字中使用换行符(即,在字符串文字中的行以反斜杠结束)。 即使ES5允许这样做,但如果在斜杠后出现任何尾随空格,则可能会导致棘手的错误,并且对读者而言不太明显。
不允许:
const longString = 'This is a very long string that far exceeds the 80 \
column limit. It unfortunately contains long stretches of spaces due \
to how the continued lines are indented.';
相反,请使用:
const longString = 'This is a very long string that far exceeds the 80 ' +
'column limit. It does not contain long stretches of spaces since ' +
'the concatenated strings are cleaner.';
5.7 数字文法
数字可以用十进制,十六进制,八进制或二进制指定。 对于十六进制,八进制和二进制,分别使用0x,0o和0b的前缀(带有小写字母)。 除非前面紧跟着x,o或b,否则切勿包含前导零。
5.8 控制结构
5.8.1 For循环
使用ES6,该语言现在具有三种不同的for循环。 可以使用所有方法,尽管在可能的情况下应首选for-of循环。
for-in循环仅可用于dict样式的对象(请参见5.3.3 不要混合使用被应用和不被应用的关键字),并且不应将其用于遍历数组。
应该在for-in循环中使用Object.prototype.hasOwnProperty来排除不需要的原型属性。 尽可能使用for-of和Object.keys。
5.8.2 异常
异常是语言的重要组成部分,每当出现例外情况时都应使用异常。 始终抛出错误或Error的子类:永远不要抛出字符串文字或其他对象。 构造错误时,请始终使用new。
这种处理扩展到Promise拒绝值,因为在异步功能中Promise.reject(obj)等效于throw obj。
自定义异常提供了一种从功能传达其他错误信息的好方法。 应该在本机错误类型不足的地方定义和使用它们。
优先于临时错误处理方法(例如,传递错误容器引用类型或返回具有error属性的对象),而不是引发异常。
5.8.2.1 空catch块
响应捕获到的异常,什么也不做是非常正确的。 当确实在catch块中不执行任何操作时,在注释中解释这样做的合理原因。
try {
return handleNumericResponse(response);
} catch (ok) {
// it's not numeric; that's fine, just continue
}
return handleTextResponse(response);
不允许:
try {
shouldFail();
fail('expected an error');
} catch (expected) {
}
提示:与其他语言不同,上述模式根本无法使用,因为这会捕获失败引发错误。 使用assertThrows()代替。
5.8.3 Switch语句
术语注:在一个开关块的括号内是一个或多个语句组。 每个语句组由一个或多个开关标签(大小写为FOO:或default:)组成,后跟一个或多个语句。
5.8.3.1 掉落特性:已注解
在switch块内,每个语句组要么突然终止(带有break,return或thrown异常),要么用注释标记,以指示执行将继续执行或可能继续执行下一个语句组。 任何传达掉落想法的评论都是足够的(通常// fall through)。 在switch块的最后一个语句组中不需要此特殊注释。
例:
switch (input) {
case 1:
case 2:
prepareOneOrTwo();
// fall through
case 3:
handleOneTwoOrThree();
break;
default:
handleLargeNumber(input);
}
5.8.3.2 应包含default分支
每个switch语句包括一个默认语句组,即使它不包含任何代码。 默认语句组必须是最后一个。
5.9 this
仅在类构造函数中,以及方法中定义的箭头函数内,或在立即封闭的函数的JSDoc中声明了显式@this的函数中使用this。
永远不要使用this来引用全局对象,评估上下文,事件的目标或不必要的call()ed或apply()函数。
5.10 Equality检查
除下面列举的情况外,请使用标识运算符(=== / !==)。
5.10.1 需要强制的异常
捕获null和undefined的值:
if (someObjectOrPrimitive == null) {
// Checking for null catches both null and undefined for objects and
// primitives, but does not catch other falsy values like 0 or the empty
// string.
}
5.11 禁止的特性
5.11.1 with
不要使用with关键字。 自ES5起,它使您的代码更难以理解,并且在strict模式下被禁止。
5.11.2 动态代码评估
不要使用eval或Function(… string)构造函数(代码加载器除外)。 这些功能具有潜在的危险,根本无法在CSP环境中使用。
5.11.3 自动分号插入
始终以分号终止语句(如上所述,函数和类声明除外)。
5.11.4 非标准功能
不要使用非标准功能。 这包括已删除的旧功能(例如,WeakMap.clear),尚未标准化的新功能(例如,当前的TC39工作草案,任何阶段的提案或已提议但尚未完成的Web标准),或仅在某些浏览器中实现的专有功能。 仅使用当前ECMA-262或WHATWG标准中定义的功能。 (请注意,针对特定API(例如Chrome扩展程序或Node.js)编写的项目显然可以使用这些API)。 禁止使用非标准语言的“扩展名”(例如某些外部编译器提供的扩展名)。
5.11.5 原始类型的包装器
切勿在原始对象包装器(Boolean,Number,String和Symbol)上使用new,也不要在类型注释中包含它们。
禁止:
const /** Boolean */ x = new Boolean(false);
if (x) alert(typeof x); // alerts 'object' - WAT?
包装器可以被称为用于强制(比使用+或连接空字符串更可取)或创建符号的函数。
示例:
const /** boolean */ x = Boolean(0);
if (!x) alert(typeof x); // alerts 'boolean', as expected
5.11.6 修改builtin对象
切勿通过向其构造函数或其原型添加方法来修改内置类型。 避免依赖执行此操作的库。 请注意,JSCompiler的运行时库将在可能的情况下提供符合标准的polyfill; 没有别的办法可以修改内置对象。
除非绝对必要(例如第三方API要求),否则请勿在全局对象中添加符号。
5.11.7 调用构造函数时忽略括弧()
在不使用括弧()的情况下,切勿在新语句中调用构造函数。
不允许:
new Foo;
相反,请使用:
new Foo();
省略括号会导致细微的错误。 这两行不相等:
new Foo().Bar();
new Foo.Bar();