Javascript编程风格

作者: 阮一峰

日期: 2012年4月27日

Douglas Crockford是Javascript权威,Json格式就是他的发明。

去年11月他有一个演讲(Youtube),谈到了好的Javascript编程风格是什么。

我非常推荐这个演讲,它不仅有助于学习Javascript,而且能让你心情舒畅,因为Crockford讲得很幽默,时不时让听众会心一笑。

下面,我根据这个演讲和Crockford编写的代码规范,总结一下"Javascript编程风格"。

所谓"编程风格"(programming style),指的是编写代码的样式规则。不同的程序员,往往有不同的编程风格。

有人说,编译器的规范叫做"语法规则"(grammar),这是程序员必须遵守的;而编译器忽略的部分,就叫"编程风格"(programming style),这是程序员可以自由选择的。这种说法不完全正确,程序员固然可以自由选择编程风格,但是好的编程风格有助于写出质量更高、错误更少、更易于维护的程序。

所以,有一点应该明确,"编程风格"的选择不应该基于个人爱好、熟悉程度、打字工作量等因素,而要考虑如何尽量使代码清晰易读、减少出错。你选择的,不是你喜欢的风格,而是一种能够清晰表达你的意图的风格。这一点,对于Javascript这种语法自由度很高、设计不完全成熟的语言尤其重要。

一、大括号的位置

绝大多数的编程语言,都用大括号({})表示区块(block)。起首的大括号的位置,有许多不同的写法

最流行的有两种。一种是起首的大括号另起一行:

  block

  {

    ...

  }

另一种是起首的大括号跟在关键字的后面:

  block {

    ...

  }

一般来说,这两种写法都可以接受。但是,Javascript要使用后一种,因为Javascript会自动添加句末的分号,导致一些难以察觉的错误。

  return

  {

    key:value;

  };

上面的代码的原意,是要返回一个对象,但实际上返回的是undefined,因为Javascript自动在return语句后面添加了分号。为了避免这一类错误,需要写成下面这样:

  return {

    key : value;

  };

因此,

  规则1:表示区块起首的大括号,不要另起一行。

二、 圆括号

圆括号(parentheses)在Javascript中有两种作用,一种表示调用函数,另一种表示不同的值的组合(grouping)。我们可以用空格,区分这两种不同的括号。

  规则2:调用函数的时候,函数名与左括号之间没有空格。

  规则3:函数名与参数序列之间,没有空格。

  规则4:所有其他语法元素与左括号之间,都有一个空格。

按照上面的规则,下面的写法都是不规范的:

  foo (bar)

  return(a+b);

  if(a === 0) {...}

  function foo (b) {...}

  function(x) {...}

三、分号

分号表示语句的结束。大多数情况下,如果你省略了句尾的分号,Javascript会自动添加。

  var a = 1

等同于

  var a = 1;

因此,有人提倡省略句尾的分号。但麻烦的是,如果下一行的第一个字元(token)是下面这五个字符之一,Javascript将不对上一行句尾添加分号:"("、"["、"/"、"+"和"-"。

  x = y

  (function (){

    ...

  })();

上面的代码等同于

  x = y(function (){...})();

因此,

  规则5:不要省略句末的分号。

四、with语句

with可以减少代码的书写,但是会造成混淆。

  with (o) {

    foo = bar;

  }

上面的代码,可以有四种运行结果:

  o.foo = bar;

  o.foo = o.bar;

  foo = bar;

  foo = o.bar;

这四种结果都可能发生,取决于不同的变量是否有定义。因此,

  规则6:不要使用with语句。

五、相等和严格相等

Javascript有两个表示"相等"的运算符:"相等"(==)和"严格相等"(===)。

因为"相等"运算符会自动转换变量类型,造成很多意想不到的情况

  0 == ''// true

  1 == true // true

  2 == true // false

  0 == '0' // true

  false == 'false' // false

  false == '0' // true

  " \t\r\n " == 0 // true

因此,

  规则7:不要使用"相等"(==)运算符,只使用"严格相等"(===)运算符。

六、语句的合并

有些程序员追求简洁,喜欢合并不同目的的语句。比如,原来的语句是

  a = b;

  if (a) {...}

他喜欢写成下面这样:

  if (a = b) {...}

虽然语句少了一行,但是可读性大打折扣,而且会造成误读,让别人误以为这行代码的意思是:

  if (a === b){...}

另外一种情况是,有些程序员喜欢在同一行中赋值多个变量:

  var a = b = 0;

他以为,这行代码等同于

  var a = 0, b = 0;

实际上不是,它的真正效果是下面这样:

  b = 0;

  var a = b;

因此,

  规则8:不要将不同目的的语句,合并成一行。

七、变量声明

Javascript会自动将变量声明"提升"(hoist)到代码块(block)的头部。

  if (!o) {

    var o = {};

  }

等同于

  var o;

  if (!o) {

    o = {};

  }

为了避免可能出现的问题,不如把变量声明都放在代码块的头部。

  for (var i ...) {...}

