JavaScript 代码风格指南 这份指南列出了编写 JavaScript 时需要遵守的规范, 指明哪些应该提倡 , 哪些应该避免. 本文基于 google 的规范翻译整理(JavaScript 是许多 Google 开源项目使用的主要客户端脚本语言).
(3) JavaScript 代码风格规范(2) Javascript 类型 鼓励和强制使用编译器.
既然使用 JSDoc 来文档化, 那就尽可能规范和准确. 支持的类型基于 EcmaScript 4 spec .
JS 类型语言
ES4 建议中包含一种用于 JS 类型说明的语言. 我们在 JSDoc 中使用这种语言来说明函数的类型和返回值.
由于 ES4 建议还处于发展中, 因此这种说明语言已经有所变化. 编译器依然支持旧的类型语法, 单这些语法已经被废弃来.
参见下表:
JS 类型语言
JS 中的类型
参见下表:
JS 中的类型
类型转换
在那些类型检查基于表达式推断不够准确的情况中, 可以使用类型注解来增加一些类型转换注释, 并且用圆括号括起表达式. 圆括号是必须的.
可空类型 vs. 可缺省参数/属性
由于 JS 属于弱类型语言. 因此理解可缺省, 可空和未定义函数参数和类属性之间的微妙差异是非常重要的.
接口以及类的实例默认是可空类型. 例如下面的声明:
1 2 3 4 5 6 7 8 9 10 11 12 13 * 一些类, 用一个值来初始化. * @param {Object} value Some value. * @constructor */ function MyClass (value ) { * 一些值. * @type {Object} * @private */ this .myValue_ = value; }
告诉编译器 myValue_
属性既可能是对象也可能是 null
. 如果不允许 myValue_
为 null
则应该像这样声明:
1 2 3 4 5 6 7 8 9 10 11 12 13 * Some class, initialized with a non-null value. * @param {!Object} value Some value. * @constructor */ function MyClass (value ) { * Some value. * @type {!Object} * @private */ this .myValue_ = value; }
如果采用这种方式, 当编译器检测到某段代码中 MyClass
使用 null
来初始化时, 就会报警告.
函数的可缺省参数在运行时可能是未定义的, 因此如果它们被赋给类的属性, 这些属性必须相应地声明:
1 2 3 4 5 6 7 8 9 10 11 12 13 * 一些类, 包含可缺省参数被初始化. * @param {Object=} opt_value Some value (optional). * @constructor */ function MyClass (opt_value ) { * 一些值. * @type {Object|undefined} * @private */ this .myValue_ = opt_value; }
上述代码告诉编译器 myValue_
可能包含一个对象, null
或未定义类型.
注意可缺省参数 opt_value
以类型 {Object=}
来声明, 而非 {Object|undefined}
. 这是因为可缺省参数也有可能会是未定义类型. 虽然显式地说明可缺省变量可能是未定义类型并不会有什么害处, 但是这样既无必要也会降低代码的可读性.
最后, 注意可空类型和可缺省类型都是正交属性.
下面的四种声明都是不同的:
1 2 3 4 5 6 7 8 9 10 11 * 接受 4 格参数, 其中两个是可空类型, 两个是可缺省类型. * @param {!Object} nonNull Mandatory (must not be undefined), must not be null. * @param {Object} mayBeNull Mandatory (must not be undefined), may be null. * @param {!Object=} opt_nonNull Optional (may be undefined), but if present, * must not be null! * @param {Object=} opt_mayBeNull Optional (may be undefined), may be null. */ function strangeButTrue (nonNull, mayBeNull, opt_nonNull, opt_mayBeNull ) { };
类型定义
有时类型可以变得非常复杂. 一个接受某个元素内容的函数可能看上去会是这样:
1 2 3 4 5 6 7 8 * @param {string} tagName * @param {(string|Element|Text|Array.<Element>|Array.<Text>)} contents * @return {!Element} */ goog.createElement = function (tagName, contents ) { ... };
你可以使用 @typedef
来标记常见的类型表达式, 比如:
1 2 3 4 5 6 7 8 9 10 11 goog.ElementContent; * @param {string} tagName * @param {goog.ElementContent} contents * @return {!Element} */ goog.createElement = function (tagName, contents ) { ... };
模板类型
编译器已经对模板类提供了有限度的支持. 它只能通过 this
参数的类型和 this
参数是否遗失来判断匿名函数字面量中的 this
类型.
1 2 3 4 5 6 7 8 9 10 11 12 13 * @param {function(this:T, ...)} fn * @param {T} thisObj * @param {...*} var_args * @template T */ goog.bind = function (fn, thisObj, var_args ) { ... }; goog.bind(function ( ) { this .someProperty; }, new SomeClass()); goog.bind(function ( ) { this .someProperty; });