Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Google JS 编码规范

Google JS 编码规范

Google JS 编码规范

Avatar for zzxadi

zzxadi

May 12, 2014

Other Decks in Programming

Transcript

  1. Google JavaScript 代码风格指南 修正版本 2.28 Aaron Whyte Bob Jervis Dan

    Pupius Eric Arvidsson Fritz Schneider Robby Walker 每个风格点都有一个展开/收起按钮以便你可以得到更多的信息:▽. 你可以将全部展开或收起: ▽ 全部展开/全部收起 内容树 JavaScript 语 言规范 变量 常量 分号 嵌套函数 块内函数声明 异常处理 自定义异常 标准特 性 基本包装类型 多级原型结构 方法定义 闭包 eval() with() {} thisfor-in 循环 关联数组 多行字符串字面量 Array 和 Object 字面 量 修改内置对象的原型 IE 下的条件注释 JavaScript 代 码风格规范 命名 自定义 toString()方法 延迟初始化 明确作用域 代码格式化 括号 字 符串 可见性(私有和受保护的字段) JavaScript 类型 注释 内部类和枚举 编译压缩 提示和技巧 背景 JavaScript 是一门客户端脚本语言,Google 经常用它来晒优越,本文档列出了一些在做 JS 项 目时需要注意的地方。都是高富帅整理的,还望各位屌丝们有时间都好好看看多学学,别对这个 世界太消极了! (译者注:所谓的客户端呢就是浏览器或本地软件环境,市面上所有的浏览器都 支持 JS,JS 发展到现在统一 WEB 端再进军移动互联网后,相信在一段时间内都是不可被轻易 替代的语言,所以有兴趣的同学们可以多关注一下) JavaScript 语言规范 变量(var) 原文▽ 每个变量声明都要加上 var 关键字噢。
  2. 解释:如果你不指定关键字 var, 该变量就会暴露在全局作用域(window)中,这很可能会覆盖 全局作用域中的同名变量,从而引发问题(另外 GC 也会因此而无法有效回收内存啊),所以务 必用 var 声明变量。 常量

    原文▽ 常量命名用类似 NAMES_LIKE_THIS 这样的形式。没事干了可以用@const 来标记它是常量, 但永远不要用 const 关键字来进行常量声明。 解释: 对于基本类型的常量,命名简介解释一下作用就可以了。 /** * 一分钟有多少秒呀 * @type {number} */ goog.example.SECONDS_IN_A_MINUTE = 60; 对于非基本类型,用@const 注释一下会更明了。 /** * 已知单位所对应的秒数 * @type {Object.<number>} * @const */ goog.example.SECONDS_TABLE = { minute: 60, hour: 60 * 60 day: 60 * 60 * 24 } 这个标记主要是让编译器知道这变量是常量状态。 至于关键词 const,IE 不解析啊会报错的,所以别用啦。 分号 原文▽每一语句的结尾都要加上分号噢。 如果不加分号 JS 解释器也会按隐式分隔的标准去执行,但那样调试、压缩、合并的时候都很不 方便。不要那样做嘛你可以做的更好的不是么。
  3. 而且在某些情况下,不写分号可是很危险的: // 1. MyClass.prototype.myMethod = function() { return 42; }

    // 这个缺德的没写分号 (function() { // 匿名函数的执行 })(); var x = { 'i': 1, 'j': 2 } // 这个天杀的也没写分号 // 2. 在 IE 和 FF 的某些版本里试试(译者表示没试出来) // 老子知道你永远也不会写这样 2 的代码的,但它真的会因为木有写分号而报错噢 [normalVersion, ffVersion][isIE](); var THINGS_TO_EAT = [apples, oysters, sprayOnCheese] // 这个屌丝木有写分号 // 3. conditional execution a la bash -1 == resultOfOperation() || die(); 上段代码会发生什么事情? 1. 会报错(number is not a function)-第一个方法返回了 42,因为没分号啊,后面就 直接跟括号,所以第二个方法就很杯具的被当成一个参数传进来给 42 执行了(效果等 同于"42(func)()"),可 42 并不是一个方法,报错。 2. 在运行时可能会报 'no such property in undefined' 这样的错误,效果等同于是执 行 x[ffVersion][isIE]()。(译者木验证) 3. 如果 resultOfOperation() 返回非 NaN 那就会调用 die 方法, 并导致 die() 执行 后的返回结果会赋给 THINGS_TO_EAT 。 为啥会这样呢? JS 语句以分号作为结束符使得 JS 解释器解析,如果省略分号,就会由 JS 解释器确定语句的结 尾。尼玛,不是在很明确的情况下,它能确定么?上面几个例子里,都是在语句中声明了/函数/ 对象/数组,但闭括号("}"或"]")并不代表着结束。如果下一个语句开始是一个中缀或括号运算 符,那 JS 就永远不会结束声明。
  4. 这真的是很奇怪的事情噢亲, 而且真的不知道还会出现什么奇怪的事情噢亲, 所以确保你的语句 有分号结束吧。 译者注: 加上分号也会在某些情况下增进代码的性能, 因为这样解析器就不必再花时间推测应该 在哪里插入分号了(取自《Javascript 高级程序设计》) 嵌套函数

    原文▽可以使用,木有问题。 嵌套函数非常有用,它可以重用而减少代码量,或防止一些辅助作用的方法暴露在外等优点,请 随意把玩(叫破喉咙也不会有人来救它的)。 块内函数声明 原文▽呀咩嗲。 只有屌丝才这样做: if (x) { function foo() {} } 虽然大多数 JS 引擎都支持块内声明函数,但它并不是 ECMAScript 标准的一部分(详 见 ECMA-262, 第 13 条和第 14 条)。更糟糕的是各引擎对于块内函数声明的实现都不一样, 和 EcmaScript 的建议相违背。 ECMAScript 只允许在根脚本语句或其他函数中进行函数声明, 如果一定要用的话可以在块内用变量来定义函数: if (x) { var foo = function() {} } 异常处理 原文▽支持合理使用。 不管谁写代码都不能百分百的说自己的程序木有异常的。所以,细心的检查代码是一方面,预先 处理一些可能出现的异常也是有必要的(使用 try-catch(e)),特别是正在做一些重要项目的时 候(框架什么的)。 译者注:如果是预先就知道自己的代码会发生错误时,再使用 try-catch 语句就不合适了噢,而 是应该预先对其进行检查,防止错误的出现(取自《JavaScript 高级程序设计》)
  5. 自定义异常 原文▽支持合理使用。 没有自定义异常抛出的情况下, 代码运行可能会报一些原始的错误信息, 但包含的内容和错误描 述会因浏览器而不同却都不是特别明确,不易维护,No fashion!所以在感觉适当的时候可以使 用自定义异常抛出(这里指的是 throw,带有适当信息的自定义错误能够显著提升代码的可维护 性)。

    标准特性 原文▽总是优越于非标准特性之上。 为了获得最大的可移植性和兼容性, 尽量依赖于标准方法。 (比如使用 string.charAt(3) 而 不是 string[3] ,再比如通过 DOM 原生方法去访问节点元素,而不是使用某框架封装好的获 取方法或快捷引用)。 基本包装类型 原文▽主动 new 基本包装类型(Boolean/Number/String)的人儿永远也成不了高富帅! 木有必要去使用基本包装类型么就不要使用啦,而且它在以下这种情况下很危险: var x = new Boolean(false); if (x) { alert('hi'); // 显示 hi。因为基本包装类型的实例调用 typeof 会返回"object",对象么 在判断时都会被转换为布尔值 true。 } 所以,你懂得!就算不懂,也终有一天会懂的嘛! 唉,可是还是有种情况是可以使用的嘛,比如类型转换: var x = Boolean(0); if (x) { alert('hi'); // 不会弹框显示 hi } typeof Boolean(0) == 'boolean'; typeof new Boolean(0) == 'object'; 它对于 number, string 和 boolean 之间的类型转换很有帮助。 多级原型结构
  6. 原文▽ 不是怎么推荐使用。 多级原型结构指的是 JavaScript 实现继承。 比如自定义类 D, 并把自定义类 B 作为

    D 的原型, 那就是一个多级原型结构了。怎么说呢,结构越来越复杂了就越难维护。 鉴于此,使用 the Closure Library 的 goog.inherits() 或许会是更好的选择。 function D() { goog.base(this) } goog.inherits(D, B); D.prototype.method = function() { ... }; 方法定义 原文▽ Foo.prototype.bar = function() { ... }; 给原型对象的构造函数添加方法和属性有很多种方式,更倾向于使用以下这种风格: Foo.prototype.bar = function() { /* ... */ }; 闭包 原文▽ 上古之大杀器,顺则飞龙在天,滥则亢龙有悔。 很有用,却经常被忽略(哪里被忽略了基本哪次面试都有被问到这样的问题有木有!!!)。有 耐心的去看看 a good description of how closures work。 有一件需要注意的事情,闭包会保持一个指向它封闭作用域的指针,所以在给 DOM 元素附加闭 包时,很可能会产生循环引用,进一步的消耗内存,比如下面的代码: function foo(element, a, b) { element.onclick = function() { /* uses a and b */ }; }
  7. 即便这个闭包函数内部并没有使用 element,可它还是保持了对 element, a 和 b 的引用, 而 element 也保持了对这闭包函数的引用,就导致了循环引用,无法被

    GC 回收。如果遇到了 这种情况,可以将代码优化一下: function foo(element, a, b) { element.onclick = bar(a, b); } function bar(a, b) { return function() { /* uses a and b */ } } 译者注:由于 IE9 之前的版本对 JScript 对象和 COM 对象使用不同的垃圾收集例程,因此闭包 在 IE 的这些版本中会导致如上问题。这个匿名函数作为 element 元素事件处理程序,形成闭包 的状态就会保存对父层函数内活动对象的引用,只要匿名函数存在,element 的引用数至少也是 1,因此它所占用的内存就永远不会被回收。注意,闭包会引用包含函数的整个活动对象,即使 闭包不直接引用 element,包含函数的活动对象中也仍然会保存一个引用。因此,有必要把使用 完的 element 变量设置为 null,解除对 DOM 对象的引用,确保正常回收其占用的内存。(取自 《Javascript 高级程序设计》184P) eval() 原文▽ 只用于反序列化。(反序列化的意思是从字节流中重构对象,这里指的应该是 JSON 字符串重 构成对象,或是执行服务器返回的 JS 语句) eval() 很不稳定,会造成语义混乱,如果代码里还包含用户输入的话就更危险了,因为你无法 确切得知用户会输入什么!难道就不能用更好更清晰更安全的方式去写你的代码么孩子? 然而 eval 很容易解析被序列化的对象,所以反序列化的任务还是可以交给它做的。 (例如:解 析 RPC 响应的时候) 反序列化的过程就是将字节流转换成内存中的数据结构。例如,你可能会将对象输出到文件中: users = [ { name: 'Eric', id: 37824, email: '[email protected]' }, { name: 'xtof', id: 31337, email: '[email protected]'
  8. }, ... ]; 用 eval 可以很简单的读取这些数据到内存中。 同样,用 eval() 可以简单的解码 RPC

    返回值。例如,你用 XMLHttpRequest 发出一个 RPC 请求,服务器端相应返回 JavaScript 代码: var userOnline = false; var user = 'nusrat'; var xmlhttp = new XMLHttpRequest(); xmlhttp.open('GET', 'http://chat.google.com/isUserOnline?user=' + user, false); xmlhttp.send(''); // Server returns: // userOnline = true; if (xmlhttp.status == 200) { eval(xmlhttp.responseText); } // userOnline is now true. with() {} 原文▽别用。 使用 with 语句会让你的代码行如踏云,毫无安全感。因为用 with 添加过来的对象可能会对当 前局部作用域的属性与方法产生冲突,进而影响到整个环境。比如,下面的代码发生了什么: with (foo) { var x = 3; return x; } 回答: 啥事都可能发生。这个局部变量 x 可能会被 foo 对象里的属性所覆盖,它甚至可能会有 setter,导致在赋值 3 时会执行许多其他的代码,所以太危险了,别用 with 。 this 原文▽ 仅在构造函数,方法,闭包中去使用它。
  9. this 语义很特别。 它大多数情况下会指向全局对象, 有的时候却是指向调用函数的作用域的 (使 用 eval 时) , 还可能会指向

    DOM 树的某个节点 (绑定事件时) , 新创建的对象 (构造函数中) , 也可能是其他的一些什么乱七八糟的玩意(如果函数被 call() 或者被 apply() )。 很容易出错的,所以最好是以下这两种情况的时候再选择使用:  在构造函数中(原型对象)  在对象的方法中(包括创建的闭包) for-in 循环 原文▽ 只在 object/map/hash 要遍历键值的时候使用。 对 Array 类型的对象进行 for-in 循环可能会出错,因为它不是从 0 到 length - 1 , 而是这个对象包括其原型链上的所有键值,下面是一些失败的例子: function printArray(arr) { for (var key in arr) { print(arr[key]); } } printArray([0,1,2,3]); // 虽然这没问题 var a = new Array(10); printArray(a); // 这就错了 a = document.getElementsByTagName('*'); printArray(a); // 错 a = [0,1,2,3]; a.buhu = 'wine'; printArray(a); // 错的 a = new Array; a[3] = 3; printArray(a); // 还是错的 遍历数组通常用 for 就可以了。(译者注:如果木有顺序要求,可以使用 for(var i = l;i--;)速度会 更高些,但这是 10 万级效率差,通常是可以被忽略的) function printArray(arr) { var l = arr.length;
  10. for (var i = 0; i < l; i++) {

    print(arr[i]); } } 关联数组 原文▽ 永远不要用 Array 去做 map/hash/associative 要做的事情。 不允许使用关联 Arrays ……更具体的说是不允许使用非数字索引的数组(Array 不都是用数字 索引的么, 也没办法用吧除非是两个数组同索引) 。 如果你需要一个 map/hash 那就用 Object 来 代替 Array 吧, 实际上你想用的就是 Object 而不是 Array。 Array 只是继承自 Object (和 Date, RegExp 和 String 一样)。 多行字符串字面量 原文▽不要! 不要这样写: var myString = 'A rather long string of English text, an error message \ actually that just keeps going and going -- an error \ message to make the Energizer bunny blush (right through \ those Schwarzenegger shades)! Where was I? Oh yes, \ you\'ve got an error and all the extraneous whitespace is \ just gravy. Have a nice day.'; 空白字符开头字符行不能被很安全的编译剥离,以至于斜杠后面的空格可能会产生奇怪的错误。 虽然大多数脚本引擎都支持这个,但它并不是 ECMAScript 标准的一部分。 可以用+号运算符来连接每一行: var myString = 'A rather long string of English text, an error message ' + 'actually that just keeps going and going -- an error ' + 'message to make the Energizer bunny blush (right through ' + 'those Schwarzenegger shades)! Where was I? Oh yes, ' + 'you\'ve got an error and all the extraneous whitespace is ' + 'just gravy. Have a nice day.'; Array 和 Object 字面量 原文▽推荐使用。
  11. 用 Array 和 Object 字面量代替 Array 和 Object 构造函数。 Array

    构造函数会因为传参不当而导致理解错误。 // 长度是 3. var a1 = new Array(x1, x2, x3); // 长度是 2. var a2 = new Array(x1, x2); // 如果 x1 是自然数那数组的长度就变成了 x1 // 如果 x1 是数字但不是自然数就会报错 // 不然数组就会有一个元素 x1 var a3 = new Array(x1); // 长度是 0. var a4 = new Array(); 如上,如果传入了一个参数而不是两个,那这数组可能就不是预期的长度了。 为了避免这种情况,用更可读的数组字面量吧。 var a = [x1, x2, x3]; var a2 = [x1, x2]; var a3 = [x1]; var a4 = []; Object 构造函数没有如上问题,但考虑到可读性和一致性,用字面量更好些。 var o = new Object(); var o2 = new Object(); o2.a = 0; o2.b = 1; o2.c = 2; o2['strange key'] = 3; 应该写成: var o = {}; var o2 = { a: 0, b: 1,
  12. c: 2, 'strange key': 3 }; 修改内置对象的原型 原文▽别这样做。 禁止修改 Object.prototype

    和 Array.prototype 这样的内置对象。可能修 改 Function.prototype 危险性会小些,但它同样会在调试中出现问题,所以尽量避免这样 的情况吧。 IE 下的条件注释 原文▽别用。 不要这样做: var f = function () { /*@cc_on if (@_jscript) { return 2* @*/ 3; /*@ } @*/ }; 条件注释会妨碍自动化工具的执行,因为他会在运行时改变 JavaScript 语义树。 JavaScript 代码风格规范 命名 原文▽ 通常,使用类似 于 functionNamesLikeThis, variableNamesLikeThis, ClassNamesLikeThis, Enum NamesLikeThis, methodNamesLikeThis, 和 SYMBOLIC_CONSTANTS_LIKE_THIS 这样 的命名方式(驼峰式) 属性和方法  私有的属性,变量和方法(在文件或类中)都应该改以下划线开头。  受保护的属性,变量和方法不需要用下划线(和公开的一样)。 更多关于 private 和 protected 去看 visibility 这一部分吧。 方法和函数参数
  13. 可选参数以 opt_ 开头。 函数参数不固定的时候应该有个参数 var_args 以数组的形式将参数传进来, 也许你不喜欢这 样做,那你可以使用 arguments 这个伪数组。

    可选和可变参数都应该用 @param 注释标记一下,虽然对解释器都没什么影响,但还是尽量遵 守吧,以让代码能够更易理解。 属性的访问器 EcmaScript 5 不推荐使用对象属性的 getters 和 setters。你用了就用了吧,但是你得注意,千 万别让 getters 方法改变当前对象的各属性状态。 /** * 错 -- 不要这样做 */ var foo = { get next() { return this.nextId++; } }; }; 函数的访问器 函数对象中的 Getters 和 Setters 也不是必须的。但如果你用了,你最好以类 似 getFoo() 和 setFoo(value).的形式来命名。 (如果返回布尔型的值, 可以用 isFoo() 或 者其他更自然的名字) 命名空间 JavaScript 并不支持包和命名空间。 这样就导致 JS 在两个项目以上的集成环境中可能会出现全局作用域下的命名冲突,后果严重且 不易调试。为了提高代码的公用性,请大家遵守如下约定以避免冲突。 在全局作用域下使用伪命名空间 在全局作用域下对相关的 JS 项目或库用唯一的顶级变量标识当作伪命名空间。比如你的项目名 是"Project Sloth",那可以写一个伪命名空间 sloth.*. var sloth = {}; sloth.sleep = function() { ... }; 好多类似 the Closure Library and Dojo toolkit 这样的JS库都会给你一个高阶方法来让你声明命 名空间,然后你就可以在这个命名空间下进行各种声明了。
  14. goog.provide('sloth'); sloth.sleep = function() { ... }; 明确命名空间所有权 当你选择了一个子命名空间下进行开发,请告知父命名空间的负责人(你懂得)。假设你开始了 一个项目是

    sloths 下的 hats,请确保这个 Sloth 项目的负责人知道是你在用 sloth.hats。 外部代码和内部代码使用不同的命名空间 “外部代码”指的是你的代码体系以外的可独立编译的代码,比如你用了一个外部库 在 foo.hats.* 下,那你自己的内部代码就不应该再在其下面了,否则会导致产生冲突和难以 维护。 foo.require('foo.hats'); /** * 错 -- 不要这样做 * @constructor * @extend {foo.hats.RoundHat} */ foo.hats.BowlerHat = function() { }; 如果你需要在外部命名空间中定义新的 API, 那你应该通过显式的方法导出公共 API 函数。 自身 内部的代码需要使用这些 API 时,可以直接通过内部命名来调用,一是为了保持一致性,二是 能让编译器更好的优化代码。 foo.provide('googleyhats.BowlerHat'); foo.require('foo.hats'); /** * @constructor * @extend {foo.hats.RoundHat} */ googleyhats.BowlerHat = function() { ... }; goog.exportSymbol('foo.hats.BowlerHat', googleyhats.BowlerHat);
  15. 为增强可读性,将长名引用化为短别名 用局部别名引用完整的包名可以增强可读性。局部别名命名应和完整包名的最后一部分相匹配。 /** * @constructor */ some.long.namespace.MyClass = function() {

    }; /** * @param {some.long.namespace.MyClass} a */ some.long.namespace.MyClass.staticHelper = function(a) { ... }; myapp.main = function() { var MyClass = some.long.namespace.MyClass; var staticHelper = some.long.namespace.MyClass.staticHelper; staticHelper(new MyClass()); }; 呃……不要别名引用命名空间。 myapp.main = function() { var namespace = some.long.namespace; namespace.MyClass.staticHelper(new namespace.MyClass()); }; 避免访问别名引用对象的属性,除非它是个枚举。 /** @enum {string} */ some.long.namespace.Fruit = { APPLE: 'a', BANANA: 'b' }; myapp.main = function() { var Fruit = some.long.namespace.Fruit; switch (fruit) { case Fruit.APPLE: ... case Fruit.BANANA:
  16. ... } }; myapp.main = function() { var MyClass =

    some.long.namespace.MyClass; MyClass.staticHelper(null); }; 不要在全局作用域中创建别名引用,仅在函数块中去使用。 文件名 文件名应该全部字母小写,以避免在某些区分大小写的系统平台产生文件名混淆。文件名应该 以 .js 结束,且不要包含 - 或 _ ( - 比 _ 更好些)。 自定义 toString() 方法 原文▽ 应该总是成功调用,无副作用。 你可以为自己的对象定义 toString()方法。这是好事呀,但是你要确保你的实现方法满足: (1)总是成功的(2)木有副作用。如果你的方法不满足这两个条件,运行起来将会很危险。 例如,如果 toString() 调用了包含 assert 的方法, assert 可能会试着输出失败的对象 名,结果又调用了 toString()。 延迟初始化 原文▽可以。 没必要总是在声明变量的时候就进行初始化,延迟初始化挺好的。 明确作用域 原文▽ 始终需要明确。 任何时候都要明确作用域 - 提高可移植性和清晰度。 例如, 不要依赖于作用域链中的 window 对 象。有的时候你可能会想让你的其他应用的函数中使用的 window 对象不是之前所指的窗口对 象。 代码格式化 原文▽一两句话说不明白,展开了详细讨论
  17. 我们鼓励使用 C++ formatting rules 里面介绍的代码格式化规范,还有下面要说的: 大括号 因为分号会被隐式插入代码中,所以应将大括号和前面的代码放在一行,以防止误读。例如: if (something) {

    // ... } else { // ... } 数组和对象的初始化 合适的话,可以直接单行进行数组和元素的初始化: var arr = [1, 2, 3]; // 前后木有空格 var obj = {a: 1, b: 2, c: 3}; // 前后木有空格 多行数组和元素初始化时,最好缩进两个空格。 // 对象初始化 var inset = { top: 10, right: 20, bottom: 15, left: 12 }; // 数组初始化 this.rows_ = [ '"Slartibartfast" <[email protected]>', '"Zaphod Beeblebrox" <[email protected]>', '"Ford Prefect" <[email protected]>', '"Arthur Dent" <[email protected]>', '"Marvin the Paranoid Android" <[email protected]>', '[email protected]' ]; // 直接在方法中调用 goog.dom.createDom(goog.dom.TagName.DIV, { id: 'foo', className: 'some-css-class',
  18. style: 'display:none' }, 'Hello, world!'); 在初始化列表中如果碰到了比较长的属性名或值, 不要为了觉得让代码好看些而手工对齐, 比如: CORRECT_Object.prototype =

    { a: 0, b: 1, lengthyName: 2 }; 只有屌丝才这样做: WRONG_Object.prototype = { a : 0, b : 1,0 lengthyName: 2 }; 函数参数 尽可能的将所有的函数参数都写在同一行上,但为了保持增强可读性,如果一行超过了 80 字符 的话可适当的换行,甚至可以每个参数都独立一行,记得格式上的优化,比如缩进 4 个空格和 对齐括号。 以下是几种常见的: // Four-space, wrap at 80. Works with very long function names, survives // renaming without reindenting, low on space. goog.foo.bar.doThingThatIsVeryDifficultToExplain = function( veryDescriptiveArgumentNumberOne, veryDescriptiveArgumentTwo, tableModelEventHandlerProxy, artichokeDescriptorAdapterIterator) { // ... }; // Four-space, one argument per line. Works with long function names, // survives renaming, and emphasizes each argument. goog.foo.bar.doThingThatIsVeryDifficultToExplain = function( veryDescriptiveArgumentNumberOne, veryDescriptiveArgumentTwo, tableModelEventHandlerProxy, artichokeDescriptorAdapterIterator) { // ...
  19. }; // Parenthesis-aligned indentation, wrap at 80. Visually groups arguments,

    // low on space. function foo(veryDescriptiveArgumentNumberOne, veryDescriptiveArgumentTwo, tableModelEventHandlerProxy, artichokeDescriptorAdapterIterator) { // ... } // Parenthesis-aligned, one argument per line. Visually groups and // emphasizes each individual argument. function bar(veryDescriptiveArgumentNumberOne, veryDescriptiveArgumentTwo, tableModelEventHandlerProxy, artichokeDescriptorAdapterIterator) { // ... } 如果调用的函数本身就已经缩进了, 呃你当然可以针对于当前被调用的函数 (或之前的原始函数) 再向前缩进 4 个空格。以下这样的风格勉强可以接受: if (veryLongFunctionNameA( veryLongArgumentName) || veryLongFunctionNameB( veryLongArgumentName)) { veryLongFunctionNameC(veryLongFunctionNameD( veryLongFunctioNameE( veryLongFunctionNameF))); } 传递匿名函数 如果调用方法传参内有匿名函数的声明,函数体应相对于该调用方法(或变量名)缩进 2 个空 格,这样匿名函数体更易阅读(而不是将代码挤到屏幕的另一半)。 prefix.something.reallyLongFunctionName('whatever', function(a1, a2) { if (a1.equals(a2)) { someOtherLongFunctionName(a1); } else { andNowForSomethingCompletelyDifferent(a2.parrot); } }); var names = prefix.something.myExcellentMapFunction(
  20. verboselyNamedCollectionOfItems, function(item) { return item.name; }); 男子汉的缩进是可以突破天际的! 事实上, 除了数组和元素的初始化和传递匿名方法以外, 都应哦该相对于上一行表达式的左对齐

    缩进 4 个空格,而不是 2 个。 someWonderfulHtml = '' + getEvenMoreHtml(someReallyInterestingValues, moreValues, evenMoreParams, 'a duck', true, 72, slightlyMoreMonkeys(0xfff)) + ''; thisIsAVeryLongVariableName = hereIsAnEvenLongerOtherFunctionNameThatWillNotFitOnPrevLine(); thisIsAVeryLongVariableName = 'expressionPartOne' + someMethodThatIsLong() + thisIsAnEvenLongerOtherFunctionNameThatCannotBeIndentedMore(); someValue = this.foo( shortArg, 'Some really long string arg - this is a pretty common case, actually.', shorty2, this.bar()); if (searchableCollection(allYourStuff).contains(theStuffYouWant) && !ambientNotification.isActive() && (client.isAmbientSupported() || client.alwaysTryAmbientAnyways())) { ambientNotification.activate(); } 空行 用空行划分一组逻辑上相关的代码,例如: doSomethingTo(x); doSomethingElseTo(x); andThen(x); nowDoSomethingWith(y);
  21. andNowWith(z); 二元和三元操作符 操作符始终放在前行,这样就不用顾虑分号的隐式插入的问题,如果一行放不下,可以参考本节 函数参数的写法。 var x = a ? b

    : c; // 能放一行的都放在一行里 // 缩进 4 个空格木有问题 var y = a ? longButSimpleOperandB : longButSimpleOperandC; // 缩进到第一个判断变量位置木有问题 var z = a ? moreComplicatedB : moreComplicatedC; 括号 原文▽ 只在需要的时候使用。 括号的事情可大可小,没必要的时候不要用它。 对于一元操作符(如 delete, typeof 和 void)或是某些关键字(如 return, throw, case, new)后面永远不要使用括号。 字符串 原文▽ 单引号(')比双引号(")更好。 使用单引号(')比双引号(")更好,特别是当创建一个 HTML 代码的字符串时候: var msg = 'This is some HTML'; 可见性(私有和受保护的字段) 原文▽ 鼓励使用 JSDoc 中的 @private 和 @protected 进行标注。
  22. 我们推荐使用 JSDoc 的 @private 和 @protected 对类, 函数和属性进行可见性权限的标注。 有个写做 --jscomp_warning=visibility

    的编译器参数,编辑器会提示违反可见性相关的警告。具 体内容请查看 Closure Compiler Warnings. 标注为 @private 的全局变量和函数,只有其自身文件中的代码能进行访问和调用。 构造函数标注了 @private 则代表着只能在自身文件中使用它的公共静态方法或实例化,也可 以通过 instanceof 操作符进行访问。 永远不要为全局变量,函数和构造函数标记 @protected 。 // 文件 1。 // AA_PrivateClass_ 和 AA_init_ 可以相互访问,因为他们在同一个文件里。 /** * @private * @constructor */ AA_PrivateClass_ = function() { }; /** @private */ function AA_init_() { return new AA_PrivateClass_(); } AA_init_(); 标记 @private 的属性,自身文件内的代码可以访问,如果这个属性是某个类的,那此类的所 有静态方法和实例方法也是都可以访问的。 但来自不同文件的子类是无法访问或重载该属性的。 标记 @protected 的属性,自身文件内的代码可以访问,如果这个属性是属于某个类的,那此 类包括子类的所有静态方法和实例方法都可以访问。 注意,这些语义不同于 C++和 Java,它们允许在同一文件中访问私有和受保护的属性,而不只是 限制在同一个类或类继承中。而且,不向在 C++中,私有属性是不能被子类重载的。 // File 1. /** @constructor */ AA_PublicClass = function() { }; /** @private */
  23. AA_PublicClass.staticPrivateProp_ = 1; /** @private */ AA_PublicClass.prototype.privateProp_ = 2; /**

    @protected */ AA_PublicClass.staticProtectedProp = 31; /** @protected */ AA_PublicClass.prototype.protectedProp = 4; // File 2. /** * @return {number} The number of ducks we've arranged in a row. */ AA_PublicClass.prototype.method = function() { // Legal accesses of these two properties. return this.privateProp_ + AA_PublicClass.staticPrivateProp_; }; // File 3. /** * @constructor * @extends {AA_PublicClass} */ AA_SubClass = function() { // Legal access of a protected static property. AA_PublicClass.staticProtectedProp = this.method(); }; goog.inherits(AA_SubClass, AA_PublicClass); /** * @return {number} The number of ducks we've arranged in a row. */ AA_SubClass.prototype.method = function() { // Legal access of a protected instance property. return this.protectedProp; }; 再注意,在 JavaScript 中,子类(如 AA_PrivateClass_)和该类的原型类的可见性是相同的, 没有办法实现两个子类是公共的,而他们的构造函数却是私有(因为构造函数很容易别名)。
  24. JavaScript 类型 原文▽ 强烈建议使用编译器。 如果使用 JSDoc,那就尽量按照它的规范去写,目前支持 JS2 和 JS1.x 两种规范。

    (译者表示表格里的单词太多,要犯懒,想了解详细信息的可以查阅 JS2) JavaScript 类型语言 这个 JS2 提议为 JavaScript 类型定制了一种语言。当我们在 JsDoc 中注释函数参数和返回值类 型的时候会用到它。 虽然 JS2 提议在不断的进化,但编译器还是会继续支持老的类型语法,只是不建议使用而已。 Operator Name Syntax Description Deprecated Syntaxes Type Name {boolean}, {Wi ndow}, {goog.u i.Menu} Simply the name of a type. Type Application {Array.<string >} An array of strings. {Object.<strin g, number>} An object in which the keys are strings and the values are numbers. Patameterizes a type, by applying a set of type arguments to that type. The idea is analogous to generics in Java. Type Union {(number|boole an)} A number or a boolean. Indicates that a value might have type A OR type B. {(number,boolean)},{num ber|boolean},{(number|| boolean)} Record Type {{myNum: number, myObject}} An anonymous type with the given type members. Indicates that the value has the specified members with the specified types. In this case, myNum
  25. with a type number andmyObjec t with any type. Notice

    that the braces are part of the type syntax. For example, to denote an Array of objects that have a lengthpro perty, you might write Array .<{length} >. Nullable type {?number} A number or NULL. Indicates that a value is type A or null. By default, all object types are nullable. NOTE: Function types are not nullable. {number?} Non-nullable type {!Object} An Object, but never the null value. Indicates that a value is type A and not null. By default, all value types (boolean, number, string, and undefined) are not nullable. {Object!}
  26. Function Type {function(stri ng, boolean)} A function that takes two

    arguments (a string and a boolean), and has an unknown return value. Specifies a function. Function Return Type {function(): number} A function that takes no arguments and returns a number. Specifies a function return type. Function this Type {function(this :goog.ui.Menu, string)} A function that takes one argument (a string), and executes in the context of a goog.ui.Menu. Specifies the context type of a function type. Function new Type {function(new: goog.ui.Menu, string)} A constructor that takes one argument (a string), and creates a new instance of goog.ui.Menu when called with the 'new' keyword. Specifies the constructed type of a constructor. Variable arguments {function(stri ng, ...[number ]): number} A function that takes one argument (a string), and then a variable number of Specifies variable arguments to a function.
  27. arguments that must be numbers. Variable arguments (in @paraman notations)

    @param {...number} var_args A variable number of arguments to an annotated function. Specifies that the annotated function accepts a variable number of arguments. Function optio nal arguments {function(?str ing=, number=)} A function that takes one optional, nullable string and one optional number as arguments. The = syntax is only for function ty pe declarations. Specifies optional arguments to a function. Function optio nal arguments (in @param annot ations) @param {number=} opt_argument An optional parameter of type number. Specifies that the annotated function accepts an optional argument. The ALL type {*} Indicates that the variable can take on any type. The UNKNOWN type {?} Indicates that the variable can take on any type, and the compiler should not type-check
  28. any uses of it. JavaScript 的对象类型 类型示例 只示例 描述 number

    1 1.0 -5 1e5 Math.PI 数字,整 型,浮点 型,科学计 算型,数字 常量 Number new Number(true) Number 对 象 string 'Hello' "World" String(42) 字符串值 String new String('Hello') new String(42) String 对 象 boolean true false Boolean(0) 布尔值 Boolean new Boolean(true) Boolean 对象 RegExp new RegExp('hello') /world/g 正则表达 式 Date new Date new Date() 日期 null null undefined undefined void function f() { return; } 返回 undefined Array ['foo', 0.3, null] [] 混型数组 Array.<number> [11, 22, 33] 数字型数 组
  29. Array.<Array.<string> > [['one', 'two', 'three'], ['foo', 'bar']] 字符串型 数组嵌套 数组

    Object {} {foo: 'abc', bar: 123, baz: null} 对象表达 式 Object.<string> {'foo': 'bar'} 一个对象, 键和值都 是字符串。 Object.<number, string> var obj = {}; obj[1] = 'bar'; 一个对象, 键是数字, 值是字符 串。 注意,在 JavaScrip t 中,对象 的键总是 会被隐式 的转换成 字符串,所 以 obj['1'] == obj[1]。 所以键总 是以一个 字符串的 形式在 for...in 循环中,但 如果键在 索引时,编 译器会验 证类型。 Function function(x, y) { return x * y; } Function 对象 function(number, number): number function(x, y) { return x * y; } 函数值 SomeClass /** @constructor */ function SomeClass() {} 伪类 - 类 实例
  30. new SomeClass(); SomeInterface /** @interface */ function SomeInterface() {} SomeInterface.prototype.draw

    = function() {}; 原型类 project.MyClass /** @constructor */ project.MyClass = function () {} new project.MyClass() 构造内部 类 - 内部 类实例 project.MyEnum /** @enum {string} */ project.MyEnum = { BLUE: '#0000dd', RED: '#dd0000' }; 枚举 Element document.createElement('div') 在 DOM 中创 建一个元 素节点 Node document.body.firstChild 返回在 DOM 中的节点 body 的第 一个子节 点 HTMLInputElement htmlDocument.getElementsByTagName('input')[0 ] 查找一组 特定标签 类型的 DOM 元素,返回 第一个 明确类型 可能出现类型检查并不能准确判断表达式的类型的情况, 可以在注释里添加类型标注, 并在中括 号内写出表达式的类型,如果有对该类型的注解就更好了。 /** @type {number} */ (x) (/** @type {number} */ x) 可空 vs.可选的参数和属性 因为 JavaScript 是弱类型的语言,理解函数参数和类属性的可选,可空与未定义之间的区别还 是很重要的。
  31. 对象类型 (也称引用类型) 默认是可空的, 注意: 函数类型默认非空。 除了字符串, 数字, 布尔, undefined 或

    null 以外,对象可以是任何类型。例如: /** * Some class, initialized with a value. * @param {Object} value Some value. * @constructor */ function MyClass(value) { /** * Some value. * @type {Object} * @private */ this.myValue_ = value; } 上段代码是告诉编译器 myValue_ 属性为一个对象或 null。 如果 myValue_ 永远也不能是 null, 那就应该像下面一样声明: /** * 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 值是,就会发出警告。 函数的可选参数可能在运行的时候并没有被定义, 如果参数引用至类的属性上, 那则需要如下声 明: /** * Some class, initialized with an optional value. * @param {Object=} opt_value Some value (optional). * @constructor */
  32. function MyClass(opt_value) { /** * Some value. * @type {Object|undefined}

    * @private */ this.myValue_ = opt_value; } 以上代码是告诉编译器说 myValue_ 有可能是一个对象,null,还可能是 undefined。 注意:可选参数 opt_value 被声明为 {Object=},而并不是 {Object|undefined}。这是 因为可选属性可能是定义的或未定义的,虽然说明确写 undefined 也没关系,但读起来前边的更 爽。 最后,注意可空和可选都是正交属性,下面的四个声明都是不同的: /** * Takes four arguments, two of which are nullable, and two of which are * optional. * @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) { // ... }; 类型定义 类型定义也可以复杂化,一个函数可以接受元素节点的内容: /** * @param {string} tagName * @param {(string|Element|Text|Array.<Element>|Array.<Text>)} contents * @return {!Element} */ goog.createElement = function(tagName, contents) { ... }; 你可以定义 @typedef 标记的常用类型表达式,例如:
  33. /** @typedef {(string|Element|Text|Array.<Element>|Array.<Text>)} */ goog.ElementContent; /** * @param {string} tagName

    * @param {goog.ElementContent} contents * @return {!Element} */ goog.createElement = function(tagName, contents) { ... }; 模板属性 编译器已经有限的支持模板类型。它只能推断的类型,这在一个匿名函数字面量从类型的这个论 点,是否这个论点是缺失的。 The compiler has limited support for template types. It can only infer the type of this inside an anonymous function literal from the type of the this argument and whether the this argument is missing. /** * @param {function(this:T, ...)} fn * @param {T} thisObj * @param {...*} var_args * @template T */ goog.bind = function(fn, thisObj, var_args) { ... }; // Possibly generates a missing property warning. goog.bind(function() { this.someProperty; }, new SomeClass()); // Generates an undefined this warning. goog.bind(function() { this.someProperty; }); 注释 原文▽ 使用 JSDoc 我们鼓励依照 C++ style for comments 的风格。 所有的文件,类,方法和属性都应该以 JSDoc 风格来进行注释。 行内注释使用 // 。
  34. 避免出现句式片段,如果是英文首字母大写,记得加标点符号。 注释语法 JSDoc 的语法基于 JavaDoc 。 许多工具可以从 JSDoc 注释中提取元数据来执行代码的验证和 优化。当然,前提是这些注释都是符合语法规则的。

    /** * A JSDoc comment should begin with a slash and 2 asterisks. * Inline tags should be enclosed in braces like {@code this}. * @desc Block tags should always start on their own line. */ JSDoc 缩进 如果你不得不换行块标签,那就应该缩进四个空格以保持注释内容的结构清晰。 /** * Illustrates line wrapping for long param/return descriptions. * @param {string} foo This is a param with a description too long to fit in * one line. * @return {number} This returns something that has a description too long to * fit in one line. */ project.MyClass.prototype.method = function(foo) { return 5; }; 你不应该缩进 @fileoverview 命令。 尽管缩进至与上排注释同列并不怎么好,但也是可以接受的。 /** * This is NOT the preferred indentation method. * @param {string} foo This is a param with a description too long to fit in * one line. * @return {number} This returns something that has a description too long to * fit in one line. */ project.MyClass.prototype.method = function(foo) { return 5; }; JSDoc 的 HTML
  35. 就像 JavaDoc 一样, JSDoc 支持好多 HTML 标签, 如 <code>, <pre>,

    <tt>, <strong>, <ul>, <ol>, <li>, <a>等等。 昂所以纯文本状态并不会被格式化,比如换行和空格什么的都会被忽略掉: /** * Computes weight based on three factors: * items sent * items received * last timestamp */ 上面的结果其实是: Computes weight based on three factors: items sent items received items received 所以,可以用这种方式: /** * Computes weight based on three factors: * <ul> * <li>items sent * <li>items received * <li>last timestamp * </ul> */ 关于写注释更多信息可以去看一下 JavaDoc 风格指南。 顶部或文件头的注释 顶部注释是为了让读者能够很快的明了这个文件里的代码是干嘛的,描述应包括作者,年代,依 赖关系或兼容信息等相关,下面的例子: // Copyright 2009 Google Inc. All Rights Reserved. /** * @fileoverview Description of file, its uses and information * about its dependencies. * @author [email protected] (Firstname Lastname) */ 类的注释 对于类的注释,肯定要写功能和类型的描述,还有一些参数和原型描述等信息。
  36. /** * Class making something fun and easy. * @param

    {string} arg1 An argument that makes this more interesting. * @param {Array.<number>} arg2 List of numbers to be processed. * @constructor * @extends {goog.Disposable} */ project.MyClass = function(arg1, arg2) { // ... }; goog.inherits(project.MyClass, goog.Disposable); 方法和函数的注释 应该有参数和返回值的描述,方法描述要以使用者的身份去写。 /** * Operates on an instance of MyClass and returns something. * @param {project.MyClass} obj Instance of MyClass which leads to a long * comment that needs to be wrapped to two lines. * @return {boolean} Whether something occured. */ function PR_someMethod(obj) { // ... } 一些简单的 get 方法没有参数和其他影响的,可以忽略描述。 属性的描述 /** * Maximum number of things per pane. * @type {number} */ project.MyClass.prototype.someProperty = 4; JSDoc 标签参考 (译者注:看到解释有一大段,译者犯懒,有想看的请查阅 JSDoc Toolkit Tag Reference ) Tag Template & Examples Description @author @author [email protected] Document the author of a file or the owner of a test, generally only used
  37. (first last) For example: /** * @fileoverview Utilities for handling

    textareas. * @author [email protected] (Uthur Pendragon) */ in the @fileoverview comment. @code {@code ...} For example: /** * Moves to the next position in the selection. * Throws {@code goog.iter.StopIteration} when it * passes the end of the range. * @return {Node} The node at the next position. */ goog.dom.RangeIterator.proto type.next = function() { // ... }; Indicates that a term in a JSDoc description is code so it may be correctly formatted in generated documentation. @const @const For example: /** @const */ var MY_BEER = 'stout'; /** * My namespace's favorite kind of beer. * @const * @type {string} */ mynamespace.MY_BEER = 'stout'; Marks a variable as read-only and suitable for inlining. Generates warnings if it is rewritten. Constants should also be ALL_CAPS, but the annotation should help eliminate reliance on the naming convention. Although @final is listed at jsdoc.org and is supported as equivalent to @const in the compiler, it is discouraged. @const is consistent with JS1.5's const keyword. Note that changes to properties of const objects are not currently prohibited by the compiler (inconsistent with C++ const
  38. /** @const */ MyClass.MY_BEER = 'stout'; semantics). The type declaration

    can be omitted if it can be clearly inferred. If present, it must be on its own line. An additional comment about the variable is optional. @constructo r @constructor For example: /** * A rectangle. * @constructor */ function GM_Rect() { ... } Used in a class's documentation to indicate the constructor. @define @define {Type} description For example: /** @define {boolean} */ var TR_FLAGS_ENABLE_DEBUG = true; /** @define {boolean} */ goog.userAgent.ASSUME_IE = false; Indicates a constant that can be overridden by the compiler at compile-time. In the example, the compiler flag --define='goog.userAgent. ASSUME_IE=true' could be specified in the BUILD file to indicate that the constant goog.userAgent.ASSUME_ IE should be replaced with true. @deprecated @deprecated Description For example: /** * Determines whether a node is a field. * @return {boolean} True if the contents of * the element are editable, but the element * itself is not. * @deprecated Use isField(). */ BN_EditUtil.isTopEditableFie Used to tell that a function, method or property should not be used any more. Always provide instructions on what callers should use instead.
  39. ld = function(node) { // ... }; @enum @enum {Type}

    For example: /** * Enum for tri-state values. * @enum {number} */ project.TriState = { TRUE: 1, FALSE: -1, MAYBE: 0 }; @export @export For example: /** @export */ foo.MyPublicClass.prototype. myPublicMethod = function() { // ... }; Given the code on the left, when the compiler is run with the --generate_exports flag, it will generate the code: goog.exportSymbol('foo.MyPublicClas s.prototype.myPublicMethod', foo.MyPublicClass.prototype.myPubli cMethod); which will export the symbols to uncompiled code. Code that uses the@export annotation must either 1. include //javascript/clos ure/base.js, or 2. define both goog.exportSymbol an d goog.exportProperty wi th the same method signature in their own codebase. @extends @extends Type @extends {Type} Used with @constructor to indicate that a class inherits from another class. Curly braces around the type
  40. For example: /** * Immutable empty node list. * @constructor

    * @extends goog.ds.BasicNodeList */ goog.ds.EmptyNodeList = function() { ... }; are optional. @externs @externs For example: /** * @fileoverview This is an externs file. * @externs */ var document; Declares an externs file. @fileovervi ew @fileoverview Description For example: /** * @fileoverview Utilities for doing things that require this very long * but not indented comment. * @author [email protected] (Uthur Pendragon) */ Makes the comment block provide file level information. @implements @implements Type @implements {Type} For example: Used with @constructor to indicate that a class implements an interface. Curly braces around the type are optional.
  41. /** * A shape. * @interface */ function Shape() {};

    Shape.prototype.draw = function() {}; /** * @constructor * @implements {Shape} */ function Square() {}; Square.prototype.draw = function() { ... }; @inheritDoc @inheritDoc For example: /** @inheritDoc */ project.SubClass.prototype.t oString() { // ... }; Deprecated. Use @override instead. Indicates that a method or property of a subclass intentionally hides a method or property of the superclass, and has exactly the same documentation. Notice that @inheritDoc implies @override. @interface @interface For example: /** * A shape. * @interface */ function Shape() {}; Shape.prototype.draw = function() {}; /** * A polygon. * @interface * @extends {Shape} */ Used to indicate that the function defines an inteface.
  42. function Polygon() {}; Polygon.prototype.getSides = function() {}; @lends @lends objectName

    @lends {objectName} For example: goog.object.extend( Button.prototype, /** @lends {Button.prototype} */ { isButton: function() { return true; } }); Indicates that the keys of an object literal should be treated as properties of some other object. This annotation should only appear on object literals. Notice that the name in braces is not a type name like in other annotations. It's an object name. It names the object on which the properties are "lent". For example, @type {Foo} means "an instance of Foo", but @lends {Foo} means "the constructor Foo". The JSDoc Toolkit docs have more information on this annotation. @license or @preserve @license Description For example: /** * @preserve Copyright 2009 SomeThirdParty. * Here is the full license text and copyright * notice for this file. Note that the notice can span several * lines and is only terminated by the closing star and slash: */ Anything marked by @license or @preserve will be retained by the compiler and output at the top of the compiled code for that file. This annotation allows important notices (such as legal licenses or copyright text) to survive compilation unchanged. Line breaks are preserved. @noalias @noalias For example: /** @noalias */ function Range() {} Used in an externs file to indicate to the compiler that the variable or function should not be aliased as part of the alias externals pass of the compiler. @nosideeffe cts @nosideeffects This annotation can be used as part of function and constructor declarations to indicate that calls to the declared
  43. For example: /** @nosideeffects */ function noSideEffectsFn1() { // ...

    }; /** @nosideeffects */ var noSideEffectsFn2 = function() { // ... }; /** @nosideeffects */ a.prototype.noSideEffectsFn3 = function() { // ... }; function have no side-effects. This annotation allows the compiler to remove calls to these functions if the return value is not used. @override @override For example: /** * @return {string} Human-readable representation of project.SubClass. * @override */ project.SubClass.prototype.t oString() { // ... }; Indicates that a method or property of a subclass intentionally hides a method or property of the superclass. If no other documentation is included, the method or property also inherits documentation from its superclass. @param @param {Type} varname Description For example: /** * Queries a Baz for items. * @param {number} groupNum Subgroup id to query. * @param Used with method, function and constructor calls to document the arguments of a function. Type names must be enclosed in curly braces. If the type is omitted, the compiler will not type-check the parameter.
  44. {string|number|null} term An itemName, * or itemId, or null to

    search everything. */ goog.Baz.prototype.query = function(groupNum, term) { // ... }; @private @private For example: /** * Handlers that are listening to this logger. * @type Array.<Function> * @private */ this.handlers_ = []; Used in conjunction with a trailing underscore on the method or property name to indicate that the member is private. Trailing underscores may eventually be deprecated as tools are updated to enforce @private. @protected @protected For example: /** * Sets the component's root element to the given element. Considered * protected and final. * @param {Element} element Root element for the component. * @protected */ goog.ui.Component.prototype. setElementInternal = function(element) { // ... }; Used to indicate that the member or property is protected. Should be used in conjunction with names with no trailing underscore. @return @return {Type} Description Used with method and function calls to document the return type. When writing descriptions for boolean parameters,
  45. For example: /** * @return {string} The hex ID of

    the last item. */ goog.Baz.prototype.getLastId = function() { // ... return id; }; prefer "Whether the component is visible" to "True if the component is visible, false otherwise". If there is no return value, do not use an @return tag. Type names must be enclosed in curly braces. If the type is omitted, the compiler will not type-check the return value. @see @see Link For example: /** * Adds a single item, recklessly. * @see #addSafely * @see goog.Collect * @see goog.RecklessAdder#add ... Reference a lookup to another class function or method. @supported @supported Description For example: /** * @fileoverview Event Manager * Provides an abstracted interface to the * browsers' event systems. * @supported So far tested in IE6 and FF1.5 */ Used in a fileoverview to indicate what browsers are supported by the file. @suppress @suppress {warning1|warning2} For example: /** Suppresses warnings from tools. Warning categories are separated by |.
  46. * @suppress {deprecation} */ function f() { deprecatedVersionOfF(); } @template

    @template For example: /** * @param {function(this:T, ...)} fn * @param {T} thisObj * @param {...*} var_args * @template T */ goog.bind = function(fn, thisObj, var_args) { ... }; This annotation can be used to declare a template typename. @this @this Type @this {Type} For example: pinto.chat.RosterWidget.exte rn('getRosterElement', /** * Returns the roster widget element. * @this pinto.chat.RosterWidget * @return {Element} */ function() { return this.getWrappedComponent_(). getElement(); }); The type of the object in whose context a particular method is called. Required when the this keyword is referenced from a function that is not a prototype method. @type @type Type @type {Type} Identifies the type of a variable, property, or expression. Curly braces are not required around most types,
  47. For example: /** * The message hex ID. * @type

    {string} */ var hexId = hexId; but some projects mandate them for all types, for consistency. @typedef @typedef For example: /** @typedef {(string|number)} */ goog.NumberLike; /** @param {goog.NumberLike} x A number or a string. */ goog.readNumber = function(x) { ... } This annotation can be used to declare an alias of a more complex type. 推荐使用的有: @typedef @type @this @template @suppress @supported @see @return @protected @private @param @override @nosideeffects @noalias @lends @interface @inheritDoc @implements @fileoverview @externs @extends @export @enum @deprecated @define @constructor @const @code 你可能在第三方代码中看到其他的 JSDoc 标注。昂它们在 Google 代码中并不怎么鼓励使用, 但谁知道呢可能未来的某一天就突然的用起来了呢,先看看吧:  @augments  @argument  @borrows  @class  @constant  @constructs  @default  @event  @example  @field  @function
  48.  @ignore  @inner  @link  @memberOf  @name

     @namespace  @property  @public  @requires  @returns  @since  @static  @version 内部类和枚举 原文▽内部类和枚举的定义应和顶级类放在同一文件内。 内部类和枚举的定义都应和顶级类在同一文件内。goog.provide(google 库的方法,主要功 能是注册类和方法声明) 只需要声明顶级类。 编译压缩 原文▽推荐使用。 使用被编译压缩后的 JS 代码可以更快下载和使用,咳咳,工具的话 google 推荐 Closure Compiler。 提示和技巧 原文▽ JavaScript 习习更健康。 True 和 False 布尔表达式 以下的表达式都返回 false:  null  undefined  '' 空字符串  0 数字 0 小心咯,以下的都返回 true:
  49.  '0' 字符串 0  [] 空数组  {} 空对象

    你可能会写下面这段代码: while (x != null) { 其实还可以写的更短些(只要你也不希望 x 是 0、空字符串和 false): while (x) { 如果想检查字符串是否为 null 或空,你可能会这样写: if (y != null && y != '') { 当然可以写的更短些: if (y) { 注意: 还有很多非直观的布尔表达式,如下:  Boolean('0') == true '0' != true  0 != null 0 == [] 0 == false  Boolean(null) == false null != true null != false  Boolean(undefined) == false undefined != true undefined != false  Boolean([]) == true [] != true [] == false (经验证无错,译者表示困惑,难道这里是按照 length 去判 断的么)  Boolean({}) == true {} != true {} != false 条件(三元)操作符(?:) 下面这段代码可以被三元操作符所替换:
  50. if (val != 0) { return foo(); } else {

    return bar(); } 你可以写成: return val ? foo() : bar(); 在生成 HTML 的时候也很有用噢: var html = '<input type="checkbox"' + (isChecked ? ' checked' : '') + (isEnabled ? '' : ' disabled') + ' name="foo">'; && 和 || 这俩二元布尔操作符可以根据前面的代码判断后面的代码是否执行, 也就是说只有在必要的时候 才会执行后面的代码。 "||" 可以被称为默认操作符,因为它可以代替下面的情况: /** @param {*=} opt_win */ function foo(opt_win) { var win; if (opt_win) { win = opt_win; } else { win = window; } // ... } 其实你可以直接写: /** @param {*=} opt_win */ function foo(opt_win) { var win = opt_win || window; // ... }
  51. "&&" 也可以缩减代码量,比如: if (node) { if (node.kids) { if (node.kids[index])

    { foo(node.kids[index]); } } } 可以写成: if (node && node.kids && node.kids[index]) { foo(node.kids[index]); } 或者写成: var kid = node && node.kids && node.kids[index]; if (kid) { foo(kid); } 但如果这样的话就有点过了: node && node.kids && node.kids[index] && foo(node.kids[index]); 用 join()构建字符串 构建字符串通常都是这样的: function listHtml(items) { var html = '<div class="foo">'; for (var i = 0; i < items.length; ++i) { if (i > 0) { html += ', '; } html += itemHtml(items[i]); } html += '</div>'; return html; }
  52. 但上面这种方式在 IE 下面是很没效率的,更好的方式是: function listHtml(items) { var html = [];

    for (var i = 0; i < items.length; ++i) { html[i] = itemHtml(items[i]); } return '<div class="foo">' + html.join(', ') + '</div>'; } 你也可以用数组做字符串拼接,然后用 myArray.join('') 转换成字符串,但要注意给数组 赋值分配快于用 push() ,你最好使用赋值的方式。 遍历节点列表 节点列表是通过节点迭代器和一个过滤器来实现的,这表示它的一个属性例如 length 的时间复 杂度是 O(n),通过 length 来遍历整个列表则需要 O(n^2)。 var paragraphs = document.getElementsByTagName('p'); for (var i = 0; i < paragraphs.length; i++) { doSomething(paragraphs[i]); } 这样写会更好些: var paragraphs = document.getElementsByTagName('p'); for (var i = 0, paragraph; paragraph = paragraphs[i]; i++) { doSomething(paragraph); } 这种方式对所有集合和数组都适用,当然只要里面没有布尔值 false。 你也可以通过 firstChild 和 nextSibling 属性来遍历子节点。 var parentNode = document.getElementById('foo'); for (var child = parentNode.firstChild; child; child = child.nextSibling) { doSomething(child); } 吐槽部分 坚持一致原则。