Google JavaScript代码风格指南
修正版本 2.28
Aaron Whyte
Bob Jervis
Dan Pupius
Eric Arvidsson
Fritz Schneider
Robby Walker
每个风格点都有一个展开/收起按钮以便你可以得到更多的信息:▽. 你可以将全部展开或收起:
▽ 全部展开/全部收起
内容树
变量 常量 分号 嵌套函数 块内函数声明 异常处理 自定义异常 标准特性 基本包装类型 多级原型结构 方法定义 闭包 eval() with() {} this for-in 循环 关联数组多行字符串字面量 Array和Object字面量 修改内置对象的原型 IE下的条件注释
命名 自定义toString()方法 延迟初始化 明确作用域 代码格式化 括号 字符串 可见性(私有和受保护的字段) JavaScript 类型 注释 内部类和枚举 编译压缩 提示和技巧
背景
JavaScript是一门客户端脚本语言,Google经常用它来晒优越,本文档列出了一些在做JS项目时需要注意的地方。都是高富帅整理的,还望各位屌丝们有时间都好好看看多学学,别对这个世界太消极了!(译者注:所谓的客户端呢就是浏览器或本地软件环境,市面上所有的浏览器都支持JS,JS发展到现在统一WEB端再进军移动互联网后,相信在一段时间内都是不可被轻易替代的语言,所以有兴趣的同学们可以多关注一下)
JavaScript语言规范
原文▽
每个变量声明都要加上var关键字噢。
解释:如果你不指定关键字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解释器也会按隐式分隔的标准去执行,但那样调试、压缩、合并的时候都很不方便。不要那样做嘛你可以做的更好的不是么。
而且在某些情况下,不写分号可是很危险的:
// 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();
上段代码会发生什么事情?
为啥会这样呢?
JS语句以分号作为结束符使得JS解释器解析,如果省略分号,就会由JS解释器确定语句的结尾。尼玛,不是在很明确的情况下,它能确定么?上面几个例子里,都是在语句中声明了/函数/对象/数组,但闭括号("}"或"]")并不代表着结束。如果下一个语句开始是一个中缀或括号运算符,那JS就永远不会结束声明。
这真的是很奇怪的事情噢亲,而且真的不知道还会出现什么奇怪的事情噢亲,所以确保你的语句有分号结束吧。
译者注:加上分号也会在某些情况下增进代码的性能,因为这样解析器就不必再花时间推测应该在哪里插入分号了(取自《Javascript高级程序设计》)
嵌套函数
原文▽可以使用,木有问题。
嵌套函数非常有用,它可以重用而减少代码量,或防止一些辅助作用的方法暴露在外等优点,请随意把玩(叫破喉咙也不会有人来救它的)。
块内函数声明
原文▽呀咩嗲。
只有屌丝才这样做:
if (x) {
function foo() {}
}
虽然大多数JS引擎都支持块内声明函数,但它并不是 ECMAScript 标准的一部分(详见 ECMA-262, 第13条和第14条)。更糟糕的是各引擎对于块内函数声明的实现都不一样,和 EcmaScript 的建议相违背。 ECMAScript 只允许在根脚本语句或其他函数中进行函数声明,如果一定要用的话可以在块内用变量来定义函数:
if (x) {
var foo = function() {}
}
异常处理
原文▽支持合理使用。
不管谁写代码都不能百分百的说自己的程序木有异常的。所以,细心的检查代码是一方面,预先处理一些可能出现的异常也是有必要的(使用try-catch(e)),特别是正在做一些重要项目的时候(框架什么的)。
译者注:如果是预先就知道自己的代码会发生错误时,再使用try-catch语句就不合适了噢,而是应该预先对其进行检查,防止错误的出现(取自《JavaScript高级程序设计》)
原文▽支持合理使用。
没有自定义异常抛出的情况下,代码运行可能会报一些原始的错误信息,但包含的内容和错误描述会因浏览器而不同却都不是特别明确,不易维护,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 之间的类型转换很有帮助。
多级原型结构
原文▽
不是怎么推荐使用。
多级原型结构指的是 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 */ };
}
即便这个闭包函数内部并没有使用 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)
原文▽
只用于反序列化。(反序列化的意思是从字节流中重构对象,这里指的应该是JSON字符串重构成对象,或是执行服务器返回的JS语句)
eval() 很不稳定,会造成语义混乱,如果代码里还包含用户输入的话就更危险了,因为你无法确切得知用户会输入什么!难道就不能用更好更清晰更安全的方式去写你的代码么孩子?
然而 eval 很容易解析被序列化的对象,所以反序列化的任务还是可以交给它做的。(例如:解析RPC响应的时候)
反序列化的过程就是将字节流转换成内存中的数据结构。例如,你可能会将对象输出到文件中:
users = [
{
name: 'Eric',
id: 37824,
email: '[email protected]'
},
{
name: 'xtof',
id: 31337,
email: '[email protected]'
},
...
];
用 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 (foo) {
var x = 3;
return x;
}
回答: 啥事都可能发生。这个局部变量 x 可能会被 foo 对象里的属性所覆盖,它甚至可能会有setter,导致在赋值3时会执行许多其他的代码,所以太危险了,别用 with 。
原文▽
仅在构造函数,方法,闭包中去使用它。
this 语义很特别。它大多数情况下会指向全局对象,有的时候却是指向调用函数的作用域的(使用eval时),还可能会指向DOM树的某个节点(绑定事件时),新创建的对象(构造函数中),也可能是其他的一些什么乱七八糟的玩意(如果函数被 call() 或者被 apply() )。
很容易出错的,所以最好是以下这两种情况的时候再选择使用:
原文▽
只在 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;
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 字面量代替 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,
c: 2,
'strange key': 3
};
原文▽别这样做。
禁止修改 Object.prototype 和 Array.prototype 这样的内置对象。可能修改 Function.prototype 危险性会小些,但它同样会在调试中出现问题,所以尽量避免这样的情况吧。
原文▽别用。
不要这样做:
var f = function () {
/*@cc_on if (@_jscript) { return 2* @*/ 3; /*@ } @*/
};
条件注释会妨碍自动化工具的执行,因为他会在运行时改变 JavaScript 语义树。
JavaScript代码风格规范
原文▽
通常,使用类似于 functionNamesLikeThis, variableNamesLikeThis, ClassNamesLikeThis, EnumNamesLikeThis, methodNamesLikeThis, 和 SYMBOLIC_CONSTANTS_LIKE_THIS 这样的命名方式(驼峰式)
属性和方法
更多关于 private 和 protected 去看 visibility 这一部分吧。
方法和函数参数
可选参数以 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库都会给你一个高阶方法来让你声明命名空间,然后你就可以在这个命名空间下进行各种声明了。
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);
为增强可读性,将长名引用化为短别名
用局部别名引用完整的包名可以增强可读性。局部别名命名应和完整包名的最后一部分相匹配。
/**
* @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:
...
}
};
myapp.main = function() {
var MyClass = some.long.namespace.MyClass;
MyClass.staticHelper(null);
};
不要在全局作用域中创建别名引用,仅在函数块中去使用。
文件名
文件名应该全部字母小写,以避免在某些区分大小写的系统平台产生文件名混淆。文件名应该以 .js 结束,且不要包含 - 或 _ ( - 比 _ 更好些)。
原文▽
应该总是成功调用,无副作用。
你可以为自己的对象定义toString()方法。这是好事呀,但是你要确保你的实现方法满足:(1)总是成功的(2)木有副作用。如果你的方法不满足这两个条件,运行起来将会很危险。例如,如果 toString() 调用了包含 assert 的方法, assert 可能会试着输出失败的对象名,结果又调用了 toString()。
原文▽可以。
没必要总是在声明变量的时候就进行初始化,延迟初始化挺好的。
原文▽
始终需要明确。
任何时候都要明确作用域 - 提高可移植性和清晰度。例如,不要依赖于作用域链中的 window 对象。有的时候你可能会想让你的其他应用的函数中使用的 window 对象不是之前所指的窗口对象。
原文▽一两句话说不明白,展开了详细讨论
我们鼓励使用 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]>',
];
// 直接在方法中调用
goog.dom.createDom(goog.dom.TagName.DIV, {
id: 'foo',
className: 'some-css-class',
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) {
// ...
};
// 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(
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);
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
进行标注。
我们推荐使用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 */
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_)和该类的原型类的可见性是相同的,没有办法实现两个子类是公共的,而他们的构造函数却是私有(因为构造函数很容易别名)。
原文▽
强烈建议使用编译器。
如果使用JSDoc,那就尽量按照它的规范去写,目前支持 JS2 和 JS1.x 两种规范。
(译者表示表格里的单词太多,要犯懒,想了解详细信息的可以查阅JS2)
JavaScript类型语言
这个JS2提议为JavaScript类型定制了一种语言。当我们在JsDoc中注释函数参数和返回值类型的时候会用到它。
虽然JS2提议在不断的进化,但编译器还是会继续支持老的类型语法,只是不建议使用而已。
Operator Name
Syntax
Description
Deprecated Syntaxes
Type Name
{boolean}, {Window}, {goog.ui.Menu}
Simply the name of a type.
Type Application
{Array.<string>}
An array of strings.
{Object.<string, 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|boolean)}
A number or a boolean.
Indicates that a value might have type A OR type B.
{(number,boolean)},{number|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 with a type number and myObject with any type.
Notice that the braces are part of the type syntax. For example, to denote an Array of objects that have a length property, you might writeArray.<{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!}
Function Type
{function(string, 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(string, ...[number]): number}
A function that takes one argument (a string), and then a variable number of arguments that must be numbers.
Specifies variable arguments to a function.
Variable arguments (in@param annotations)
@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 optional arguments
{function(?string=, number=)}
A function that takes one optional, nullable string and one optional number as arguments. The = syntax is only for functiontype declarations.
Specifies optional arguments to a function.
Function optional arguments (in @paramannotations)
@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 any uses of it.
JavaScript的对象类型
类型示例
只示例
描述
number
1
1.0
-5
1e5
Math.PI
数字,整型,浮点型,科学计算型,数字常量
Number
new Number(true)
string
'Hello'
"World"
String(42)
字符串值
String
new String('Hello')
new String(42)
boolean
true
false
Boolean(0)
布尔值
Boolean
new Boolean(true)
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]
数字型数组
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';
一个对象,键是数字,值是字符串。
注意,在JavaScript中,对象的键总是会被隐式的转换成字符串,所以obj['1'] == obj[1]。所以键总是以一个字符串的形式在for...in循环中,但如果键在索引时,编译器会验证类型。
Function
function(x, y) {
return x * y;
}
function(number, number): number
function(x, y) {
return x * y;
}
函数值
/** @constructor */
function SomeClass() {}
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)
因为JavaScript是弱类型的语言,理解函数参数和类属性的可选,可空与未定义之间的区别还是很重要的。
对象类型(也称引用类型)默认是可空的,注意:函数类型默认非空。除了字符串,数字,布尔,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
*/
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 标记的常用类型表达式,例如:
/** @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 风格来进行注释。
行内注释使用 // 。
避免出现句式片段,如果是英文首字母大写,记得加标点符号。
注释语法
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
就像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)
*/
类的注释
对于类的注释,肯定要写功能和类型的描述,还有一些参数和原型描述等信息。
/**
* 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 [email protected] (first last)
For example:
/**
* @fileoverview Utilities for handling textareas.
* @author [email protected] (Uthur Pendragon)
*/
Document the author of a file or the owner of a test, generally only used in the @fileoverviewcomment.
{@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.prototype.next = function() {
// ...
};
Indicates that a term in a JSDoc description is code so it may be correctly formatted in generated documentation.
@const
For example:
/** @const */ var MY_BEER = 'stout';
/**
* My namespace's favorite kind of beer.
* @const
* @type {string}
*/
mynamespace.MY_BEER = 'stout';
/** @const */ MyClass.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 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.
@constructor
For example:
/**
* A rectangle.
* @constructor
*/
function GM_Rect() {
...
}
Used in a class's documentation to indicate the constructor.
@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 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.isTopEditableField = function(node) {
// ...
};
Used to tell that a function, method or property should not be used any more. Always provide instructions on what callers should use instead.
@enum {Type}
For example:
/**
* Enum for tri-state values.
* @enum {number}
*/
project.TriState = {
TRUE: 1,
FALSE: -1,
MAYBE: 0
};
@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.MyPublicClass.prototype.myPublicMethod',
foo.MyPublicClass.prototype.myPublicMethod);
which will export the symbols to uncompiled code. Code that uses the @export annotation must either
@extends Type
@extends {Type}
For example:
/**
* Immutable empty node list.
* @constructor
* @extends goog.ds.BasicNodeList
*/
goog.ds.EmptyNodeList = function() {
...
};
Used with @constructor to indicate that a class inherits from another class. Curly braces around the type are optional.
@externs
For example:
/**
* @fileoverview This is an externs file.
* @externs
*/
var document;
Declares an externs file.
@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 Type
@implements {Type}
For example:
/**
* A shape.
* @interface
*/
function Shape() {};
Shape.prototype.draw = function() {};
/**
* @constructor
* @implements {Shape}
*/
function Square() {};
Square.prototype.draw = function() {
...
};
Used with @constructor to indicate that a class implements an interface. Curly braces around the type are optional.
@inheritDoc
For example:
/** @inheritDoc */
project.SubClass.prototype.toString() {
// ...
};
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
For example:
/**
* A shape.
* @interface
*/
function Shape() {};
Shape.prototype.draw = function() {};
/**
* A polygon.
* @interface
* @extends {Shape}
*/
function Polygon() {};
Polygon.prototype.getSides = function() {};
Used to indicate that the function defines an inteface.
@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 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
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.
@nosideeffects
For example:
/** @nosideeffects */
function noSideEffectsFn1() {
// ...
};
/** @nosideeffects */
var noSideEffectsFn2 = function() {
// ...
};
/** @nosideeffects */
a.prototype.noSideEffectsFn3 = function() {
// ...
};
This annotation can be used as part of function and constructor declarations to indicate that calls to the declared function have no side-effects. This annotation allows the compiler to remove calls to these functions if the return value is not used.
@override
For example:
/**
* @return {string} Human-readable representation of project.SubClass.
* @override
*/
project.SubClass.prototype.toString() {
// ...
};
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 {Type} varname Description
For example:
/**
* Queries a Baz for items.
* @param {number} groupNum Subgroup id to query.
* @param {string|number|null} term An itemName,
* or itemId, or null to search everything.
*/
goog.Baz.prototype.query = function(groupNum, term) {
// ...
};
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.
@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
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 {Type} Description
For example:
/**
* @return {string} The hex ID of the last item.
*/
goog.Baz.prototype.getLastId = function() {
// ...
return id;
};
Used with method and function calls to document the return type. When writing descriptions for boolean parameters, 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 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 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 {warning1|warning2}
For example:
/**
* @suppress {deprecation}
*/
function f() {
deprecatedVersionOfF();
}
Suppresses warnings from tools. Warning categories are separated by |.
@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 Type
@this {Type}
For example:
pinto.chat.RosterWidget.extern('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 thiskeyword is referenced from a function that is not a prototype method.
@type Type
@type {Type}
For example:
/**
* The message hex ID.
* @type {string}
*/
var hexId = hexId;
Identifies the type of a variable, property, or expression. Curly braces are not required around most types, but some projects mandate them for all types, for consistency.
@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代码中并不怎么鼓励使用,但谁知道呢可能未来的某一天就突然的用起来了呢,先看看吧:
原文▽内部类和枚举的定义应和顶级类放在同一文件内。
内部类和枚举的定义都应和顶级类在同一文件内。goog.provide(google库的方法,主要功能是注册类和方法声明)
只需要声明顶级类。
原文▽推荐使用。
使用被编译压缩后的JS代码可以更快下载和使用,咳咳,工具的话google推荐 Closure Compiler。
原文▽
JavaScript 习习更健康。
True 和 False 布尔表达式
以下的表达式都返回false:
小心咯,以下的都返回true:
你可能会写下面这段代码:
while (x != null) {
其实还可以写的更短些(只要你也不希望x是0、空字符串和false):
while (x) {
如果想检查字符串是否为null或空,你可能会这样写:
if (y != null && y != '') {
当然可以写的更短些:
if (y) {
注意: 还有很多非直观的布尔表达式,如下:
条件(三元)操作符(?:)
下面这段代码可以被三元操作符所替换:
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;
// ...
}
"&&" 也可以缩减代码量,比如:
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;
}
但上面这种方式在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);
}