最好写成:

  var i;

  for (i ...) {...,}

因此,

  规则9:所有变量声明都放在函数的头部。

  规则10:所有函数都在使用之前定义。

八、全局变量

Javascript最大的语法缺点,可能就是全局变量对于任何一个代码块,都是可读可写。这对代码的模块化和重复使用,非常不利。

  规则11:避免使用全局变量;如果不得不使用,用大写字母表示变量名,比如UPPER_CASE。

九、new命令

Javascript使用new命令,从建构函数生成一个新对象。

  var o = new myObject();

这种做法的问题是,一旦你忘了加上new,myObject()内部的this关键字就会指向全局对象,导致所有绑定在this上面的变量,都变成全部变量。

  规则12:不要使用new命令,改用Object.create()命令。

如果不得不使用new,为了防止出错,最好在视觉上把建构函数与其他函数区分开来。

  规则13:建构函数的函数名,采用首字母大写(InitialCap);其他函数名,一律首字母小写。

十、自增和自减运算符

自增(++)和自减(--)运算符,放在变量的前面或后面,返回的值不一样,很容易发生错误。

事实上,所有的++运算符都可以用"+= 1"代替。

  ++x

等同于

  x += 1;

代码变得更清晰了。有一个很可笑的例子,某个Javascript函数库的源代码中出现了下面的片段:

  ++x;

  ++x;

这个程序员忘了,还有更简单、更合理的写法:

  x += 2;

因此,

  规则14:不要使用自增(++)和自减(--)运算符,用+=和-=代替。

十一、区块

如果循环和判断的代码体只有一行,Javascript允许该区块(block)省略大括号。

下面的代码

  if (a) b(); c();

原意可能是

  if (a) { b(); c();}

但是,实际效果是

  if (a) { b();} c();

因此,

  规则15:总是使用大括号表示区块。

(完)

留言(50条)

请推荐一两本优秀中文js的入门教材,谢谢!

我始終認為 JS 在每一行末尾自動添加分號是很不好的設計。

赞一个 ... 平时的小问题多注意一些, 肯定会给将来写出来框架级的js代码会减少不少错误和麻烦 !

严格相等通常翻译:恒等;
建构函数通常翻译:构造函数;

有些过于吹毛求疵了。

最重要只要注意两点:
1. JS前加 "use strict"; 使新浏览器进入严格模式,保证不小心污染根对象的情形不会发生 (chrome里会抛出异常)。
2. 使用 JSLint 确保代码基本正常

已经能cover大部分问题点了。

@RedNax:

认同,有些编程规范要遵守,有些还是自由发挥吧,重要的还是在于编程所创造的东西,编码清晰易读就好

不是应该写作JavaScript而不是Javascript吗?

引用yale的发言:

请推荐一两本优秀中文js的入门教材,谢谢!

告诉你个少走弯路的经验:不要看中国人写的书,极个别除外。推荐阅读这篇文章javascript的那些书

第九条中提到了“构建函数”,我没有使用过Javascript,不知道是不是应该是“构造函数”?

JS用的少,看来代码块起始花括号换行的习惯要改了。

引用yale的发言:

请推荐一两本优秀中文js的入门教材,谢谢!

老道说他那本《优良的部分》算一本

因为Javascript会自动添加句末的分号,导致一些难以察觉的错误——说明阮一峰用的编辑软件不好

引用yale的发言:

请推荐一两本优秀中文js的入门教材,谢谢!

原文地址 http://limu.iteye.com/blog/1267475

大括号写在下面才看着清楚,你那种写法我不是很赞同.省那点地方带来的是较差的可读性.

推荐阮哥了解一下CoffeeScript,在您有时间的时候

Douglas Crockford在他的Javascript:the good parts一书中,也提到了这些意见。

引用xulboy的发言:

大括号写在下面才看着清楚,你那种写法我不是很赞同.省那点地方带来的是较差的可读性.

不是可读性的问题了,左大括号单独一行在Javascript里面有可能造成错误!
(不过这是Javascript的设计问题)

引用Pob的发言:

推荐阮哥了解一下CoffeeScript,在您有时间的时候

我也是 CoffeeScript 的爱好者.
JavaScript 社区普遍承认并接受这门语言天生的语法缺陷, 但是他们往往不能接受对 JavaScript 语法层面的包装, 理由是导致源代码分裂, 使重用发生困难, 造成彼此之间的隔离. Express 的作者就极度不接受 CoffeeScript, 连他写的测试框架都十分不情愿支持 CoffeeScript.
所以 CoffeeScript 成了两难的选择, 目前公开的项目除了 CoffeeScript 周边的项目之外, 很少采用这门语言的.

阮兄涉足领域之广啊!

jQuery作者Resig在他的书中专门写了with语句,不知道该听谁的呢?

使用 create 来代替 new 并不是最好的办法。jQuery 作者 John Resig 的做法更 fool-proof:如果一个函数是 constructor,在使用者忘记加 new 时应该保证跟使用了 new 的效果一致。现在很多 library 在学习 jQuery API 时也学习了这一点。

什么叫不规范?那个函数名空格的地方全部都不会影响编译器,也没有同一标准,只能说这人认为这样容易读一点。

说实话,这篇文章阮先生的翻译让我失望了。。

引用nobody的发言:

因为Javascript会自动添加句末的分号,导致一些难以察觉的错误——说明阮一峰用的编辑软件不好

好像这个意思是说,JavaScript 被解释执行时会被加上分号;而不是指发生在开发编辑的阶段。

这篇文章中提到的关于 with\new\++ 的原则,疑似因噎废食啊

前面的完全是认同,而后面说++和--运算符不要用这个我表示完全不能理解。
比如一个初学程序的例子,sum([1:100]),如果不按照这篇文章的规范,可能是这样:


var i, s = 0;
for (i = 1; i

甚至是

for (i = 1, s = 0; i

然后这篇文章告诉我们,要写成这样

var i, s;
s = 0;
for (i = 1; i s += i;
}

借用《短码之美》作者一句话,不要责怪这些代码可读性太差,而是应该去学习学习如何读懂代码。

很多条规则都太保守了,Javascript语法本来很灵活,如果都按照这些规则来,出错率和可读性的确会提升,但写代码会变得很无趣

绝大部分规则都是合理的。但规则5除外。因为你总有可能漏写分号,而JS的设计导致漏写的分号难以被察觉(因为你不知道是有意漏写还是无意漏写)。所以我赞同的方式是始终不写分号,并配合合理的断行格式。只在(、[等为起始的代码行的行首加分号。参见:http://hax.iteye.com/blog/382186

请问 函数名与参数序列之间,没有空格。是什么意思?

挺好的,javascript的鸡肋都是滥用出来的,作者讲的是精华部分,同时javascript不等同于其他语言,他依靠网络,需要压缩,没有分号js引擎怎么解析他

其实大多数规则都没有必要,例如第一条
  return
  {
    key:value;
  };

  return {
    key:value;
  };

为什么大家不能老老实实的写代码呢?
var ret =
  {
    key:value;
  };
return ret;

说的很有道理,而且解决以前的疑惑:局部变量可以在外部引用!原来被提到外面了。。。

用=== 严格等于这条很受用

我觉得这些都是有必要的,养成良好的编写规范是件好事。像我以前就找了return(换行)的道了,结果自己还没发现,都是细小的问题。坚决支持!

对于最后一条是无论如何不能接受的,我的 CS 要求能不用大括号的地方一律不准用大括号。
而且我就是喜欢写 if ((a = b)) {...} // 如果是C语言,用 gcc -Wall 编译,会要求在 a=b 外面加括号。这样倒不容易看错

我很幸运在没犯这些错误时了解了这些特性,对策多样的,只要牢记这些隐晦的特性即可~

呵呵,看了回复才发现大家其实都在讲英文,只不过是汉化了的英文。“抛出异常”太别扭了。

引用chenge的发言:

jQuery作者Resig在他的书中专门写了with语句,不知道该听谁的呢?

还是不用为好,因为你不能保证在你的代码域内,没有任何一个全局对象和你with的对象重名。就算你现在能保证,那以后万一有人改写程序咋办?

很好的文章,其实遵守c/c++的语法,JS的代码会很优秀。

赞一个,都是很容易折磨人的错误。

有几条之前都没有注意到,感谢提醒~

庸俗的想法。估计是个低俗的程序员

很有python的风格~

我觉得javascript最好的大全还是Javascript: the definitive Guide 6th Edition.
http://shop.oreilly.com/product/9780596805531.do

如果仅仅偏重于语言本身,Javascript Garden是一篇非常好的总结。可以说,完全掌握Garden里的每一点,语言本身就过关了。
http://bonsaiden.github.com/JavaScript-Garden/

规则9:所有变量声明都放在函数的头部。
请问一下,这个指的是变量声明写在函数内的前部分,函数中代码块的上方,是这个意思么?不是写在函数外面吧?

过了一遍,很有收获,写的很好,也把留言看了下,也有收获,谢谢

笔误

"这种做法的问题是,一旦你忘了加上new,myObject()内部的this关键字就会指向全局对象,导致所有绑定在this上面的变量,都变成全部变量。"

改成“都变成全局变量”

"函数名与参数序列之间,没有空格。" 这句看不懂,函数名与参数序列之间,难道不是左括号吗?

“规则5:不要省略句末的分号。” 这个问题是很有争议的,我肯定倾向于省略分号。
http://www.zhihu.com/question/20298345

new/++/--
这几个属于好多语言都有的最基础的语法部分,正常使用应该不会有什么问题。++/-- 在计算表达式(例如: y= x+z++;)中可能会产生歧义,这时候可以使用空格和括号来增加可读性(y = x + (z++);)

基本跟我平时写代码的风格一模一样,用了TSlint,不按规则写会报错,而我又有强迫症.....

我要发表看法

«-必填

«-必填,不公开

«-我信任你,不会填写广告链接