You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
/** * @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. */functiondestructured(ordinary,{num, str ='some default'}={})
3.8 枚举
在对象里面,枚举可以通过@enum注解来定义。在枚举对象被定义之后,它不能添加其他额外的属性。
枚举必须被序列化,并且所有的枚举值不能改变。
/** * Supported temperature scales. * @enum {string} */constTemperatureScale={CELSIUS: 'celsius',FAHRENHEIT: 'fahrenheit',};/** * An enum with two options. * @enum {number} */constOption={/** The option used shall have been the first. */FIRST_OPTION: 1,/** The second among two options. */SECOND_OPTION: 2,};
/** * Something that can frobnicate. * @record */classFrobnicator{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){}}
4.9 抽象类
合适的时候使用抽象类。抽象类或者抽象方法必须添加@abstrct的注解。
5. 函数
5.1 顶层函数
顶层函数可以直接被定义在exports对象上面,或者定义为局部函数,然后选择性的导出。
/** @param {string} str */exports.processString=(str)=>{// Process the string.};
/** @param {string} str */constprocessString=(str)=>{// Process the string.};exports={processString};
/** * 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. */constmoduleLocalFunc=(numParam,strParam)=>numParam+Number(strParam);// Uses the single expression syntax with `void` because the program logic does// not require returning a value.// 使用带`void`的单行语句写法,因为程序逻辑上不要求返回一个值getValue((result)=>voidalert(`Got ${result}`));classCallbackExample{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;});}}
/** * @param {!Array<string>} array This is an ordinary parameter. * @param {...number} numbers The remainder of arguments are all numbers. */functionvariadic(array, ...numbers){}
constlongString='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.';
替代:
constlongString='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.';
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.}
编程风格
良好的编程风格有助于团队的协同开发,这里是针对 Google JavaScript Style Guide 语言特性章节的翻译,持续修正中,仅供参考。
1. 变量声明
1.1 使用
const
和let
使用
const
或者let
声明局部变量,默认使用const
声明。当明确该变量在以后会被重新赋值的时候使用let
,不再使用var
声明变量。1.2 一次只声明一个变量
1.3 需要的时候再声明,并尽可能初始化该变量
局部变量通常不要在其包含的块或类似块的结构的开始处声明。
为了缩小变量的作用域,应当在该变量初次使用的时候再声明。
1.4 根据需要声明变量类型
在声明语句的上方添加 JSDoc 注释或者使用行内注释都是可行的,但是两者不能同时使用。
2. 数组字面量
2.1 使用尾部逗号
当最后一个元素和右括号之间有换行符的时候,在最后一个元素后面加上逗号。
2.2 不要使用
Array
构造器创建数组Array
构造函数会因为传递参数的不同会产生不同的结果,使用字面量的形式来创建数组。在上面代码中,会出现一些歧义。当
x
为一个整数的时候,a3
会是一个包含x
个undefined
成员的数组;如果x
为其他数字,则会抛出异常。在定义一个定长度的数组的时候可以使用
new Array(length)
来定义2.3 非数值属性
不要在一个数组里面定义和使用非数值的属性,除开
length
属性。如果要使用,请使用Map
或者Object
代替。2.4 解构
可以使用数组字面量来对数组进行解构,吧 rest 参数放在最后的位置,不需要使用的变量可以省略。
。
可以在函数参数使用数组解构,如果参数是可选的,可以提供一个
[]
作为参数的默认值,成员的默认值放在左边。2.5 扩展运算符
使用扩展运算符
...
展开一个数组,并且可以代替一些数组原型的方法,...
后面没有空格。3. 对象字面量
3.1 使用尾部逗号
当最后一个属性和右括号之间有换行符的时候,在最后一个属性后面加上逗号。
3.2 不要使用
Object
构造器来创建对象虽然
Object
没有Array
那样混淆的问题,但还是不要使用Object
来创建对象,直接使用{}
来创建。3.3 不要混合使用带引号和不带引号的键值
3.4 计算属性名
计算属性名(e.g.,
['foo' + bar()]: '10'
)是被允许使用的,但要把它作为带引号的属性名(不能和不带引号的属性名混合使用),除非这个属性名是一个 symbol 值(e.g.,[Symbol.iterator]
)。枚举值也能被用作属性名,但是也不能和其他非枚举的属性名混合使用。
3.5 方法简写
对象里面的方法可以使用
{method() { ... }}
简写,取代冒号后面跟function
或者箭头函数的写法。简写方法里面的
this
指向当前对象自己,箭头函数里面的this
指向对象外面的作用域。3.6 属性简写
在对象里面,属性可以被简写。
3.7 解构赋值
对象的解构赋值可用于赋值语句的左侧,用来在一个对象里面解构出多个值。
对象解构赋值能用在函数参数里面,但应该尽可能保持简洁:只有一层的简写属性。如果有更深层次的嵌套或者计算属性,则不可以使用参数解构。
左边解构参数的默认值可以写成
{str = 'some default'} = {}
而不是{str} = {str: 'some default'}
,如果待解构的参数对象是可选的,必须使用{}
对参数设置默认值。3.8 枚举
在对象里面,枚举可以通过
@enum
注解来定义。在枚举对象被定义之后,它不能添加其他额外的属性。枚举必须被序列化,并且所有的枚举值不能改变。
4. 类
4.1 构造器
类构造器是可选的,但是子类的构造器是必须的,因为要在子类的构造器里面调用
super()
,接口应该在构造器中声明非方法属性。4.2 字段
在构造方法里面设置一个类对象的所有字段(除开方法的属性)。
@const
注解。@private
、@protected
、@package
,并且添加了@private
注解的字段名称以_
结尾。4.3 计算属性
在一个类里面,当属性是一个 symbol 值的时候,才能使用计算属性。
定义在类里面的
Symbol.iterator
方法用来处理迭代的逻辑。symbol 应该尽量少的使用
4.4 静态方法
在不影响可读性的情况下,宁可使用模块本地函数,也不要使用私有静态方法。
静态方法只能够在类内部自己调用。
静态方法不能在包含动态实例的变量上面调用,这些变量可能是构造函数,也可能是子类(如果已经调用了,必须加上
@nocollapse
注解),并且不能直接在没有定义它自己方法的子类里面调用。Disallowed:
4.5 不要直接操作
prototype
class
关键字允许比定义原型属性更清晰、更可读的类定义。普通的实现代码没有处理这些对象的业务,尽管它们对于定义类仍然很有用,混合和修改对象的原型是被明确禁止的。
注意:一些框架的代码(比如 Angular)可能需要使用
prototype
,不应该使用更糟糕的变通方法来避免这样做。4.6 Getter and Setter
不要使用 JavaScript 的 getter 和 setter 属性。它们会让人感觉很惊讶,难以推理,并且在编译器中的支持是有限的。使用普通的方法去替代它。
注意:有一些无法避免使用 getter 和 setter 的情况(比如,Angular 的数据绑定)。仅仅在这些情况下,getter 和 setter 才可以被小心的使用。并且要使用带上
get
和set
关键字的简写方法来定义。或者也使用Object.defineProperties()(不是
Object.defineProperty()`,会影响到属性的重命名)来定义 getter 和 setter 方法。getter 方法不能改变观察中的状态。4.7 重写 toString
在保证总是成功执行并且没有副作用的情况下,
toString()
方法可以被重写。4.8 接口
接口通过添加
@interface
或者@record
注解来声明。使用
@record
注解的接口声明能够被一个对象或者一个类显式地(即@implements
)和隐式地实现。接口里面所有非静态方法的方法体必须是空的。字段必须在构造函数里面声明为未初始化的成员。
4.9 抽象类
合适的时候使用抽象类。抽象类或者抽象方法必须添加
@abstrct
的注解。5. 函数
5.1 顶层函数
顶层函数可以直接被定义在
exports
对象上面,或者定义为局部函数,然后选择性的导出。5.2 嵌套函数和闭包
函数里面能够嵌套定义函数,函数的名称使用
const
定义。5.3 箭头函数
箭头函数为嵌套函数提供了一个简洁的语法声明和容易理解的
this
作用域。尽可能使用箭头函数而不是funciton
关键字,特别是在嵌套函数里面。使用箭头函数,避免
f.bind(this)
、const self = this
这种写法来修改this
的指向。箭头函数作为回调函数的时候非常有用,因为它可以显示的指定需要传递的参数,而绑定的回调函数直接传递所有的参数。箭头函数的左侧可以包含零个或者多个参数。如果只有一个不可分解的参数,则包裹参数的括号则是可省略的。使用括号时,可以指定内联参数类型。
箭头函数的右边是函数体。默认情况下,函数体是一个块语句(用花括号扩起来的零个或者多个语句)。如果程序在逻辑上需要返回一个值,或者在函数和方法调用之前使用
void
(使用void
确保返回一个undefined
,防止值泄漏,并传达意图)函数体也可以隐式的返回一个单行语句。返回一个单行语句可以提高程序的可读性。例子:
5.4 Generators
Generator 可以支持很多有用的抽象,可以根据需要使用。
在
function
关键字后面附加一个*
,并且与函数名之间用一个空格隔开,来定一个 Generator 函数。当使用委托 yield 时,在yield
关键字前面附加一个*
5.5 参数及返回值类型
函数的参数及返回值类型通常应该使用 JSDoc 注释记录。
5.5.1 默认参数
在参数列表中,使用等于操作符的可选参数是被允许的。可选参数的等于号的两边都必须有空格,并且命名和必填参数一样(即不要使用
opt_
的前缀)。可选参数的 JSDoc 跟在必选参数后面,并且在类型标注上面使用=
后缀。可选参数不要使用产生可见副作用的初始值。具象函数的所有可选参数必须要有默认值,尽管这个值是undefined
。与之相反的是,抽象或者接口方法必须省略默认的参数值。尽可能少的使用默认参数。当有需要一些没有自然顺序的可选参数的时候,可以使用函数参数解构取代。
5.5.2 Rest 参数
使用 Rest 参数避免访问
arguments
,在 JSDoc 中,Rest 参数使用一个...
前缀来标注类型。Rest 参数必须在参数列表的最后面。在...
和参数名之间没有空格。不要使用var_args
命名 Rest 参数。不要使用局部变量或者arguments
参数命名参数,这些命名会混淆内建名称。5.6 泛型
必要时,在函数或者方法定义之上的 JSDoc 中使用
@template TYPE
声明泛型函数和方法。5.7 延展操作符
函数可以使用延展操作符(
...
)调用。当一个数组或迭代被拆分成一个可变参数函数的多个参数时,使用延展操作符代替Function.prototype.apply
。在...
后面没有空格。6. 字符串字面量
6.1 使用单引号
普通字符串使用单引号
'
分割,不要使用双引号""
普通字符串字面量不能跨越多行。
6.2 模版字符串
对于复杂的字符串拼接,使用模版字面量(使用 ` 分割),特别是牵涉到多个字符串的时候。模版字符串可以跨越多行。
如果模版字符串跨越多行,空格和换行都是可以被正确解释。
6.3 不要使用行延展
不要在普通字符串或者模版字符串里面使用行延展(在一个字符串内部一行的末尾加上反斜杠
\
)。虽然 ES5 允许这个语法,但是当在反斜杠后面加上一个空格的时候,可能导致一些难以解决的问题。并且被加上的这些空格对于阅读代码的人来说不容易被发现。不允许:
替代:
7. 数值字面量
在 JavaScript 中,数值可以使用十进制、十六进制、八进制或者二进制表示。使用带小写字母的
0x
、0o
、0b
前缀分别表示十六进制,八进制和二进制。数值不要使用前置 0,除非后面跟了x
、o
、b
这三个字母。8. 控制结构
8.1 循环
随着 ES6 的到来,JavaScript 现在三种不同的
for
循环的方式。这三种方式都可以被使用,但建议尽可能的使用for...of
。for...in
循环只能用于字典类型的对象例如:{'bar': 'foo'}
,并且不能使用for...of
去迭代一个数组。在
for...in
循环里面使用Object.prototype.hasOwnProperty()
可以排除原型对象上面的属性。尽可能的使用Object.keys()
和for...of
来代替for...in
循环。8.2 异常
异常是一门编程语言很重要的一部分。异常在任何异常情况发生的时候应该被使用。保持抛出一个
Error
或者Error
的子类:不要抛出一个字符串或者一个对象字面量。使用new
来构造一个Error
。这种处理可以使用
Promise
的 rejection 值类扩展。在异步函数中,Promise.reject(obj)
和throw obj
是相等的。在一个函数里面,自定义异常是一种非常好的输出错误信息的方式。自定义异常应该被定义在原生
Error
类型不适用的场景下。选择抛出异常,而不是特定的的错误处理方法(比如传递一个包含引用类型的 error 或者是一个带有 error 属性的对象)。
8.2.1 空 catch 块
对捕获的异常不做任何响应是非常不正确的。如果在 catch 块中确定不需要任何操作,也需要在 catch 块中加一段注释表示为什么这么做是对的。
8.3 switch 语句
术语说明:在 switch 块的花括号里面是一个或者多个语句组。每个语句组由一个或多个 swtich 标签(
case FOO:
或者default:
)组成,后面跟着一个或者多个语句。8.3.1 Fall-through: 注释
在一个 swtich 块里面,每个语句组需要被中断(使用
break
、return
或者throw
一个异常),或者使用注释进行标记标明程序基础会执行到下一个语句。任何能够表示 fall through 意思的注释都可以。这个特殊的注释不要求在 swtich 块语句组的最后面。8.3.2
default
case 是要存在的每个 swtich 语句都包含一个
default
语句组,尽管它没有任何代码。default
语句必须在最后面。9. this
使用
this
的几种场景:类的构造器和方法里面,在类构造器和方法里面定义的箭头函数里面,或者在 JSDoc 中使用@this
标注了的立即执行函数中。不能使用
this
的几种场景:引用全局对象,调用eval
的上下文,调用事件的目标,或者是不必要的call()
或者apply()
函数。10. 相等检查
除下述情况外使用严格相等运算符(
===
、!===
)。10.1 根据需求检查
需要同时捕获
null
和undefined
。11. 不允许使用
11.1 with
不能使用
with
关键字。它会使代码变得难以理解,并且在严格模式下的 ES5 也已经被禁止使用。11.2 动态代码求值
不要使用
eval
和Function(...string)
构造器(出了代码加载器)。这个特性存在潜在的危险,并且在 CSP 环境不会有用。11.3 自动分号
总是使用分号结束语句(除了上面提到的类和函数声明之外)。
11.4 非标准特性
不要使用非标准特性。这个包括一些就特性(比如
WeakMap.clear
),没有写入标准的新特性(比如处于 TC39草案阶段和征求意见阶段的建议,或者是已经采取的意见但是没有实现的 web 标准),或者使一些只在部分浏览器实现了一些特性。只能使用 ECMS-262 或者 WHATWG 中定义的特性。非标准的语言扩展(比如一些由外部转置器提供的)被禁止使用。11.5 原始类型的包装对象
不要在原始包装对象(比如
Boolean
、Number
、String
、Symbol
)前面使用new
,也不要在类型标注里面使用。不允许的:
包装对象可以作为函数调用(比使用
+
或者拼接空字符串更好),或者创建一个 Symbol。11.6 修改内建对象
不要修改内建类型,无论是向它们的构造方法添加方法,还是向它们的原型添加方法。避免依赖修改内建对象的库。请注意,JSCompiler 的运行时库将在可能的地方提供符合标准的填充;其他任何东西都不能修改内建对象。
非必要情况下(比如第三方 API 要求)不要在全局对象上面添加 Symbol。
11.7 调用构造器的时候省略
()
new
语句调用构造器的时候不要省略小括号()
。不允许的:
替代:
省略括号可能导致一些细微的错误。下面两个语句是不相等的。
The text was updated successfully, but these errors were encountered: