Airbnb 规范大全

想要Xmind版的请关注私聊我,写的很全了。 前端的基本都概括了。


命名编码规范

驼峰式命名法介绍

·      Pascal Case 大驼峰式命名法:首字母大写。eg:StudentInfo、UserInfo、ProductInfo

·      Camel Case 小驼峰式命名法:首字母小写。eg:studentInfo、userInfo、productInfo

文件资源命名

·      文件名不得含有空格

·      文件名建议只使用小写字母,不使用大写字母。( 为了醒目,某些说明文件的文件名,可以使用大写字母,比如README、LICENSE。 )

·      文件名包含多个单词时,单词之间建议使用半角的连词线 ( – ) 分隔。

·      引入资源使用相对路径,不要指定资源所带的具体协议 ( http:,https: ) ,除非这两者协议都不可用。

不推荐:

<scriptsrc="http://cdn.com/foundation.min.js"></script>

推荐

<scriptsrc="//cdn.com/foundation.min.js"></script>

变量命名

命名方式 : 小驼峰式命名方法
命名规范 : 类型+对象描述的方式,如果没有明确的类型,就可以使前缀为名词

类型

小写字母

array

a

boolean

b

function

fn

int

i

object

o

regular

r

string

s

推荐

1. var tableTitle ="LoginTable"

不推荐

1. var getTitle ="LoginTable"

函数

命名方式 : 小驼峰方式 ( 构造函数使用大驼峰命名法 )
命名规则 : 前缀为动词

动词

含义

返回值

can

判断是否可执行某个动作 ( 权限 )

函数返回一个布尔值。true:可执行;false:不可执行

has

判断是否含有某个值

函数返回一个布尔值。true:含有此值;false:不含有此值

is

判断是否为某个值

函数返回一个布尔值。true:为某个值;false:不为某个值

get

获取某个值

函数返回一个非布尔值

set

设置某个值

无返回值、返回是否设置成功或者返回链式对象

推荐:

1. //是否可阅读

2. function canRead(){

3.   returntrue;

4. }

5.  

6. //获取姓名

7. function getName{

8.   returnthis.name

9. }

常量

命名方法 : 全部大写
命名规范 : 使用大写字母和下划线来组合命名,下划线用以分割单词。
推荐:

1.  var MAX_COUNT =10;

2.  var URL ='http://www.baidu.com';

类的成员

·      公共属性和方法 : 同变量命名方式

·      私有属性和方法 : 前缀为下划线(_)后面跟公共属性和方法一样的命名方式

 

推荐(name换成this是不是更熟悉了呢)

 

 

 

1. functionStudent(name){

2.     var _name = name;// 私有成员

3.     // 公共方法

4.     this.getName =function(){

5.         return _name;

6.     }

7.     // 公共方式

8.     this.setName =function(value){

9.         _name = value;

10.      }

11.  }

12.  var st =newStudent('tom');

13.  st.setName('jerry');

14.  console.log(st.getName());// => jerry:输出_name私有变量的值

注释规范

单行注释 ( // )

·      单独一行://(双斜线)与注释文字之间保留一个空格

·      在代码后面添加注释://(双斜线)与代码之间保留一个空格,并且//(双斜线)与注释文字之间保留一个空格。

·      注释代码://(双斜线)与代码之间保留一个空格。

 

推荐 :

1. // 调用了一个函数;1)单独在一行

2. setTitle();

3.  

4. var maxCount =10;// 设置最大量;2)在代码后面注释

5.  

6. // setName(); // 3)注释代码

多行注释 ( / 注释说明 / )

·      若开始(/*和结束(*/)都在一行,推荐采用单行注释

·      若至少三行注释时,第一行为/*,最后行为*/,其他行以*开始,并且注释文字与*保留一个空格。

 

推荐 :

1. /*

2. * 代码执行到这里后会调用setTitle()函数

3. * setTitle():设置title的值

4. */

5. setTitle();

函数 ( 方法 ) 注释

函数(方法)注释也是多行注释的一种,但是包含了特殊的注释要求,参照 javadoc(百度百科)

语法:

1. /**

2. * 函数说明

3. * @关键字

4. */

常用注释关键字

注释名

语法

含义

示例

@param

@param 参数名 {参数类型} 描述信息

描述参数的信息

@param name {String} 传入名称

@return

@return {返回类型} 描述信息

描述返回值的信息

@return {Boolean} true:可执行;false:不可执行

@author

@author 作者信息 [附属信息:如邮箱、日期]

描述此函数作者的信息

@author 张三 2015/07/21

@version

@version XX.XX.XX

描述此函数的版本号

@version 1.0.3

@example

@example 示例代码

@example setTitle(‘测试’)

如下

推荐 :

1. /**

2.  - 合并Grid的行

3.  - @param grid {Ext.Grid.Panel} 需要合并的Grid

4.  - @param cols {Array} 需要合并列的Index(序号)数组;从0开始计数,序号也包含。

5.  - @param isAllSome {Boolean} :是否2个tr的cols必须完成一样才能进行合并。true:完成一样;false(默认):不完全一样

6.  - @return void

7.  - @author polk6 2015/07/21

8.  - @example

9. */

10.  function mergeCells(grid, cols, isAllSome){

11.      // Do Something

12.  }

13.   

HTML规范

文档规范

使用HTML5 的文档声明类型 : <!DOCTYPE html>

·      DOCTYPE标签是一种标准通用标记语言的文档类型声明,它的目的是要告诉标准通用标记语言解析器,它应该使用什么样的文档类型定义(DTD)来解析文档。

·      使用文档声明类型的作用是为了防止开启浏览器的怪异模式。

·      没有DOCTYPE文档类型声明会开启浏览器的怪异模式,浏览器会按照自己的解析方式渲染页面,在不同的浏览器下面会有不同的样式。

·      如果你的页面添加了<!DOCTYP>那么,那么就等同于开启了标准模式。浏览器会按照W3C标准解析渲染页面。

脚本加载

说到js和css的位置,大家应该都知道js放在下面,css放在上面。
但是,如果你的项目只需要兼容ie10+或者只是在移动端访问,那么可以使用HTML5的新属性async,将脚本文件放在<head>
兼容老旧浏览器(IE9-)时
脚本引用写在 body 结束标签之前,并带上 async 属性。这虽然在老旧浏览器中不会异步加载脚本,但它只阻塞了 body 结束标签之前的 DOM 解析,这就大大降低了其阻塞影响。
而在现代浏览器中
脚本将在 DOM 解析器发现 body 尾部的 script 标签才进行加载,此时加载属于异步加载,不会阻塞 CSSOM(但其执行仍发生在 CSSOM 之后)。
综上所述,
所有浏览器中推荐:

1. <html>

2.   <head>

3.     <linkrel="stylesheet"href="main.css">

4.   </head>

5.   <body>

6.     <!-- body goes here-->

7.     <scriptsrc="main.js"async></script>

8.   </body>

9. </html>

只兼容现代浏览器推荐:

1. <html>

2.   <head>

3.     <linkrel="stylesheet"href="main.css">

4.     <scriptsrc="main.js"async></script>

5.   </head>

6.   <body>

7.     <!-- body goes here-->

8.   </body>

9. </html>

语义化

我们一直都在说语义化编程,语义化编程,但是在代码中很少有人完全使用正确的元素。使用语义化标签也是有理由SEO的。

语义化是指:根据元素其被创造出来时的初始意义来使用它。
意思就是用正确的标签干正确的事,而不是只有divspan

不推荐:

1. <b>My page title</b>

2. <divclass="top-navigation">

3.   <divclass="nav-item"><ahref="#home">Home</a></div>

4.   <divclass="nav-item"><ahref="#news">News</a></div>

5.   <divclass="nav-item"><ahref="#about">Abo</a></div>

6. </div>

7.  

8. <divclass="news-page">

9.   <divclass="page-sectionnews">

10.      <divclass="title">All news articles</div>

11.      <divclass="news-article">

12.        <h2>Bad article</h2>

13.        <divclass="intro">Introduction </div>

14.        <divclass="content">This is very bad</div>

15.        <divclass="article-side-notes">the main credits</div>

16.        <divclass="article-foot-notes">

17.          This article wascreated by David

18.              <divclass="time">2014-01-01</div>

19.        </div>

20.      </div>

21.   

22.      <divclass="section-footer">

23.        Related sections: Events,Public holidays

24.      </div>

25.    </div>

26.  </div>

27.   

28.  <divclass="page-footer">

29.    Copyright 2014

30.  </div>

推荐

 

1. <header>

2.   <h1>My page title</h1>

3. </header>

4. <navclass="top-navigation">

5.   <ul>

6.     <liclass="nav-item"><ahref="#home">Ho</a></li>

7.     <liclass="nav-item"><ahref="#news">Ne</a></li>

8.     <liclass="nav-item"><ahref="#about">A</a></li>

9.   </ul>

10.  </nav>

11.  <mainclass="news-page"role="main">

12.    <sectionclass="page-sectionnews">

13.      <header>

14.        <h2class="title">All news articles</h2>

15.      </header>

16.      <articleclass="news-article">

17.        <header>

18.          <divclass="article-title">Good le</div>

19.          <smallclass="intro">Introduction</small>

20.        </header>

21.        <divclass="content">

22.          <p>This is a good examplesemantics</p>

23.        </div>

24.        <asideclass="article-side-notes">

25.          <p>I think main credits</p>

26.        </aside>

27.        <footerclass="article-foot-notes">

28.          <p>This article was<timedatetime="2014-01-01"class="time">1 month ago</time></p>

29.        </footer>

30.      </article>

31.      <footerclass="section-footer">

32.        <p>Related sections: holidays</p>

33.      </footer>

34.    </section>

35.  </main>

36.  <footerclass="page-footer">

37.    Copyright 2014

38.  </footer>

alt标签不为空

<img>标签的alt 属性指定了替代文本,用于在图像无法显示或者用户禁用图像显示时,代替图像显示在浏览器中的内容。
假设由于下列原因用户无法查看图像,alt 属性可以为图像提供替代的信息:

·      网速太慢

·      src 属性中的错误

·      浏览器禁用图像

·      用户使用的是屏幕阅读器

 

从SEO角度考虑,浏览器的爬虫爬不到图片的内容,所以我们要有文字告诉爬虫图片的内容

结构、表现、行为三者分离

尽量在文档和模板中只包含结构性的 HTML;而将所有表现代码,移入样式表中;将所有动作行为,移入脚本之中。
在此之外,为使得它们之间的联系尽可能的小,在文档和模板中也尽量少地引入样式和脚本文件。
建议:

·      不使用超过一到两张样式表

·      不使用超过一到两个脚本(学会用合并脚本)

·      不使用行内样式(<style>.no-good{}</style>

·      不在元素上使用 style 属性(<hr style="border-top: 5px solid black">

·      不使用行内脚本(<script>alert('nogood')</script>

·      不使用表象元素(i.e. <b>,<u>, <center>, <font>, <b>

·      不使用表象 class 名(i.e. red, left, center

HTML只关注内容

·      HTML只显示展示内容信息

·      不要引入一些特定的 HTML 结构来解决一些视觉设计问题

·      不要将img元素当做专门用来做视觉设计的元素

·      样式上的问题应该使用css解决

 

不推荐:

1. <spanclass="text-box">

2.   <spanclass="square"></span>

3.   See the square next to me?

4. </span>

5. css 代码:

6. .text-box > .square {

7.   display: inline-block;

8.   width: 1rem;

9.   height: 1rem;

10.    background-color: red;

11.  }

12.   

推荐

1. <spanclass="text-box">

2.   See the square next to me?

3. </span>

4. css 代码:

5. .text-box:before {

6.   content: "";

7.   display: inline-block;

8.   width: 1rem;

9.   height:1rem;

10.    background-color:red;

11.  }

图片和 SVG 图形能被引入到 HTML 中的唯一理由是它们呈现出了与内容相关的一些信息。

不推荐

1. <spanclass="text-box">

2.   <imgsrc="square.svg"alt="Square"/>

3.   See the square next to me?

4. </span>

 

 

推荐

1. <spanclass="text-box">

2.   See the square next to me?

3. </span>

4.css 代码:

5. .text-box:before {

6.   content: "";

7.   display: inline-block;

8.   width: 1rem;

9.   height: 1rem;

10.    background: url(square.svg) no-repeat;

11.    background-size: 100%;

12.  }

js规范

避免全局命名空间污染

防止全局命名空间被污染,我们通常的做法是将代码包裹成一个 IIFE(Immediately-InvokedFunction Expression),创建独立隔绝的定义域。立即调用的函数表达式,声明函数的同时立即调用这个函数。(return为闭包)

IIFE还可确保你的代码不会轻易被其它全局命名空间里的代码所修改(i.e.第三方库,window 引用,被覆盖的未定义的关键字等等)。
不推荐:

1. var x =10,

2.    y =100;

3. console.log(window.x +' '+ window.y);

推荐

1.  (function(log, w,undefined){

2.   'use strict';

3.   var x =10,

4.      y =100;

5.   // Will output 'truetrue'

6.   log((w.x ===undefined)+' '+(w.y ===undefined));

7. }(window.console.log, window));

推荐的IIFE写法:

1.  (function(){

2.   'use strict';

3.   // Code goes here

4. }());

如果你想引用全局变量或者是外层 IIFE 的变量,可以通过下列方式传参:

1.  (function($, w, d){

2.   'use strict';

3.  

4.   $(function(){

5.     w.alert(d.querySelectorAll('div').length);

6.   });

7. }(jQuery, window, document));

严格模式

ECMAScript 5 严格模式可在整个脚本或独个方法内被激活。它对应不同的 javascript 语境会做更加严格的错误检查。严格模式也确保了 javascript 代码更加的健壮,运行的也更加快速。

严格模式会阻止使用在未来很可能被引入的预留关键字。

你应该在你的脚本中启用严格模式,最好是在独立的 IIFE 中应用它。避免在你的脚本第一行使用它而导致你的所有脚本都启动了严格模式,这有可能会引发一些第三方类库的问题。

变量声明

总是使用 var 来声明变量。如不指定 var,变量将被隐式地声明为全局变量,例如

1. var a = b =0;//b会被隐式的创建为全局变量

所以,请总是使用 var 来声明变量,并且使用单var模式(将所有的变量在函数最前面只使用一个var定义)。例如:

1.  (function(){

2.   'use strict'

3.   var a =0,

4.      b =0,

5.      c =0,

6.      i,

7.      j,

8.      myObject();

9. }())

采用严格模式带来的好处是,当你手误输入错误的变量名时,它可以通过报错信息来帮助你定位错误出处

js声明提前

javascript会自动将函数作用域内的变量和方法的定义提前(只是提前声明,赋值还是在原处)
例如:

 

1.  (function(log){

2.   'use strict';

3.   var a =10;

4.   for(var i =0; i < a; i++){

5.     var b = i * i;

6.     log(b);

7.   }

8.   if(a ===10){

9.     var f =function(){

10.        log(a);

11.      };

12.      f();

13.    }

14.    function x(){

15.      log('Mr. X!');

16.    }

17.    x();

18.   

19.  }(window.console.log));

 

提升后的js

1.  (function(log){

2.   'use strict';

3.   var a,

4.      i,

5.      b,

6.      f;

7.   function x(){

8.     log('Mr. X!');

9.   }

10.   a =10;

11.   for(i =0; i < a; i++){

12.      b = i * i;

13.      log(b);

14.   }

15.   if(a ===10){

16.      f =function(){

17.        log(a);

18.      };

19.      f();

20.    }

21.    x();

22.  }(window.console.log));

使用严格等

总是使用 === 精确的比较操作符,避免在判断的过程中,由 JavaScript 的强制类型转换所造成的困扰。例如:

1.  (function(log){

2.   'use strict';

3.  

4.   log('0'==0);// true

5.   log(''==false);// true

6.   log('1'==true);// true

7.   log(null==undefined);// true

8.  

9.   var x ={

10.      valueOf:function(){

11.        return'X';

12.      }

13.    };

14.   

15.    log(x =='X');

16.   

17.  }(window.console.log));

等同==和严格等===的区别

·      ==, 两边值类型不同的时候,要先进行类型转换,再比较。

·      ===,不做类型转换,类型不同的一定不等。如果两个值具有相同类型,会进行===比较,返回===的比较值

·      如果两个值不具有相同类型,也有可能返回true

·      如果一个值是null另一个值是undefined,返回true

·      如果一个值是string另个是number,会把string转换成number再进行比较

·      如果一个值是true,会把它转成1再比较,false会转成0

1. console.log(false==null)      // false

2. console.log(false==undefined)// false

3. console.log(false==0)        // true

4. console.log(false=='')       // true

5. console.log(false==NaN)       // false

6. console.log(null==undefined)// true

7. console.log(null==0)        // false

8. console.log(null=='')       // false

9. console.log(null==NaN)       // false

10.  console.log(undefined==0)   // false

11.  console.log(undefined=='')  // false

12.  console.log(undefined==NaN)// false

13.  console.log(0=='')  // true

14.  console.log(0==NaN)// false

总结一下==

·      false 除了和自身比较为 true 外,和 0,”” 比较也为 true

·      null 只和 undefined 比较时为 true, 反过来 undefined 也仅和 null 比较为 true,没有第二个

·      0 除了和 false 比较为 true,还有空字符串 ””和空数组 []

·      空字符串 ” 除了和 false 比较为 true,还有一个数字 0

==, >, <, +, -, … 这些操作符所造成的隐式类型转换都是无副作用的,它不会改变变量本身保存的值。,但是,如果你覆写某个对象的 valueOf/toString的话,==就会产生副作用.

例如:

1. Array.prototype.valueOf =function(){

2.   this[0]++;

3.   returnthis;

4. }

5. var x =[1,2,3];

6. x ==0;

7. console.log(x);   // [2, 2, 3]

===操作符:

·      要是两个值类型不同,返回false

·      要是两个值都是number类型,并且数值相同,返回true

·      要是两个值都是stirng,并且两个值的String内容相同,返回true

·      要是两个值都是true或者都是false,返回true

·      要是两个值都是指向相同的Object,Arraya或者function,返回true

·      要是两个值都是null或者都是undefined,返回true

真假判断

·      js中以下内容为假

·      false

·      null

·      undefined

·      0

·      ” (空字符串)

·      NaN

设置默认参数

辑操作符 || 和 && 也可被用来返回布尔值。如果操作对象为非布尔对象,那每个表达式将会被自左向右地做真假判断。基于此操作,最终总有一个表达式被返回回来。这在变量赋值时,是可以用来简化你的代码的。例如:如果x不存在且y不存在,x=1;如果x存在y存在,x = y

 

 

1. if(!x){

2.   if(!y){

3.     x =1;

4.   }else{

5.     x = y;

6.   }

7. }

等同于:

1. x = x || y ||1;

这一小技巧经常用来给方法设定默认的参数。

1.  (function(log){

2.   'use strict';

3.   function multiply(a, b){

4.     a = a ||1;

5.     b = b ||1;

6.     log('Result '+ a * b);

7.   }

8.   multiply();// Result 1

9.   multiply(10);// Result 10

10.    multiply(3,NaN);// Result 3

11.    multiply(9,5);// Result 45

12.  }(window.console.log));

不使用eval()函数

就如eval的字面意思来说,恶魔,使用eval()函数会带来安全隐患。
eval()函数的作用是返回任意字符串,当作js代码来处理。

this关键字

只在对象构造器、方法和在设定的闭包中使用 this 关键字。this的语义在此有些误导。它时而指向全局对象(大多数时),时而指向调用者的定义域(在 eval 中),时而指向 DOM 树中的某一节点(当用事件处理绑定到 HTML 属性上时),时而指向一个新创建的对象(在构造器中),还时而指向其它的一些对象(如果函数被 call() 和 apply() 执行和调用时)。

正因为它是如此容易地被搞错,请限制它的使用场景:

·      在构造函数中

·      在对象的方法中(包括由此创建出的闭包内)

首选函数式风格

函数式编程让你可以简化代码并缩减维护成本,因为它容易复用,又适当地解耦和更少的依赖。

接下来的例子中,在一组数字求和的同一问题上,比较了两种解决方案。第一个例子是经典的程序处理,而第二个例子则是采用了函数式编程和 ECMA Script 5.1 的数组方法。


不推荐

1.  (function(log){

2.   'use strict';

3.   var arr =[10,3,7,9,100,20],

4.       sum =0,

5.       i;

6.  

7.   for(i =0; i < arr.length; i++){

8.     sum += arr[i];

9.   }

10.   

11.    log('The sum of array '+ arr +' is: '+ sum)

12.   

13.  }(window.console.log));

14.   

 

推荐(函数式编程):

1.  (function(log){

2.   'use strict';

3.  

4.   var arr =[10,3,7,9,100,20];

5.  

6.   var sum = arr.reduce(function(prevValue, currentValue){

7.     return prevValue + currentValue;

8.   },0);

9.  

10.    log('The sum of array '+ arr +' is: '+ sum);

11.   

12.  }(window.console.log));

修改内建对象的原型链

修改内建的诸如 Object.prototype 和 Array.prototype 是被严厉禁止的。修改其它的内建对象比如 Function.prototype,虽危害没那么大,但始终还是会导致在开发过程中难以 debug 的问题,应当也要避免。

三元条件判断(if 的快捷方法)

用三元操作符分配或返回语句。在比较简单的情况下使用,避免在复杂的情况下使用。没人愿意用 10 行三元操作符把自己的脑子绕晕。
不推荐:

1. if(x ===10){

2.   return'valid';

3. }else{

4.   return'invalid';

5. }

推荐:

1. return x ===10?'valid':'invalid'

JSHint

在js规范中,有很多规范都是样式上的规范而不是逻辑上的规范,比如尽量使用===而不是==,我们可以使用JSHint或者JSLint,Javascript代码验证工具,这种工具可以检查你的代码并提供相关的代码改进意见。我个人使用的是JSHint,所以就以这个为例

webstorm内置JSHint

对于ws爱好者来说,我没有用过其他的编译器,ws基本上能满足你的所有需求(最新的ws集成了vue)。
在Settings => language & frameworks =>JavaScript => Code Quality Tolls => JSHint

这些规范都是什么意思呢,这里列出一些常用的,剩下的大家可以参考官方文档

名称

含义

curly

循环或者条件语句必须使用花括号包住

eqeqeq

使用强制等===

newcap

对于首字母大写的函数(声明的类),强制使用new

noarg

禁用arguments.caller和arguments.callee

sub

对于属性使用aaa.bbb而不是aaa[‘bbb’]

undef

查找所有未定义的变量

boss

查找类似与if(a = 0)这样的代码

node

指定运行环境为node

strict

必须使用严格模式

asi

允许省略分号

bitwise

禁止使用位运算符,比如经常把&&写错& 规避此错误

jquery

定义全局暴露的jQuery

evil

禁止使用eval

maxdepth

嵌套的最大深度

maxparams

参数的最大个数

css规范  

id和class的命名

ID和class的名称总是使用可以反应元素目的和用途的名称,或其他通用的名称,代替表象和晦涩难懂的名称
常用 :

1. .fw-800 {

2.   font-weight:800;

3. }

4. .red {

5.   color: red;

6. }

 

 

推荐使用 :

1. .heavy {

2.   font-weight:800;

3. }

4. .important {

5.   color: red;

6. }

合理的使用ID

一般情况下ID不应该被用于样式,并且ID的权重很高,所以不使用ID解决样式的问题,而是使用class


不推荐:

1. #content .title {

2.   font-size:2em;

3. }

推荐:

1. .content .title {

2.   font-size:2em;

3. }

4.  

css选择器中避免使用标签名

从结构、表现、行为分离的原则来看,应该尽量避免css中出现HTML标签,并且在css选择器中出现标签名会存在潜在的问题。

使用子选择器

很多前端开发人员写选择器链的时候不使用 直接子选择器(注:直接子选择器和后代选择器的区别)。
有时,这可能会导致疼痛的设计问题并且有时候可能会很耗性能
然而,在任何情况下,这是一个非常不好的做法。
如果你不写很通用的,需要匹配到DOM末端的选择器, 你应该总是考虑直接子选择器。
不推荐:

1. .content .title {

2.   font-size:2rem;

3. }

推荐

1. .content >.title {

2.   font-size:2rem;

3. }

尽量使用缩写属性

尽量使用缩写属性对于代码效率和可读性是很有用的,比如font属性。
不推荐:

1. border-top-style: none;

2. font-family: palatino, georgia, serif;

3. font-size:100%;

4. line-height:1.6;

5. padding-bottom:2em;

6. padding-left:1em;

7. padding-right:1em;

8. padding-top:0;

推荐:

1. border-top:0;

2. font:100%/1.6 palatino, georgia, serif;

3. padding:01em2em;

0后面不带单位

省略0后面的单位,


不推荐:

1. padding-bottom:0px;

2. margin:0em;

推荐:

1. padding-bottom:0;

2. margin:0;

属性格式

·      为了保证一致性和可扩展性,每个声明应该用分号结束,每个声明换行。

·      属性名的冒号后使用一个空格。出于一致性的原因,
属性和值(但属性和冒号之间没有空格)的之间始终使用一个空格。

·      每个选择器和属性声明总是使用新的一行。

·      属性选择器或属性值用双引号(””)而不是单引号(”)括起来。

·      URI值(url())不要使用引号。

作为最佳实践,我们应该遵循以下顺序(应该按照下表的顺序):

结构性属性:

1.  display

2.  position, left, top, right etc.

3.  overflow, float, clear etc.

4.  margin, padding

表现性属性:

·      background, border etc.

·      font, text

不推荐:

1. .box {

2.   font-family:'Arial', sans-serif;

3.   border:3px solid #ddd;

4.   left:30%;

5.   position: absolute;

6.   text-transform: uppercase;

7.   background-color:#eee;

8.   right:30%;

9.   isplay: block;

10.    font-size:1.5rem;

11.    overflow: hidden;

12.    padding:1em;

13.    margin:1em;

14.  }

推荐:

1. .box {

2.   display: block;

3.   position: absolute;

4.   left:30%;

5.   right:30%;

6.   overflow: hidden;

7.   margin:1em;

8.   padding:1em;

9.   background-color:#eee;

10.    border:3px solid #ddd;

11.    font-family:'Arial', sans-serif;

12.    font-size:1.5rem;

13.    text-transform: uppercase;

14.  }

 

Airbnb CSS / Sass 编码规范

术语

规则声明

我们把一个(或一组)选择器和一组属性称之为“规则声明”。举个例子:

.listing {
  font-size: 18px;
  line-height: 1.2;
}

选择器

在规则声明中,“选择器”负责选取 DOM 树中的元素,这些元素将被定义的属性所修饰。选择器可以匹配HTML 元素,也可以匹配一个元素的类名、ID, 或者元素拥有的属性。以下是选择器的例子:

.my-element-class {
  /* ... */
}
 
[aria-hidden] {
 /* ... */
}

属性

最后,属性决定了规则声明里被选择的元素将得到何种样式。属性以键值对形式存在,一个规则声明可以包含一或多个属性定义。以下是属性定义的例子:

/* some selector */
{
  background: #f1f1f1;
  color: #333;
}

CSS

格式

·        使用 2 个空格作为缩进。

·        类名建议使用破折号代替驼峰法。如果你使用 BEM,也可以使用下划线(参见下面的OOCSS 和 BEM)。

·        不要使用 ID 选择器。

·        在一个规则声明中应用了多个选择器时,每个选择器独占一行。

·        在规则声明的左大括号 { 前加上一个空格。

·        在属性的冒号 : 后面加上一个空格,前面不加空格。

·        规则声明的右大括号 } 独占一行。

·        规则声明之间用空行分隔开。

Bad

.avatar{
    border-radius:50%;
border:2px solid white; 
}
.no, .nope, .not_good {
    // ...
}
#lol-no {
  // ...
}

Good

.avatar {
  border-radius: 50%;
  border: 2px solid white;
}
 
.one,
.selector,
.per-line {
  // ...
}

注释

·        建议使用行注释 (在 Sass 中是 //) 代替块注释。

·        建议注释独占一行。避免行末注释。

·        给没有自注释的代码写上详细说明,比如:

·        为什么用到了 z-index

·        兼容性处理或者针对特定浏览器的 hack

OOCSS 和BEM

出于以下原因,我们鼓励使用 OOCSS 和 BEM 的某种组合:

·        可以帮助我们理清 CSS 和 HTML 之间清晰且严谨的关系。

·        可以帮助我们创建出可重用、易装配的组件。

·        可以减少嵌套,降低特定性。

·        可以帮助我们创建出可扩展的样式表。

OOCSS,也就是 “ObjectOriented CSS(面向对象的CSS)”,是一种写CSS 的方法,其思想就是鼓励你把样式表看作“对象”的集合:创建可重用性、可重复性的代码段让你可以在整个网站中多次使用。

参考资料:

·        Nicole Sullivan OOCSS wiki

·        Smashing Magazine Introduction toOOCSS

BEM,也就是 “Block-Element-Modifier”,是一种用于 HTML 和 CSS 类名的命名约定。BEM最初是由 Yandex 提出的,要知道他们拥有巨大的代码库和可伸缩性,BEM 就是为此而生的,并且可以作为一套遵循 OOCSS 的参考指导规范。

·        CSS Trick BEM 101

·        Harry Roberts introduction to BEM

示例

<article class="listing-card listing-card--featured">
 
  <h1 class="listing-card__title">Adorable 2BR in the </h1>
 
  <div class="listing-card__content">
    <p>Vestibulum id ligula porta felis euismod semper.</p>
  </div>
 
</article>
.listing-card { }
.listing-card--featured { }
.listing-card__title { }
.listing-card__content { }

·        .listing-card 是一个块(block),表示高层次的组件。

·        .listing-card__title 是一个元素(element),它属于.listing-card 的一部分,因此块是由元素组成的。

·        .listing-card--featured 是一个修饰符(modifier),表示这个块与.listing-card有着不同的状态或者变化。

ID 选择器

在 CSS 中,虽然可以通过 ID 选择元素,但大家通常都会把这种方式列为反面教材。ID 选择器给你的规则声明带来了不必要的高优先级,而且 ID 选择器是不可重用的。

想要了解关于这个主题的更多内容,参见CSS Wizardry 的文章,文章中有关于如何处理优先级的内容。

JavaScript 钩子

避免在 CSS 和 JavaScript 中绑定相同的类。否则开发者在重构时通常会出现以下情况:轻则浪费时间在对照查找每个要改变的类,重则因为害怕破坏功能而不敢作出更改。

我们推荐在创建用于特定 JavaScript 的类名时,添加 .js- 前缀:

<button class="btn btn-primary js-request-to-book">Request to Book</button>

边框

在定义无边框样式时,使用 0 代替 none。

Bad

.foo {
  border: none;
}

Good

.foo {
  border: 0;
}

Sass

语法

·        使用 .scss 的语法,不使用 .sass 原本的语法。

·        CSS 和 @include 声明按照以下逻辑排序(参见下文)

属性声明的排序

1.   属性声明

首先列出除去 @include 和嵌套选择器之外的所有属性声明。

.btn-green {
  background: green;
  font-weight: bold;
  // ...
}

2.   @include 声明

紧随后面的是 @include,这样可以使得整个选择器的可读性更高。

.btn-green {
  background: green;
  font-weight: bold;
  @include transition(background 0.5s ease);
  // ...
}

3.   嵌套选择器

如果有必要用到嵌套选择器,把它们放到最后,在规则声明和嵌套选择器之间要加上空白,相邻嵌套选择器之间也要加上空白。嵌套选择器中的内容也要遵循上述指引。

.btn {
  background: green;
  font-weight: bold;
  @include transition(background 0.5s ease);
 
  .icon {
    margin-right: 10px;
  }
}

变量

变量名应使用破折号(例如 $my-variable)代替 camelCased 和 snake_cased 风格。对于仅用在当前文件的变量,可以在变量名之前添加下划线前缀(例如 $_my-variable)。

Mixins

为了让代码遵循 DRY 原则(Don’t RepeatYourself)、增强清晰性或抽象化复杂性,应该使用 mixin,这与那些命名良好的函数的作用是异曲同工的。虽然 mixin 可以不接收参数,但要注意,假如你不压缩负载(比如通过 gzip),这样会导致最终的样式包含不必要的代码重复。

扩展指令

应避免使用 @extend 指令,因为它并不直观,而且具有潜在风险,特别是用在嵌套选择器的时候。即便是在顶层占位符选择器使用扩展,如果选择器的顺序最终会改变,也可能会导致问题。(比如,如果它们存在于其他文件,而加载顺序发生了变化)。其实,使用 @extend 所获得的大部分优化效果,gzip 压缩已经帮助你做到了,因此你只需要通过 mixin 让样式表更符合 DRY 原则就足够了。

嵌套选择器

请不要让嵌套选择器的深度超过 3 层!

.page-container {
  .content {
    .profile {
      // STOP!
    }
  }
}

当遇到以上情况的时候,你也许是这样写 CSS 的:

·        与 HTML 强耦合的(也是脆弱的)—或者—

·        过于具体(强大)—或者—

·        没有重用

再说一遍: 永远不要嵌套 ID 选择器!

如果你始终坚持要使用 ID 选择器(劝你三思),那也不应该嵌套它们。如果你正打算这么做,你需要先重新检查你的标签,或者指明原因。如果你想要写出风格良好的 HTML 和 CSS,你是应该这样做的。

 

Airbnb JavaScript 编码规范

类型


– 
1.1 基本类型: 当您访问一个基本类型时,您将直接处理它的值。

  • string
  • number
  • boolean
  • null
  • undefined
  • symbol
1. const foo =1;
2. let bar = foo;
3.  
4. bar =9;
5.  
6. console.log(foo, bar);// => 1, 9
  • Symbols 不能被完全 polyfill, 所以不应该在目标浏览器/环境不支持它们的情况下使用它们。


– 
1.2 复合类型: 当你访问一个复合类型时,你需要引用它的值。

  • object
  • array
  • function
1. const foo =[1,2];
2. const bar = foo;
3.  
4. bar[0]=9;
5.  
6. console.log(foo[0], bar[0]);// => 9, 9

引用 References
– 
2.1 对所有的引用使用 const ;不要使用 vareslint: prefer-constno-const-assign

为什么?这可以确保你无法对引用重新分配,重新分配可能会导致 bug 和难以理解的代码。

1. // bad
2. var a =1;
3. var b =2;
4. // good
5. const a =1;
6. const b =2;


– 
2.2 如果你一定需要可变动的引用,使用 let 代替 vareslint: no-var jscs: disallowVar

为什么?因为 let 是块级作用域,而 var 是函数作用域。

1. // bad
2. var count =1;
3. if(true){
4.     count +=1;
5. }
6.  
7. // good, 使用 let.
8. let count =1;
9. if(true){
10.      count +=1;
11.  }


– 
2.3 注意 let  const 都是块级作用域。

1. // const  let 只存在于定义它们的代码块内。
2. {
3.     let a =1;
4.     const b =1;
5. }
6. console.log(a);// ReferenceError,引用错误
7. console.log(b);// ReferenceError,引用错误

对象 Objects


– 
3.1 使用字面量语法创建对象。 eslint: no-new-object

1. // bad
2. const item =newObject();
3.  
4. // good
5. const item ={};


– 
3.2 当创建带有动态属性名称的对象时使用计算的属性名称。

为什么? 它们允许你在一个地方定义一个对象的所有属性。

1. function getKey(k){
2.     return`a key named ${k}`;
3. }
4.  
5. // bad
6. const obj ={
7.     id:5,
8.     name:'San Francisco',
9. };
10.  obj[getKey('enabled')]=true;
11.   
12.  // good
13.  const obj ={
14.      id:5,
15.      name:'San Francisco',
16.      [getKey('enabled')]:true,
17.  };


– 
3.3 使用对象方法速记语法。 eslint: object-shorthand jscs: requireEnhancedObjectLiterals

1. // bad
2. const atom ={
3.     value:1,
4.  
5.     addValue:function(value){
6.     return atom.value+value;
7.     },
8. };
9.  
10.  // good
11.  const atom ={
12.      value:1,
13.   
14.      addValue(value){
15.      return atom.value+value;
16.      },
17.  };


– 
3.4 使用对象属性速记语法。eslint: object-shorthand jscs: requireEnhancedObjectLiterals

为什么?编写代码和描述更加简短。

1. const lukeSkywalker ='Luke Skywalker';
2.  
3. // bad
4. const obj ={
5.     lukeSkywalker: lukeSkywalker,
6. };
7.  
8. // good
9. const obj ={
10.      lukeSkywalker,
11.  };


– 
3.5 将速记属性分组写在对象声明的开始处。

为什么?更容易看出哪些属性在使用速记语法。

1. const anakinSkywalker ='Anakin Skywalker';
2. const lukeSkywalker ='Luke Skywalker';
3.  
4. // bad
5. const obj ={
6.     episodeOne:1,
7.     twoJediWalkIntoACantina:2,
8.     lukeSkywalker,
9.     episodeThree:3,
10.     mayTheFourth:4,
11.     anakinSkywalker,
12.  };
13.   
14.  // good
15.  const obj ={
16.      lukeSkywalker,
17.      anakinSkywalker,
18.      episodeOne:1,
19.      twoJediWalkIntoACantina:2,
20.      episodeThree:3,
21.      mayTheFourth:4,
22.  };


– 
3.6 只用引号引无效标识符的属性。 eslint: quote-props jscs: disallowQuotedKeysInObjects

为什么?一般来说,我们认为比较容易阅读。它改进了语法高亮显示,并且更容易被许多JS引擎优化。

1. // bad
2. const bad ={
3.     'foo':3,
4.     'bar':4,
5.     'data-blah':5,
6. };
7.  
8. // good
9. const good ={
10.      foo:3,
11.      bar:4,
12.      'data-blah':5,
13.  };


– 
3.7 不要直接调用 Object.prototype 的方法,比如 hasOwnPropertypropertyIsEnumerable,  isPrototypeOf.

为什么?这些方法可能会被对象的属性所覆盖比如 { hasOwnProperty: false } – 或者,对象可能是空null )对象(Object.create(null))

1. // bad
2. console.log(object.hasOwnProperty(key));
3.  
4. // good
5. console.log(Object.prototype.hasOwnProperty.call(object, key));
6.  
7. // best
8. const has =Object.prototype.hasOwnProperty;// 在模块作用域内,缓存查找一次。
9. /* or */
10.  import has from'has';
11.  // ...
12.  console.log(has.call(object, key));


– 
3.8 用对象展开操作符浅复制对象,优先于Object.assign 。使用对象剩余操作符来获得一个省略某些属性的新对象。

1. // very bad
2. const original ={ a:1, b:2};
3. const copy =Object.assign(original,{ c:3});//  `original` 是可变的_
4. delete copy.a;// so does this
5.  
6. // bad
7. const original ={ a:1, b:2};
8. const copy =Object.assign({}, original,{ c:3});// copy => { a: 1, b: 2, c: 3 }
9.  
10.  // good
11.  const original ={ a:1, b:2};
12.  const copy ={...original, c:3};// copy => { a: 1, b: 2, c: 3 }
13.   
14.  const{ a,...noA }= copy;// noA => { b: 2, c: 3 }

数组 Arrays


– 
4.1 使用字面量创建数组。 eslint: no-array-constructor

1. // bad
2. const items =newArray();
3.  
4. // good
5. const items =[];


– 
4.2 在向数组添加元素时使用 Array#push 替代直接赋值。

1. const someStack =[];
2.  
3. // bad
4. someStack[someStack.length]='abracadabra';
5.  
6. // good
7. someStack.push('abracadabra');


– 
4.3 使用数组展开操作符 ... 复制数组

1. // bad
2. const len = items.length;
3. const itemsCopy =[];
4. let i;
5.  
6. for(i =0; i < len; i +=1){
7.     itemsCopy[i]= items[i];
8. }
9.  
10.  // good
11.  const itemsCopy =[...items];


– 
4.4 使用展开操作符 ... 代替 Array.from,来将一个类数组(array-like) 对象转换成数组。

1. const foo = document.querySelectorAll('.foo');
2. // good
3. const nodes =Array.from(foo);
4. // best
5. const nodes =[...foo];


– 
4.5 实用 Array.from 代替展开操作符 ... 来映射迭代,因为它避免了创建媒介数组。

1. // bad
2. const baz =[...foo].map(bar);
3.  
4. // good
5. const baz =Array.from(foo, bar);


– 
4.6 在数组方法回调中使用 return 语句。如果函数体由一个返回无副作用的表达式的单个语句组成,那么可以省略返回值,查看8.2 说明。 eslint: array-callback-return

1. // good
2. [1,2,3].map((x)=>{
3.     const y = x +1;
4.     return x * y;
5. });
6.   
7. // good
8. [1,2,3].map(x => x +1);
9.  
10.  // bad - 没有返回值意味着  `memo` 在第一次迭代后变成 undefined
11.  [[0,1],[2,3],[4,5]].reduce((memo, item, index)=>{
12.      const flatten = memo.concat(item);
13.      memo[index]= flatten;
14.  });
15.   
16.  // good
17.  [[0,1],[2,3],[4,5]].reduce((memo, item, index)=>{
18.      const flatten = memo.concat(item);
19.      memo[index]= flatten;
20.      return flatten;
21.  });
22.   
23.  // bad
24.  inbox.filter((msg)=>{
25.      const{ subject, author }= msg;
26.      if(subject ==='Mockingbird'){
27.      return author ==='Harper Lee';
28.      }else{
29.      returnfalse;
30.      }
31.  });
32.   
33.  // good
34.  inbox.filter((msg)=>{
35.      const{ subject, author }= msg;
36.      if(subject ==='Mockingbird'){
37.      return author ==='Harper Lee';
38.      }
39.   
40.      returnfalse;
41.  });

 

 

 

 


– 
4.7 如果数组有多行,请在打开和关闭数组括号之前使用换行符。

1. // bad
2. const arr =[
3.   [0,1],[2,3],[4,5],
4. ];
5.  
6. const objectInArray =[{
7.     id:1,
8.  },{
9.     id:2,
10.  }];
11.   
12.  const numberInArray =[
13.     1,2,
14.  ];
15.   
16.  // good
17.  const arr =[[0,1],[2,3],[4,5]];
18.   
19.  const objectInArray =[
20.    {
21.      id:1,
22.    },
23.    {
24.      id:2,
25.    },
26.  ];
27.   
28.  const numberInArray =[
29.    1,
30.    2,
31.  ];

解构 Destructuring


– 
5.1 当访问和使用对象的多个属性时,请使用对象解构。eslint: prefer-destructuring jscs: requireObjectDestructuring

为什么?解构可以在你建这些属性的临时引用时,为你节省时间。

 

1. // bad
2. function getFullName(user){
3.     const firstName = user.firstName;
4.     const lastName = user.lastName;
5.  
6.     return`${firstName} ${lastName}`;
7. }
8.  
9. // good
10.  function getFullName(user){
11.      const{ firstName, lastName }= user;
12.      return`${firstName} ${lastName}`;
13.  }
14.   
15.  // best
16.  function getFullName({ firstName, lastName }){
17.      return`${firstName} ${lastName}`;
18.  }


– 
5.2 使用数组解构。 eslint: prefer-destructuring jscs: requireArrayDestructuring

1. const arr =[1,2,3,4];
2.  
3. // bad
4. const first = arr[0];
5. const second = arr[1];
6.  
7. // good
8. const[first, second]= arr;


– 
5.3 使用对象解构来实现多个返回值,而不是数组解构。jscs: disallowArrayDestructuringReturn

为什么?您可以随着时间的推移添加新的属性或更改排序,而不会改变调用时的位置。

1. // bad
2. function processInput(input){
3.     // 那么奇迹发生了
4.     return[left, right, top, bottom];
5. }
6.  
7.   
8. // 调用者需要考虑返回数据的顺序
9. const[left, __, top]= processInput(input);
10.   
11.  // good
12.  function processInput(input){
13.      // 那么奇迹发生了
14.      return{ left, right, top, bottom };
15.  }
16.   
17.  // 调用者只选择他们需要的数据
18.  const{ left, top }= processInput(input);

 

字符串 Strings


– 
6.1 字符串使用单引号 '' eslint: quotes jscs: validateQuoteMarks

1. // bad
2. const name ="Capt. Janeway";
3.  
4. // bad - 模板字面量应该包含插值或换行符
5. const name =`Capt. Janeway`;
6.  
7. // good
8. const name ='Capt. Janeway';


– 
6.2 超过100个字符,导致换行的字符串不应使用字符串连接符写成多行。

为什么?连接字符串是痛苦的工作,而且使代码不易于搜索。

 

1. // bad
2. const errorMessage ='This is a super long error that was thrown because \
3. of Batman. When you stop to think about how Batman had anything to do \
4. with this, you would get nowhere \
5. fast.';
6.   
7. // bad
8. const errorMessage ='This is a super long error that was thrown because '+
9.     'of Batman. When you stop to think about how Batman had anything to do '+
10.      'with this, you would get nowhere fast.';
11.   
12.  // good
13.  const errorMessage ='This is a super long error that was thrown because of Batman. When you stop to think about how Batman had anything to do with this, you would get nowhere fast.';


– 
6.3 以编程方式构建字符串时,请使用模板字符串而不是字符串连接。eslint: prefer-templatetemplate-curly-spacing jscs: requireTemplateStrings

为什么?模板字符串为你提供了更好的可读性,简洁的语法,正确的换行符和字符串插值功能。

1. // bad
2. function sayHi(name){
3.     return'How are you, '+ name +'?';
4. }
5.  
6. // bad
7. function sayHi(name){
8.     return['How are you, ', name,'?'].join();
9. }
10.   
11.  // bad
12.  function sayHi(name){
13.      return`How are you, ${ name }?`;
14.  }
15.   
16.  // good
17.  function sayHi(name){
18.      return`How are you, ${name}?`;
19.  }


– 
6.4 永远不要在字符串上使用 eval() ,它会打开太多的漏洞。 eslint: no-eval


– 
6.5 不要转义字符串中不必要转义的字符。 eslint: no-useless-escape

为什么?反斜杠会破坏可读性,因此只有在必要时才转义。

1. // bad
2. const foo ='\'this\' \i\s \"quoted\"';
3.  
4. // good
5. const foo ='\'this\' is "quoted"';
6. const foo =`my name is '${name}'`;

函数 Functions


– 
7.1 使用命名函数表达式而不是函数声明。 eslint: func-style jscs: disallowFunctionDeclarations

为什么?函数声明很容易被提升(Hoisting),你可以在函数被定义之前引用该函数。这对可读性和可维护性来说都是不利的。如果你发现一个函数的定义很大或很复杂,以至于妨碍了解文件的其他部分,那么也许是时候把它提取到自己的模块中去!不要忘记显式地命名表达式,不管该名称是否从包含变量中推断出来的,这消除了关于Error的调用堆栈的任何假设。

1. // bad
2. function foo(){
3.     // ...
4. }
5. // bad
6. const foo =function(){
7.     // ...
8. };
9. // good 
10.  // 用明显区别于变量引用调用的词汇命名
11.  constshort=function longUniqueMoreDescriptiveLexicalFoo(){
12.      // ...
13.  };


– 
7.2 用圆括号包裹立即调用函数表达式 (IIFE) eslint: wrap-iife jscs: requireParenthesesAroundIIFE

为什么?一个立即调用函数表达式是一个单独的单元将函数表达式包裹在括号中,后面再跟一个调用括号,这看上去很赶紧。请注意,在模块的世界中,你几乎不需要 IIFE

1. // 立即调用函数表达式 (IIFE)
2. (function(){
3.     console.log('Welcome to the Internet. Please follow me.');
4. }());


– 
7.3 永远不要在一个非函数代码块(ifwhile 等)中声明一个函数,把那个函数赋给一个变量代替。浏览器允许你这么做,但是它们都以不同的方式解析。 eslint: no-loop-func


– 
7.4 注意: ECMA-262  block 定义为一组语句。函数声明不是语句。

1. // bad
2. if(currentUser){
3.     function test(){
4.         console.log('Nope.');
5.     }
6. }
7.  
8. // good
9. let test;
10.  if(currentUser){
11.       test =()=>{
12.          console.log('Yup.');
13.      };
14.  }


– 
7.5 永远不要把参数命名为 arguments。这将会覆盖原来函数作用域内的 arguments 对象。

 

 

1. // bad
2. function foo(name, options, arguments){
3.     // ...
4. }
5.  
6. // good
7. function foo(name, options, args){
8.     // ...
9. }


– 
7.6 不要使用 arguments。可以选择 rest 语法 ... 替代。eslint: prefer-rest-params

为什么?使用 ... 能明确你要传入的参数。另外 rest(剩余)参数是一个真正的数组,而 arguments 是一个类数组(Array-like)。

1. // bad
2. function concatenateAll(){
3.     const args =Array.prototype.slice.call(arguments);
4.     return args.join('');
5. }
6.  
7. // good
8. function concatenateAll(...args){
9.     return args.join('');
10.  }


– 
7.7 使用默认参数语法,而不要使用一个变化的函数参数。

1. // really bad
2. function handleThings(opts){
3.     // 不!我们不应该改变函数参数。
4.     // 更加糟糕: 如果参数 opts  falsy(假值) 的话,它将被设置为一个对象,
5.     // 这可能是你想要的,但它可以引起一些小的错误。
6.     opts = opts ||{};
7.     // ...
8. }
9.  
10.  // still bad
11.  function handleThings(opts){
12.      if(opts ===void0){
13.      opts ={};
14.      }
15.      // ...
16.  }
17.   
18.  // good
19.  function handleThings(opts ={}){
20.      // ...
21.  }


– 
7.8 避免默认参数的副作用。

为什么?因为这样写会让人感到很困惑。

1. var b =1;
2. // bad
3. function count(a = b++){
4.     console.log(a);
5. }
6. count();  // 1
7. count();  // 2
8. count(3);// 3
9. count();  // 3


– 
7.9 始终将默认参数放在最后。

1. // bad
2. function handleThings(opts ={}, name){
3.     // ...
4. }
5.  
6. // good
7. function handleThings(name, opts ={}){
8.     // ...
9. }


– 
7.10 切勿使用 Function 构造函数来创建新函数。 eslint: no-new-func

为什么?以这种方式创建一个函数,与 eval() 类似,会对字符串求值,这会打开漏洞。

1. // bad
2. varadd=newFunction('a','b','return a + b');
3.  
4. // still bad
5. var subtract =Function('a','b','return a - b');


– 
7.11 隔开函数签名,括号两边用空格隔开。 eslint: space-before-function-parenspace-before-blocks

为什么?这样做有益代码的一致性,添加或删除函数名时不需要添加或删除空格。

1. // bad
2. const f =function(){};
3. const g =function(){};
4. const h =function(){};
5.  
6. // good
7. const x =function(){};
8. const y =function a(){};


– 
7.12 不要改变参数。 eslint: no-param-reassign

为什么?操作作为参数传入的对象,可能会在调用原始对象时造成不必要的变量副作用。(对象是引用类型

1. // bad
2. function f1(obj){
3.     obj.key =1;
4. }
5.  
6. // good
7. function f2(obj){
8.     const key =Object.prototype.hasOwnProperty.call(obj,'key')? obj.key :1;
9. }


– 
7.13 参数不要重新赋值。 eslint: no-param-reassign

为什么?重新分配参数可能会导致意外的行为,特别是在访问 arguments 对象时。它也可能导性能化问题,特别是在V8中。

1. // bad
2. function f1(a){
3.     a =1;
4.     // ...
5. }
6.   
7.   function f2(a){
8.     if(!a){ a =1;}
9.     // ...
10.  }
11.   
12.  // good
13.  function f3(a){
14.      const b = a ||1;
15.      // ...
16.  }
17.   
18.  function f4(a =1){
19.      // ...
20.  }


– 
7.14 优先使用展开运算符 ... 来调用可变参数函数。 eslint: prefer-spread

为什么?它更清洁,你不需要提供一个上下文,而且你不能轻易地实用 apply  new

1. // bad
2. const x =[1,2,3,4,5];
3. console.log.apply(console, x);
4.  
5. // good
6. const x =[1,2,3,4,5];
7. console.log(...x);
8.  
9. // bad
10.  new(Function.prototype.bind.apply(Date,[null,2016,8,5]));
11.   
12.  // good
13.  newDate(...[2016,8,5]);

 

 

 

 

 


– 
7.15 具有多行签名或调用的函数,应该像本指南中的其他多行列表一样缩进:每一项都独占一行,最后一项上有一个尾逗号。

 

1. // bad
2. function foo(bar,
3.                 baz,
4.                 quux){
5.     // ...
6. }
7.  
8. // good
9. function foo(
10.      bar,
11.      baz,
12.      quux,
13.  ){
14.      // ...
15.  }
16.   
17.  // bad
18.  console.log(foo,
19.      bar,
20.      baz);
21.   
22.  // good
23.  console.log(
24.      foo,
25.      bar,
26.      baz,
27.  );

箭头函数 Arrow Functions


– 
8.1 当您必须使用匿名函数(如在传递一个内联回调时),请使用箭头函数表示法。 eslint: prefer-arrow-callbackarrow-spacing jscs: requireArrowFunctions

为什么?它创建了一个在 this 上下文中执行的函数的版本,这通常是你想要的,而且这样的写法更为简洁。(愚人码头注:参考 Arrow functions – JavaScript | MDN  ES6 arrow functions, syntax andlexical scoping

为什么不?如果你有一个相当复杂的函数,你或许可以把逻辑部分转移到一个声明函数上。

1. // bad
2. [1,2,3].map(function(x){
3.     const y = x +1;
4.     return x * y;
5. });
6.  
7. // good
8. [1,2,3].map((x)=>{
9.     const y = x +1;
10.     return x * y;
11.  });


– 
8.2 如果函数体由一个返回无副作用(side effect)expression(表达式)的单行语句组成,那么可以省略大括号并使用隐式返回。否则,保留大括号并使用 return 语句。eslint: arrow-parensarrow-body-style jscs: disallowParenthesesAroundArrowParamrequireShorthandArrowFunctions

愚人码头注,什么是副作用(side effect)?一段代码,即在不需要的情况下,创建一个变量并在整个作用域内可用。

为什么?语法糖。当多个函数链式调用时,可读性更高

1. // bad
2. [1,2,3].map(number =>{
3.     const nextNumber = number +1;
4.     `A string containing the ${nextNumber}.`;
5. });
6.  
7. // good
8. [1,2,3].map(number =>`A string containing the ${number}.`);
9.  
10.  // good
11.  [1,2,3].map((number)=>{
12.      const nextNumber = number +1;
13.      return`A string containing the ${nextNumber}.`;
14.  });
15.   
16.  // good
17.  [1,2,3].map((number, index)=>({
18.      [index]: number,
19.  }));
20.   
21.  // No implicit return with side effects
22.  function foo(callback){
23.      const val = callback();
24.      if(val ===true){
25.      // Do something if callback returns true
26.      }
27.  }
28.   
29.  letbool=false;
30.   
31.  // bad
32.  foo(()=>bool=true);
33.  // good
34.  foo(()=>{
35.      bool=true;
36.  });


– 
8.3 如果表达式跨多行,将其包裹在括号中,可以提高可读性。

为什么?它清楚地显示了函数开始和结束的位置。

1. // bad
2. ['get','post','put'].map(httpMethod =>Object.proto  type.hasOwnProperty.call(
3.     httpMagicObjectWithAVeryLongName,
4.     httpMethod,
5.   )
6. );
7.  
8. // good
9. ['get','post','put'].map(httpMethod =>(
10.    Object.prototype.hasOwnProperty.call(
11.       httpMagicObjectWithAVeryLongName,
12.       httpMethod,
13.    )
14.  ));


– 
8.4 如果你的函数只有一个参数并且不使用大括号,则可以省略参数括号。否则,为了清晰和一致性,总是给参数加上括号。
注意:总是使用圆括号也是可以被lint工具接受的,在这种情况下使用 eslint  “always” 选项,或者 jscs 中不要包含 disallowParenthesesAroundArrowParam 选项。 eslint: arrow-parens jscs: disallowParenthesesAroundArrowParam

为什么?不造成视觉上的混乱。

 

1. // bad
2. [1,2,3].map((x)=> x * x);
3.  
4. // good
5. [1,2,3].map(x => x * x);
6.  
7. // good
8. [1,2,3].map(number =>(
9.     `A long string with the ${number}. It’s so long that we don’t want it to take up space on the .map line!`
10.  ));
11.   
12.  // bad
13.  [1,2,3].map(x =>{
14.      const y = x +1;
15.      return x * y;
16.  });
17.   
18.  // good
19.  [1,2,3].map((x)=>{
20.      const y = x +1;
21.      return x * y;
22.  });

 

 

 

 


– 
8.5 避免使用比较运算符(&lt; =&gt;=)时,混淆箭头函数语法(=&gt;) eslint: no-confusing-arrow

 

1. // bad
2. const itemHeight = item => item.height >256? item.largeSize : item.smallSize;
3. // bad
4. const itemHeight =(item)=> item.height >256? item.largeSize : item.smallSize;
5.  
6. // good
7. const itemHeight = item =>(item.height >256? item.largeSize : item.smallSize);
8.  
9. // good
10.  const itemHeight =(item)=>{
11.      const{ height, largeSize, smallSize }= item;
12.      return height >256? largeSize : smallSize;
13.  };

类 Classes & 构造函数 Constructors


– 
9.1 总是使用 class。避免直接操作 prototype 

为什么? 因为 class 语法更为简洁更易读。

1. // bad
2. functionQueue(contents =[]){
3.     this.queue =[...contents];
4. }
5. Queue.prototype.pop =function(){
6.     constvalue=this.queue[0];
7.     this.queue.splice(0,1);
8.     returnvalue;
9. };
10.  // good
11.  classQueue{
12.      constructor(contents =[]){
13.        this.queue =[...contents];
14.      }
15.      pop(){
16.        constvalue=this.queue[0];
17.        this.queue.splice(0,1);
18.        returnvalue;
19.      }
20.   }


– 
9.2 使用 extends 继承。

为什么?因为 extends 是一个内置的原型继承方法并且不会破坏 instanceof

1. // bad
2. const inherits =require('inherits');
3. functionPeekableQueue(contents){
4.     Queue.apply(this, contents);
5. }
6. inherits(PeekableQueue,Queue);
7. PeekableQueue.prototype.peek =function(){
8.     returnthis.queue[0];
9. };
10.   
11.  // good
12.  classPeekableQueueextendsQueue{
13.      peek(){
14.      returnthis.queue[0];
15.      }
16.  }


– 
9.3 方法可以返回 this 来帮助链式调用。

1. // bad
2. Jedi.prototype.jump =function(){
3.     this.jumping =true;
4.     returntrue;
5. };
6.  
7. Jedi.prototype.setHeight =function(height){
8.     this.height = height;
9. };
10.  const luke =newJedi();
11.  luke.jump();// => true
12.  luke.setHeight(20);// => undefined
13.  // good
14.  classJedi{
15.      jump(){
16.        this.jumping =true;
17.         returnthis;
18.      }
19.   
20.      setHeight(height){
21.        this.height = height;
22.        returnthis;
23.      }
24.  }
25.   
26.  const luke =newJedi();
27.   
28.  luke.jump();
29.  luke.setHeight(20);


– 
9.4 可以写一个自定义的 toString() 方法,但要确保它能正常运行并且不会引起副作用。

1. classJedi{
2.     constructor(options ={}){
3.       this.name = options.name ||'no name';
4.     }
5.  
6.     getName(){
7.       returnthis.name;
8.     }
9.  
10.    toString(){
11.      return`Jedi - ${this.getName()}`;
12.    }
13.  }


– 
9.5 如果没有指定,类有一个默认的构造函数。一个空的构造函数或者只是委托给父类则不是必须的。 eslint: no-useless-constructor

1. // bad
2. classJedi{
3.     constructor(){}
4.     getName(){
5.       returnthis.name;
6.     }
7. }
8. // bad
9. classReyextendsJedi{
10.      constructor(...args){
11.        super(...args);
12.       }
13.   }
14.   
15.  // good
16.  classReyextendsJedi{
17.      constructor(...args){
18.        super(...args);
19.        this.name ='Rey';
20.      }
21.  }


– 
9.6 避免重复类成员。 eslint: no-dupe-class-members

为什么?重复类成员声明将默认使用最后一个重复类成员几乎肯定是一个错误。

1. // bad
2. classFoo{
3.     bar(){return1;}
4.     bar(){return2;}
5. }
6.  
7. // good
8. classFoo{
9.     bar(){return1;}
10.  }
11.   
12.  // good
13.  classFoo{
14.     bar(){return2;}
15.  }

模块 Modules


– 
10.1 总是使用模块 (import/export) 而不是其他非标准模块系统。你可以编译为你喜欢的模块系统。

为什么?模块就是未来,让我们开始迈向未来吧。

1. // bad
2. constAirbnbStyleGuide=require('./AirbnbStyleGuide');
3. module.exports =AirbnbStyleGuide.es6;
4.  
5. // ok
6. importAirbnbStyleGuidefrom'./AirbnbStyleGuide';
7. exportdefaultAirbnbStyleGuide.es6;
8.  
9. // best
10.  import{ es6 }from'./AirbnbStyleGuide';
11.  exportdefault es6;


– 
10.2 不要使用通配符 import(导入)

为什么?这样能确保你只有一个默认 export(导出)

1. // bad
2. import*asAirbnbStyleGuidefrom'./AirbnbStyleGuide';
3.  
4. // good
5. importAirbnbStyleGuidefrom'./AirbnbStyleGuide';


– 
10.3 不要从 import(导入) 中直接 export(导出)

为什么?虽然一行代码简洁明了,但有一个明确的 import(导入) 方法和一个明确的 export(导出) 方法,使事情能保持一致。

1. // bad
2. // filename es6.js
3. export{ es6 asdefault}from'./AirbnbStyleGuide';
4.  
5. // good
6. // filename es6.js
7. import{ es6 }from'./AirbnbStyleGuide';
8. exportdefault es6;


– 
10.4 一个地方只在一个路径中 import(导入)
eslint: 
no-duplicate-imports

为什么?从同一路径 import(导入) 多个模块分散在多行代码中,可能会使代码难以维护。

1. // bad
2. import foo from'foo';
3. // … 其他一些 imports … //
4. import{ named1, named2 }from'foo';
5.  
6. // good
7. import foo,{ named1, named2 }from'foo';
8.  
9. // good
10.  import foo,{
11.      named1,
12.      named2,
13.  }from'foo';


– 
10.5 不要 export(导出) 可变绑定。eslint: import/no-mutable-exports

为什么?一般应该避免可变性,特别是在导出可变绑定时。虽然一些特殊情况下,可能需要这种技术,但是一般而言,只应该导出常量引用。

1. // bad
2. let foo =3;
3. export{ foo };
4.  
5. // good
6. const foo =3;
7. export{ foo };


– 
10.6 在只有单个导出的模块中,默认 export(导出) 优于命名 export(导出)eslint: import/prefer-default-export

为什么?为了鼓励更多的文件只有一个 export(导出),这有利于模块的可读性和可维护性。

1. // bad
2. exportfunction foo(){}
3.  
4. // good
5. exportdefaultfunction foo(){}


– 
10.7 将所有 import 导入放在非导入语句的上面。eslint: import/first

由于 import 被提升,保持他们在顶部,防止意外的行为。

1. // bad
2. import foo from'foo';
3. foo.init();
4.  
5. import bar from'bar';
6.  
7. // good
8. import foo from'foo';
9. import bar from'bar';
10.   
11.  foo.init();


– 
10.8 多行导入应该像多行数组和对象字面量一样进行缩进。

为什么?花括号应遵循与编码风格指南中的每个其他花括号相同的缩进规则,末尾的逗号也一样。

1. // bad
2. import{longNameA, longNameB, longNameC, longNameD, longNameE}from'path';
3.  
4. // good
5. import{
6.     longNameA,
7.     longNameB,
8.     longNameC,
9.     longNameD,
10.      longNameE,
11.  }from'path';


– 
10.9 禁止在模块 import(导入) 语句中使用 Webpack 加载器语法。
eslint: 
import/no-webpack-loader-syntax

为什么?由于在 import(导入) 中使用 Webpack 语法会将代码耦合到模块打包器。首选在 webpack.config.js 中使用加载器语法。

1. // bad
2. import fooSass from'css!sass!foo.scss';
3. import barCss from'style!css!bar.css';
4.  
5. // good
6. import fooSass from'foo.scss';
7. import barCss from'bar.css';

迭代器 Iterators 和 生成器 Generators


– 
11.1 不要使用 iterators(迭代器)。请使用高阶函数,例如 map()  reduce() 等,而不是像 for-in  for-of 这样的循环。 eslint: no-iterator no-restricted-syntax

为什么?这是强制执行我们不变性的规则。处理返回值的纯函数比 Side Effects(副作用) 更容易推理。

使用 map() / every() / filter() / find() / findIndex() / reduce() / some() / … 来迭代数组, 使用 Object.keys() / Object.values() / Object.entries() 来生成数组,以便可以迭代对象。

1. const numbers =[1,2,3,4,5];
2.  
3. // bad
4. let sum =0;
5. for(let num of numbers){
6.     sum += num;
7. }
8. sum ===15;
9.  
10.  // good
11.  let sum =0;
12.  numbers.forEach((num)=>{
13.      sum += num;
14.  });
15.  sum ===15;
16.   
17.  // best (use the functional force)
18.  const sum = numbers.reduce((total, num)=> total + num,0);
19.  sum ===15;
20.   
21.  // bad
22.  const increasedByOne =[];
23.  for(let i =0; i < numbers.length; i++){ increasedByOne.push(numbers[i]+1);}// good const increasedByOne = []; numbers.forEach((num) => {
24.      increasedByOne.push(num +1);
25.  });
26.   
27.  // best (keeping it functional)
28.  const increasedByOne = numbers.map(num => num +1);


– 
11.2 现在不要使用 generators (生成器)

为什么?因为目前没有很好地办法将他们转译成 ES5


– 
11.3 如果您必须使用 generators (生成器),或者如果漠视我们的建议,请确保它们的函数签名恰当的间隔。 eslint: generator-star-spacing

为什么? function  * 都是同一概念关键字的组成部分 – * 不是 function 的修饰符,function* 是一个独特的构造,与function不同。

1. // bad
2. function* foo(){
3.     // ...
4. }
5.  
6. // bad
7. const bar =function*(){
8.     // ...
9. };
10.   
11.  // bad
12.  const baz =function*(){
13.      // ...
14.  };
15.   
16.  // bad
17.  const quux =function*(){
18.      // ...
19.  };
20.   
21.  // bad
22.  function*foo(){
23.      // ...
24.  }
25.   
26.  // bad
27.  function*foo(){
28.      // ...
29.  }
30.   
31.  // very bad
32.  function
33.  *
34.  foo(){
35.      // ...
36.   }
37.   
38.  // very bad
39.  const wat =function
40.  *
41.  (){
42.      // ...
43.  };
44.   
45.  // good
46.  function* foo(){
47.      // ...
48.  }
49.   
50.  // good
51.  const foo =function*(){
52.      // ...
53.  };

属性 Properties


– 
12.1 使用点语法(.) 来访问对象的属性。 eslint: dot-notation jscs: requireDotNotation

1. const luke ={
2.     jedi:true,
3.     age:28,
4. };
5.  
6. // bad
7. const isJedi = luke['jedi'];
8.  
9. // good
10.  const isJedi = luke.jedi;


– 
12.2 当通过变量访问属性时使用中括号 []

1. const luke ={
2.     jedi:true,
3.     age:28,
4. };
5.  
6. function getProp(prop){
7.     return luke[prop];
8. }
9.  
10.  const isJedi = getProp('jedi');


– 
12.3 求幂时使用求幂运算符 ** eslint: no-restricted-properties.

1. // bad
2. const binary =Math.pow(2,10);
3.  
4. // good
5. const binary =2**10;

变量 Variables


– 
13.1 总是使用 const  let 来声明变量。不这样做会导致产生全局变量。我们希望避免污染全局命名空间。 eslint: no-undef prefer-const

1. // bad
2. superPower =newSuperPower();
3.  
4. // good
5. const superPower =newSuperPower();


– 
13.2 使用 const  let声明每个变量。 eslint: one-var jscs: disallowMultipleVarDecl

为什么?以这种方式添加新的变量声明更容易,你永远不必担心是否需要将 , 换成 ;,或引入标点符号差异。您也可以在调试器中遍历每个声明,而不是一次跳过所有的变量。

1. // bad
2. const items = getItems(),
3.     goSportsTeam =true,
4.     dragonball ='z';
5.  
6. // bad
7. // (与上面的比较,并尝试找出错误)
8. const items = getItems(),
9.     goSportsTeam =true;
10.      dragonball ='z';
11.   
12.  // good
13.  const items = getItems();
14.  const goSportsTeam =true;
15.  const dragonball ='z';


– 
13.3 将所有的 const  let 分组

为什么?当你需要把已分配的变量分配给一个变量时非常有用。

1. // bad
2. let i, len, dragonball,
3.     items = getItems(),
4.     goSportsTeam =true;
5.  
6. // bad
7. let i;
8. const items = getItems();
9. let dragonball;
10.  const goSportsTeam =true;
11.  let len;
12.   
13.  // good
14.  const goSportsTeam =true;
15.  const items = getItems();
16.  let dragonball;
17.  let i;
18.  let length;


– 
13.4 在你需要的地方分配变量,但请把它们放在一个合理的位置。

为什么?let  const 是块级作用域而不是函数作用域。

1.  // bad - 不必要的函数调用
2. function checkName(hasName){
3.     const name = getName();
4.     if(hasName ==='test'){
5.       returnfalse;
6.     }
7.     if(name ==='test'){
8.       this.setName('');
9.       returnfalse;
10.    }
11.     return name;
12.  }
13.    
14.  // good
15.  function checkName(hasName){
16.      if(hasName ==='test'){
17.      returnfalse;
18.      }
19.   
20.      const name = getName();
21.   
22.      if(name ==='test'){
23.      this.setName('');
24.      returnfalse;
25.      }
26.   
27.      return name;
28.  }


– 
13.5 变量不要链式赋值。eslint: no-multi-assign

为什么?链接变量赋值会创建隐式全局变量。

1. // bad
2. (function example(){
3.     // JavaScript 将其解析为
4.     // let a = ( b = ( c = 1 ) );
5.     // let关键字只适用于变量a;
6.     // 变量bc变成了全局变量。
7.     let a = b = c =1;
8. }());
9. console.log(a);// 抛出 ReferenceError(引用错误)
10.  console.log(b);// 1
11.  console.log(c);// 1
12.  // good
13.  (function example(){
14.      let a =1;
15.      let b = a;
16.      let c = a;
17.  }());
18.   
19.  console.log(a);// 抛出 ReferenceError(引用错误)
20.  console.log(b);// 抛出 ReferenceError(引用错误)
21.  console.log(c);// 抛出 ReferenceError(引用错误)
22.   
23.  // 同样适用于 `const`


– 
13.6 避免使用一元递增和递减运算符(++--) eslint no-plusplus

为什么?根据 eslint 文档,一元递增和递减语句会受到自动插入分号的影响,并可能导致应用程序中的值递增或递减,从而导致无提示错误。使用像 num += 1 而不是 num++  num ++ 这样的语句来改变你的值也更具有表现力。不允许一元递增和递减语句也会阻止您无意中预先递增/递减值,这也会导致程序中的意外行为。

1. // bad
2.  
3. const array =[1,2,3];
4. let num =1;
5. num++;
6. --num;
7.  
8. let sum =0;
9. let truthyCount =0;
10.  for(let i =0; i < array.length; i++){
11.      letvalue= array[i];
12.      sum +=value;
13.      if(value){ truthyCount++;}
14.  }
15.  // good 
16.  const array = [1, 2, 3]; 
17.  let num = 1; num += 1; num -= 1; 
18.  const sum = array.reduce((a, b) => a + b, 0);
19.  const truthyCount = array.filter(Boolean).length;

Hoisting


– 
14.1 var 声明会被提升至他们作用域的顶部,但它们赋值不会提升。let  const 声明被赋予了一种称为「暂时性死区(Temporal Dead Zones, TDZ」的概念。这对于了解为什么 type of 不再安全相当重要。

1. // 我们知道这样运行不了
2. // (假设没有 notDefined 全局变量)
3. function example(){
4.     console.log(notDefined);// => 抛出一个 ReferenceError(引用错误)
5. }
6. // 在引用变量后创建变量声明
7. // 将因变量提升而起作用。
8. // 注意:赋值的 `true`没有被提升。
9. function example(){
10.      console.log(declaredButNotAssigned);// => undefined
11.      var declaredButNotAssigned =true;
12.  }
13.   
14.  // 解析器将变量声明提升到作用域的顶部,
15.      // 这意味着我们的例子可以被重写为:
16.  function example(){
17.      let declaredButNotAssigned;
18.      console.log(declaredButNotAssigned);// => undefined
19.      declaredButNotAssigned =true;
20.  }
21.   
22.  // 使用 const  let
23.  function example(){
24.      console.log(declaredButNotAssigned);// => 抛出一个 ReferenceError(引用错误)
25.      console.log(typeof declaredButNotAssigned);// => 抛出一个 ReferenceError(引用错误)
26.      const declaredButNotAssigned =true;
27.  }


– 
14.2 匿名函数表达式的变量名会被提升,但函数分配不会。

1. function example(){
2.     console.log(anonymous);// => undefined
3.  
4.     anonymous();// => TypeError anonymous is not a function 输入错误,anonymous 不是一个函数
5.  
6.     var anonymous =function(){
7.       console.log('anonymous function expression');
8.     };
9. }


– 
14.3 命名的函数表达式的变量名会被提升,但函数名和函数体并不会。

1. function example(){
2.     console.log(named);// => undefined
3.  
4.     named();// => TypeError named is not a function,输入错误,named 不是一个函数
5.  
6.     superPower();// => ReferenceError superPower is not defined ReferenceError(引用错误)superPower 未定义
7.     var named =function superPower(){
8.        console.log('Flying');
9.      };
10.  }
11.   
12.  // 当函数名称与变量名称相同时
13.  // 也是如此。
14.  function example(){
15.      console.log(named);// => undefined
16.   
17.      named();// => TypeError named is not a function,输入错误,named 不是一个函数
18.   
19.      var named =function named(){
20.          console.log('named');
21.      };
22.  }


– 
14.4 函数声明的名称和函数体都会被提升。

1. function example(){
2.     superPower();// => Flying
3.  
4.     function superPower(){
5.        console.log('Flying');
6.     }
7. }

 

比较运算符 Comparison Operators 和 等号 Equality


– 
15.1 使用 ===  !== 优先于 ==  != eslint: eqeqeq


– 
15.2 诸如 if 语句之类的条件语句使用 ToBoolean 抽象方法来强制求值它们的表达式,并始终遵循以下简单规则:

  • Objects 求值为 true
  • Undefined 求值为 false
  • Null 求值为 false
  • Booleans 求值为 布尔值
  • Numbers 如果是 +0、-0、或 NaN 求值为 false , 否则为 true
  • Strings 如果是空字符串 '' 求值为 false , 否则为 true
1. if([0]&&[]){
2.     // true
3.     // 一个数组 (即使是一个空数组) 是一个 object, objects 被求值为 true
4. }


– 
15.3 对于布尔值使用简写,但对于字符串和数字使用显式比较。

1. // bad
2. if(isValid ===true){
3.     // ...
4. }
5. // good
6. if(isValid){
7.     // ...
8. }
9. // bad
10.  if(name){
11.      // ...
12.  }
13.  // good
14.  if(name !==''){
15.      // ...
16.  }
17.  // bad
18.  if(collection.length){
19.      // ...
20.  }
21.  // good
22.  if(collection.length >0){
23.      // ...
24.  }


– 
15.4 想了解更多信息,参考 Angus Croll  Truth Equality and JavaScript


– 
15.5  case  default 子句中,使用大括号来创建包含词法声明的语句块(例如 letconstfunction,  class).eslint: no-case-declarations

为什么?词法声明在整个 switch 语句块中都是可见的,但是只有在分配时才被初始化,这只有当它到达 case 时才会发生。这在多个 case 子句试图定义相同的变量时会导致问题。

1. // bad
2. switch(foo){
3.     case1:
4.         let x =1;
5.         break;
6.     case2:
7.         const y =2;
8.         break;
9.     case3:
10.      function f(){
11.          // ...
12.      }
13.       break;
14.       default:
15.       class C {}
16.  }
17.   
18.  // good
19.  switch(foo){
20.      case1:{
21.         let x =1;
22.         break; 
23.      }
24.      case2:{
25.          const y =2;
26.          break;
27.      }
28.      case3:{
29.         function f(){}
30.          break;
31.      }
32.      case4:
33.         bar();
34.          break;
35.          default:{
36.            class C {}
37.            }
38.   }


– 
15.6 三元表达式不应该嵌套,通常写成单行表达式。 eslint: no-nested-ternary

1. // bad
2. const foo = maybe1 > maybe2
3.     ?"bar"
4.     : value1 > value2 ?"baz":null;
5.  
6. // 拆分成2个分离的三元表达式
7. const maybeNull = value1 > value2 ?'baz':null;
8.  
9. // better
10.  const foo = maybe1 > maybe2
11.      ?'bar'
12.      : maybeNull;
13.   
14.  // best
15.  const foo = maybe1 > maybe2 ?'bar': maybeNull;


– 
15.7 避免不必要的三元表达式语句。 eslint: no-unneeded-ternary

1. // bad
2. const foo = a ? a : b;
3. const bar = c ?true:false;
4. const baz = c ?false:true;
5.  
6. // good
7. const foo = a || b;
8. const bar =!!c;
9. const baz =!c;


– 
15.8 当运算符混合在一个语句中时,请将其放在括号内。混合算术运算符时,不要将 **  %  +  -*/ 混合在一起。eslint: no-mixed-operators

为什么?这可以提高可读性,并清晰展现开发者的意图。

1. // bad
2. const foo = a && b <0|| c >0|| d +1===0;
3.  
4. // bad
5. const bar = a ** b -5% d;
6.  
7. // bad
8. if(a || b && c){
9.     return d;
10.  }
11.   
12.  // good
13.  const foo =(a && b <0)|| c >0||(d +1===0);
14.   
15.  // good
16.  const bar =(a ** b)-(5% d);
17.   
18.  // good
19.  if((a || b)&& c){
20.      return d;
21.  }
22.   
23.  // good
24.   const bar = a + b / c * d;

代码块 Blocks


– 
16.1 使用大括号包裹所有的多行代码块。 eslint: nonblock-statement-body-position

1.  // bad
2. if(test)
3.     returnfalse;
4.  
5. // good
6. if(test)returnfalse;
7.  
8. // good
9. if(test){
10.      returnfalse;
11.  }
12.   
13.  // bad
14.  function foo(){returnfalse;}
15.   
16.  // good
17.  function bar(){
18.      returnfalse;
19.  }


– 
16.2 如果通过 if  else 使用多行代码块,把 else 放在 if 代码块闭合括号的同一行。eslint: brace-style jscs: disallowNewlineBeforeBlockStatements

1. // bad
2. if(test){
3.     thing1();
4.     thing2();
5. }
6. else{
7.     thing3();
8. }
9.  
10.  // good
11.  if(test){
12.      thing1();
13.      thing2();
14.  }else{
15.      thing3();
16.  }


– 
16.3 如果一个 if 块总是执行一个 return 语句,后面的 else 块是不必要的。在 else if 块中的 return,可以分成多个 if 块来 return eslint: no-else-return

1. // bad
2. function foo(){
3.     if(x){
4.        return x;
5.     }else{
6.        return y;
7.     }
8. }
9.  
10.  // bad
11.  function cats(){
12.      if(x){
13.          return x;
14.      }elseif(y){
15.          return y;
16.      }
17.  }
18.  // bad
19.  function dogs(){
20.      if(x){
21.          return x;
22.      }else{
23.          if(y){
24.              return y;
25.          }
26.      }
27.  }
28.  // good
29.  function foo(){
30.      if(x){
31.         return x;
32.      }
33.   
34.      return y;
35.  }
36.  // good
37.  function cats(){
38.      if(x){
39.        return x;
40.      }
41.   
42.      if(y){
43.        return y;
44.      }
45.  }
46.  //good
47.  function dogs(x){
48.      if(x){
49.         if(z){
50.            return y;
51.         }
52.      }else{
53.         return z;
54.      }
55.  }

控制语句 Control Statements

  • 17.1 如果您的控制语句(ifwhile 的)太长或超过最大行长度,那么每个(分组)条件可以放单独一行。逻辑运算符应该放在每行起始处。

为什么?在每行起始处要求运算符可以使运算符保持一致,并遵循与方法链式调用类似的模式。这样可以使复杂逻辑更易于查看,以提高可读性。

1. // bad
2. if((foo ===123|| bar ==='abc')&& doesItLookGoodWhenItBecomesThatLong()&& isThisReallyHappening()){
3.     thing1();
4. }
5.  
6. // bad
7. if(foo ===123&& bar ==='abc'){
8.     thing1();
9. }
10.   
11.  // bad
12.  if(foo ===123&& bar ==='abc'){
13.      thing1();
14.  }
15.   
16.  // bad
17.  if(foo ===123&& bar ==='abc'){
18.      thing1();
19.  }
20.   
21.  // good
22.  if(
23.      foo ===123
24.      && bar ==='abc'
25.  ){
26.      thing1();
27.  }
28.   
29.  // good
30.  if(
31.      (foo ===123|| bar ==="abc")
32.      && doesItLookGoodWhenItBecomesThatLong()
33.      && isThisReallyHappening()
34.  ){
35.      thing1();
36.  }
37.   
38.  // good
39.  if(foo ===123&& bar ==='abc'){
40.      thing1();
41.  }

注释 Comments


– 
18.1 多行注释使用 /** ... */

1. // bad
2. // make() returns a new element
3. // based on the passed in tag name
4. //
5. // @param {String} tag
6. // @return {Element} element
7. function make(tag){
8.  
9.     // ...
10.   
11.      return element;
12.  }
13.   
14.  // good
15.  /**
16.      * make() returns a new element
17.      * based on the passed-in tag name
18.      */
19.  function make(tag){
20.   
21.      // ...
22.   
23.      return element;
24.  }


– 
18.2 单行注释使用 // 。将单行注释放在续注释的语句上方。在注释之前放置一个空行,除非它位于代码块的第一行。

 

1. // bad
2. const active =true;  // is current tab
3.  
4. // good
5. // is current tab
6. const active =true;
7.  
8. // bad
9. function getType(){
10.      console.log('fetching type...');
11.      // set the default type to 'no type'
12.      const type =this.type ||'no type';
13.   
14.      return type;
15.  }
16.   
17.  // good
18.  function getType(){
19.      console.log('fetching type...');
20.   
21.      // set the default type to 'no type'
22.      const type =this.type ||'no type';
23.   
24.      return type;
25.  }
26.   
27.  // also good
28.  function getType(){
29.      // set the default type to 'no type'
30.      const type =this.type ||'no type';
31.   
32.      return type;
33.  }


– 
18.3 所有注释符和注释内容用一个空格隔开,让它更容易阅读。 eslint: spaced-comment

1. // bad
2. //is current tab
3. const active =true;
4.  
5. // good
6. // is current tab
7. const active =true;
8.  
9. // bad
10.  /**
11.      *make() returns a new element
12.      *based on the passed-in tag name
13.      */
14.  function make(tag){
15.   
16.      // ...
17.   
18.      return element;
19.  }
20.   
21.  // good
22.  /**
23.      * make() returns a new element
24.      * based on the passed-in tag name
25.      */
26.  function make(tag){
27.   
28.      // ...
29.   
30.      return element;
31.  }


– 
18.4 给注释增加 FIXME  TODO 的前缀,可以帮助其他开发者快速了解这个是否是一个需要重新复查的问题,或是你正在为需要解决的问题提出解决方案。这将有别于常规注释,因为它们是可操作的。使用 FIXME -- need to figure this out 或者 TODO -- need to implement


– 
18.5 使用 // FIXME: 来标识需要修正的问题。愚人码头注:如果代码中有该标识,说明标识处代码需要修正,甚至代码是错误的,不能工作,需要修复,如何修正会在说明中简略说明。

1. classCalculatorextendsAbacus{
2.     constructor(){
3.        super();
4.        // FIXME: shouldn’t use a global here
5.        total =0;
6.     }
7. }


– 
18.6 使用 // TODO: 来标识需要实现的问题。愚人码头注:如果代码中有该标识,说明在标识处有功能代码待编写,待实现的功能在说明中会简略说明。

1. classCalculatorextendsAbacus{
2.     constructor(){
3.        super();
4.  
5.     // TODO: total should be configurable by an options param
6.        this.total =0;
7.     }
8. }

愚人码头注:还有 // XXX: 注释,如果代码中有该标识,说明标识处代码虽然实现了功能,但是实现的方法有待商榷,希望将来能改进,要改进的地方会在说明中简略说明。部分 IDE 有这些注释的收集视图,例如任务(task)视图,TODO视图等,在项目发布前,检查一下任务视图是一个很好的习惯。

空白 Whitespace


– 
19.1 使用 2 个空格作为缩进。 eslint: indent jscs: validateIndentation

1. // bad
2. function foo(){
3. ∙∙∙∙let name;
4. }
5.  
6. // bad
7. function bar(){
8. let name;
9. }
10.   
11.  // good
12.  function baz(){
13.  ∙∙let name;
14.  }


– 
19.2 在大括号前放置 1 个空格。eslint: space-before-blocks jscs: requireSpaceBeforeBlockStatements

 

1. // bad
2. function test(){
3.     console.log('test');
4. }
5.  
6. // good
7. function test(){
8.     console.log('test');
9. }
10.   
11.  // bad
12.  dog.set('attr',{
13.      age:'1 year',
14.      breed:'Bernese Mountain Dog',
15.  });
16.   
17.  // good
18.  dog.set('attr',{
19.      age:'1 year',
20.      breed:'Bernese Mountain Dog',
21.  });


– 
19.3 在控制语句(ifwhile 等)的小括号前放一个空格。在函数调用及声明中,不在函数的参数列表前加空格。 eslint: keyword-spacing jscs: requireSpaceAfterKeywords

1. // bad
2. if(isJedi){
3.     fight ();
4. }
5.  
6. // good
7. if(isJedi){
8.     fight();
9. }
10.   
11.  // bad
12.  function fight (){
13.      console.log ('Swooosh!');
14.  }
15.   
16.  // good
17.  function fight(){
18.      console.log('Swooosh!');
19.  }


– 
19.4 使用空格把运算符隔开。 eslint: space-infix-ops jscs: requireSpaceBeforeBinaryOperatorsrequireSpaceAfterBinaryOperators

1. // bad
2. const x=y+5;
3.  
4. // good
5. const x = y +5;


– 
19.5 在文件末尾插入一个空行。 eslint: eol-last

1. // bad
2. import{ es6 }from'./AirbnbStyleGuide';
3.     // ...
4. exportdefault es6;
1. // bad
2. import{ es6 }from'./AirbnbStyleGuide';
3.     // ...
4. exportdefault es6;
5. 
1. // good
2. import{ es6 }from'./AirbnbStyleGuide';
3.     // ...
4. exportdefault es6;


– 
19.6 长方法链式调用时使用缩进(2个以上的方法链式调用)。使用一个点 . 开头,强调该行是一个方法调用,不是一个新的声明。eslint: newline-per-chained-callno-whitespace-before-property

1. // bad
2. $('#items').find('.selected').highlight().end().find('.open').updateCount();
3.  
4. // bad
5. $('#items').
6.     find('.selected').
7.     highlight().
8.     end().
9.     find('.open').
10.      updateCount();
11.   
12.  // good
13.  $('#items')
14.      .find('.selected')
15.      .highlight()
16.      .end()
17.      .find('.open')
18.      .updateCount();
19.   
20.  // bad
21.  const leds = stage.selectAll('.led').data(data).enter().append('svg:svg').classed('led',true)
22.      .attr('width',(radius + margin)*2).append('svg:g')
23.      .attr('transform',`translate(${radius + margin},${radius + margin})`)
24.      .call(tron.led);
25.   
26.  // good
27.  const leds = stage.selectAll('.led')
28.      .data(data)
29.      .enter().append('svg:svg')
30.      .classed('led',true)
31.      .attr('width',(radius + margin)*2)
32.      .append('svg:g')
33.      .attr('transform',`translate(${radius + margin},${radius + margin})`)
34.      .call(tron.led);
35.   
36.  // good
37.  const leds = stage.selectAll('.led').data(data);


– 
19.7 在语句块后和下条语句前留一个空行。jscs: requirePaddingNewLinesAfterBlocks

1. // bad
2. if(foo){
3.     return bar;
4. }
5. return baz;
6.  
7. // good
8. if(foo){
9.     return bar;
10.  }
11.  return baz;
12.  // bad
13.  const obj ={
14.      foo(){
15.      },
16.      bar(){
17.      },
18.  };
19.  return obj;
20.  // good
21.  const obj ={
22.      foo(){
23.      },
24.      bar(){
25.      },
26.  };
27.  return obj;
28.  // bad
29.  const arr =[
30.      function foo(){
31.      },
32.      function bar(){
33.      },
34.  ];
35.  return arr;
36.  // good
37.  const arr =[
38.      function foo(){},
39.      function bar(){},
40.  ];
41.   
42.  return arr;


– 
19.8 不要用空行来填充块。 eslint: padded-blocks jscs: disallowPaddingNewlinesInBlocks

 

 

1.   
2. // bad
3. function bar(){
4.  
5.     console.log(foo);
6.  
7. }
8.  
9. // bad
10.  if(baz){
11.   
12.      console.log(qux);
13.  }else{
14.      console.log(foo);
15.   
16.  }
17.   
18.  // bad
19.  classFoo{
20.   
21.      constructor(bar){
22.         this.bar = bar;
23.      }
24.  }
25.   
26.  // good
27.  function bar(){
28.      console.log(foo);
29.  }
30.   
31.  // good
32.  if(baz){
33.      console.log(qux);
34.  }else{
35.      console.log(foo);
36.  }


– 
19.9 不要在圆括号内加空格。 eslint: space-in-parens jscs: disallowSpacesInsideParentheses

1. // bad
2. function bar( foo ){
3.     return foo;
4. }
5.  
6. // good
7. function bar(foo){
8.     return foo;
9. }
10.   
11.  // bad
12.  if( foo ){
13.      console.log(foo);
14.  }
15.   
16.  // good
17.  if(foo){
18.      console.log(foo);
19.  }


– 
19.10 不要在中括号内添加空格。 eslint: array-bracket-spacing jscs: disallowSpacesInsideArrayBrackets

1. // bad
2. const foo =[1,2,3];
3. console.log(foo[0]);
4.  
5. // good
6. const foo =[1,2,3];
7. console.log(foo[0]);


– 
19.11 在大括号内添加空格。 eslint: object-curly-spacing jscs: requireSpacesInsideObjectBrackets

1. // bad
2. const foo ={clark:'kent'};
3.  
4. // good
5. const foo ={ clark:'kent'};


– 
19.12 避免有超过100个字符(包括空格)的代码行。注意:根据上面的规则,长字符串可以免除这个规则,不应该被破坏。eslint: max-len jscs: maximumLineLength

为什么?这可以确保可读性和可维护性。

1. // bad
2. const foo = jsonData && jsonData.foo && jsonData.foo.bar && jsonData.foo.bar.baz && jsonData.foo.bar.baz.quux && jsonData.foo.bar.baz.quux.xyzzy;
3.  
4. // bad
5. $.ajax({ method:'POST', url:'https://airbnb.com/', data:{ name:'John'}}).done(()=> console.log('Congratulations!')).fail(()=> console.log('You have failed this city.'));
6.  
7. // good
8. const foo = jsonData
9.     && jsonData.foo
10.      && jsonData.foo.bar
11.      && jsonData.foo.bar.baz
12.      && jsonData.foo.bar.baz.quux
13.      && jsonData.foo.bar.baz.quux.xyzzy;
14.   
15.  // good
16.  $.ajax({
17.      method:'POST',
18.      url:'https://airbnb.com/',
19.      data:{ name:'John'},
20.  })
21.      .done(()=> console.log('Congratulations!'))
22.      .fail(()=> console.log('You have failed this city.'));

逗号 Commas


– 
20.1 行开头处不要实用使用逗号。 eslint: comma-style jscs: requireCommaBeforeLineBreak

1. // bad
2. const story =[
3.     once
4.     , upon
5.     , aTime
6. ];
7.  
8. // good
9. const story =[
10.      once,
11.      upon,
12.      aTime,
13.  ];
14.   
15.  // bad
16.  const hero ={
17.      firstName:'Ada'
18.      , lastName:'Lovelace'
19.      , birthYear:1815
20.      , superPower:'computers'
21.  };
22.   
23.  // good
24.  const hero ={
25.      firstName:'Ada',
26.      lastName:'Lovelace',
27.      birthYear:1815,
28.      superPower:'computers',
29.  };


– 
20.2 添加结尾的逗号。 eslint: comma-dangle jscs: requireTrailingComma

为什么?这会让 git diff(差异比较) 更干净。另外,像Babel这样的转译器会删除转译后代码中的结尾逗号,这意味着您不必担心传统浏览器中的结尾逗号问题

1. // bad - 没有结尾逗号的 git diff 差异比较
2. const hero ={
3.         firstName:'Florence',
4. -    lastName:'Nightingale'
5. +    lastName:'Nightingale',
6. +    inventorOf:['coxcomb chart','modern nursing']
7. };
8. // good - 有结尾逗号的 git diff 差异比较
9. const hero ={
10.          firstName:'Florence',
11.          lastName:'Nightingale',
12.  +    inventorOf:['coxcomb chart','modern nursin'],
13.  };
1. // bad
2. const hero ={
3.     firstName:'Dana',
4.     lastName:'Scully'
5. };
6.  
7. const heroes =[
8.     'Batman',
9.     'Superman'
10.  ];
11.   
12.  // good
13.  const hero ={
14.      firstName:'Dana',
15.      lastName:'Scully',
16.  };
17.   
18.  const heroes =[
19.      'Batman',
20.      'Superman',
21.  ];
22.   
23.  // bad
24.  function createHero(
25.      firstName,
26.      lastName,
27.      inventorOf
28.  ){
29.      // does nothing
30.  }
31.   
32.  // good
33.  function createHero(
34.      firstName,
35.      lastName,
36.      inventorOf,
37.  ){
38.      // does nothing
39.  }
40.   
41.  // good (请注意,逗号不能出现在 “rest” 元素的后面)
42.  function createHero(
43.      firstName,
44.      lastName,
45.      inventorOf,
46.      ...heroArgs
47.  ){
48.      // does nothing
49.  }
50.   
51.  // bad
52.  createHero(
53.      firstName,
54.      lastName,
55.      inventorOf
56.  );
57.   
58.  // good
59.  createHero(
60.      firstName,
61.      lastName,
62.      inventorOf,
63.  );
64.   
65.  // good (请注意,逗号不能出现在 “rest” 元素的后面)
66.  createHero(
67.      firstName,
68.      lastName,
69.      inventorOf,
70.      ...heroArgs
71.  );

分号 Semicolons


– 
21.1 当然要使用封号 eslint: semi jscs: requireSemicolons

为什么? JavaScript 遇到没有分号的换行符时,它使用一组称为自动分号插入的规则来确定是否应该将换行符视为语句的结尾,并且(顾名思义)如果被这样认为的话,在换行符前面自动插入一个分号。ASI(自动分号插入)包含了一些稀奇古怪的的行为,不过,如果 JavaScript 错误地解释了你的换行符,你的代码将会被中断执行。随着新功能成为 JavaScript 的一部分,这些规则将变得更加复杂。明确地结束你的语句并配置你的 linter 来捕获缺少的分号,将有助于防止遇到问题。

 

 

1. // bad - 引发异常
2. const luke ={}
3. const leia ={}
4. [luke, leia].forEach(jedi => jedi.father ='vader')
5.  
6. // bad - 引发异常
7. const reaction ="No! That's impossible!"
8. (asyncfunction meanwhileOnTheFalcon(){
9.     // handle `leia`, `lando`, `chewie`, `r2`, `c3p0`
10.      // ...
11.  }())
12.   
13.  // bad - 返回`undefined`,而不是下一行的值 -  `return` 独占一行时,自动分号插入总是会发生。
14.  function foo(){
15.      return
16.      'search your feelings, you know it to be foo'
17.  }
18.   
19.  // good
20.  const luke ={};
21.  const leia ={};
22.  [luke, leia].forEach((jedi)=>{
23.      jedi.father ='vader';
24.  });
25.   
26.  // good
27.  const reaction ="No! That's impossible!";
28.  (asyncfunction meanwhileOnTheFalcon(){
29.      // handle `leia`, `lando`, `chewie`, `r2`, `c3p0`
30.      // ...
31.  }());
32.   
33.  // good
34.  function foo(){
35.      return'search your feelings, you know it to be foo';
36.  }

类型转换 Type Casting &Coercion


– 
22.1 在声明语句的开始处就执行强制类型转换.


– 
22.2 字符串: eslint: no-new-wrappers

1. // => this.reviewScore = 9;
2.  
3. // bad
4. const totalScore =newString(this.reviewScore);// typeof totalScore  "object" 而不是 "string"
5.  
6. // bad
7. const totalScore =this.reviewScore +'';// 调用 this.reviewScore.valueOf()
8.  
9. // bad
10.  const totalScore =this.reviewScore.toString();// 不能保证返回一个字符串
11.   
12.  // good
13.  const totalScore =String(this.reviewScore);


– 
22.3 数字: 使用 Number 进行转换,而 parseInt 则始终以基数解析字串。 eslint: radixno-new-wrappers

1. const inputValue ='4';
2.  
3. // bad
4. const val =newNumber(inputValue);
5.  
6. // bad
7. const val =+inputValue;
8.  
9. // bad
10.  const val = inputValue >>0;
11.   
12.  // bad
13.  const val = parseInt(inputValue);
14.   
15.  // good
16.  const val =Number(inputValue);
17.   
18.  // good
19.  const val = parseInt(inputValue,10);


– 
22.4 如果你因为某个原因正在做些疯狂的事情,但是 parseInt 是你的瓶颈,所以你对于 性能方面的原因而必须使用位运算,请留下评论并解释为什么使用,及你做了哪些事情。

1. // good
2. /**
3.     * parseInt was the reason my code was slow.
4.     * Bitshifting the String to coerce it to a
5.     * Number made it a lot faster.
6.     */
7. const val = inputValue >>0;


– 
22.5 注意: 使用位运算请小心。数字使用 64位值表示, 但是位运算只返回32位整数 (来源)小于32位整数的位运算会导致不可预期的行为讨论。最大的有符号整数是 2,147,483,647:

1. 2147483647>>0;// => 2147483647
2. 2147483648>>0;// => -2147483648
3. 2147483649>>0;// => -2147483647


– 
22.6 布尔值: eslint: no-new-wrappers

1. const age =0;
2.  
3. // bad
4. const hasAge =newBoolean(age);
5.  
6. // good
7. const hasAge =Boolean(age);
8.  
9. // best
10.  const hasAge =!!age;

命名规则 Naming Conventions


– 
23.1 避免使用单字母名称。使你的命名具有描述性。 eslint: id-length

1. // bad
2. function q(){
3.     // ...
4. }
5.  
6. // good
7. function query(){
8.     // ...
9. }


– 
23.2 当命名对象,函数和实例时使用驼峰式命名。 eslint: camelcase jscs: requireCamelCaseOrUpperCaseIdentifiers

1. // bad
2. constOBJEcttsssss={};
3. const this_is_my_object ={};
4. function c(){}
5.  
6. // good
7. const thisIsMyObject ={};
8. function thisIsMyFunction(){}


– 
23.3 当命名构造函数或类的时候使用 PascalCase 式命名,(愚人码头注:即单词首字母大写)。 eslint: new-cap jscs: requireCapitalizedConstructors

1. // bad
2. function user(options){
3.     this.name = options.name;
4. }
5. const bad =new user({
6.     name:'nope',
7. });
8. // good
9. classUser{
10.      constructor(options){
11.         this.name = options.name;
12.      }
13.  }
14.   
15.  const good =newUser({
16.      name:'yup',
17.  });


– 
23.4 不要使用下划线开头或结尾。 eslint: no-underscore-dangle jscs: disallowDanglingUnderscores

为什么? JavaScript 对于属性或方法而言并没有私有的概念。虽然下划线开头通常意味着 ‘private’(私有)是通用的惯例,事实上,这些属性是完全公开的,是公开API的一部分。这个惯例可能会导致开发人员错误地认为这不重要或者测试也不必要。简而言之:如果你想让其 “private”, 必须使其不可见。

1. // bad
2. this.__firstName__ ='Panda';
3. this.firstName_ ='Panda';
4. this._firstName ='Panda';
5.  
6. // good
7. this.firstName ='Panda';


– 
23.5 不要存储 this 引用。请实用箭头函数或者 Function#bind jscs: disallowNodeTypes

1. // bad
2. function foo(){
3.     constself=this;
4.     returnfunction(){
5.        console.log(self);
6.     };
7. }
8.  
9. // bad
10.  function foo(){
11.      const that =this;
12.      returnfunction(){
13.         console.log(that);
14.      };
15.  }
16.   
17.  // good
18.  function foo(){
19.      return()=>{
20.          console.log(this);
21.      };
22.  }


– 
23.6 basename 应与其默认导出的名称正好匹配。(愚人码头注:basename 指的是文件名)

1. // file 1 contents
2. classCheckBox{
3.     // ...
4. }
5. exportdefaultCheckBox;
6.  
7. // file 2 contents
8. exportdefaultfunction fortyTwo(){return42;}
9.  
10.  // file 3 contents
11.  exportdefaultfunction insideDirectory(){}
12.   
13.  // in some other file
14.  // bad
15.  importCheckBoxfrom'./checkBox';// import/export 单词首字母大写命名 , filename 驼峰式命名
16.  importFortyTwofrom'./FortyTwo';// import/filename 单词首字母大写命名, export 驼峰式命名
17.  importInsideDirectoryfrom'./InsideDirectory';// import/filename 单词首字母大写命名, export 驼峰式命名
18.   
19.  // bad
20.  importCheckBoxfrom'./check_box';// import/export 单词首字母大写命名, filename 下划线命名
21.  import forty_two from'./forty_two';// import/filename 下划线命名, export 驼峰式命名
22.  import inside_directory from'./inside_directory';// import 下划线命名, export 驼峰式命名
23.  import index from'./inside_directory/index';// 明确地 require 索引文件
24.  import insideDirectory from'./insideDirectory/index';//  明确地 require 索引文件
25.   
26.  // good
27.  importCheckBoxfrom'./CheckBox';// export/import/filename 单词首字母大写命名
28.  import fortyTwo from'./fortyTwo';// export/import/filename 驼峰式命名
29.  import insideDirectory from'./insideDirectory';// camelCase export/import/directory name/implicit "index"
30.  // ^ supports both insideDirectory.js and insideDirectory/index.js


– 
23.7 导出(export) 一个默认函数时使用驼峰式命名。你的文件名应该和你的函数的名字一致。

1. function makeStyleGuide(){
2.     // ...
3. }
4.  
5. exportdefault makeStyleGuide;


– 
23.8 当导出一个构造函数 / / 单例 / 函数库 / 纯对象时使用 PascalCase 式命名,(愚人码头注:即单词首字母大写)。

1. constAirbnbStyleGuide={
2.     es6:{
3.     },
4. };
5.  
6. exportdefaultAirbnbStyleGuide;


– 
23.9 首字母缩写词应该总是全部大写,或全部小写。

为什么?名字是更具可读性,而不是为了满足计算机算法。

1. // bad
2. importSmsContainerfrom'./containers/SmsContainer';
3.  
4. // bad
5. constHttpRequests=[
6.     // ...
7. ];
8.  
9. // good
10.  importSMSContainerfrom'./containers/SMSContainer';
11.   
12.  // good
13.  constHTTPRequests=[
14.      // ...
15.  ];
16.   
17.  // also good
18.  const httpRequests =[
19.      // ...
20.  ];
21.   
22.  // best
23.  importTextMessageContainerfrom'./containers/TextMessageContainer';
24.   
25.  // best
26.  const requests =[
27.      // ...
28.  ];

存取器 Accessors


– 
24.1 属性的存取器函数不是必须的。


– 
24.2 別使用 JavaScript getters/setters,因为它们会导致意想不到的副作用,而且很难测试,维护和理解。相反,如果要使用存取器函数,使用 getVal() setVal(‘hello’)

1. // bad
2. classDragon{
3.     get age(){
4.     // ...
5.     }
6.  
7.     set age(value){
8.     // ...
9.     }
10.  }
11.   
12.  // good
13.  classDragon{
14.      getAge(){
15.      // ...
16.      }
17.   
18.      setAge(value){
19.      // ...
20.      }
21.  }


– 
24.3 如果属性/方法是一个 boolean, 使用 isVal()  hasVal() 方法。

1. // bad
2. if(!dragon.age()){
3.     returnfalse;
4. }
5.  
6. // good
7. if(!dragon.hasAge()){
8.     returnfalse;
9. }


– 
24.4 也可以创建 get() set() 函数, 但要保持一致。

1. classJedi{
2.     constructor(options ={}){
3.       const lightsaber = options.lightsaber ||'blue';
4.       this.set('lightsaber', lightsaber);
5.     }
6.  
7.     set(key, val){
8.       this[key]= val;
9.     }
10.   
11.     get(key){
12.       returnthis[key];
13.     }
14.  }

事件 Events


– 
25.1 将绑定数据到事件时 (不论是 DOM 事件还是其他像Backbone一类的事件), 传递 hash 而不是原始值。这将允许后续的贡献者不用查找和更新事件的每一个处理程序就可以给事件添加更多的数据。例如,不要使用下边的:

1. // bad
2. $(this).trigger('listingUpdated', listing.id);
3.  
4. // ...
5.  
6. $(this).on('listingUpdated',(e, listingId)=>{
7.     // do something with listingId
8. });

prefer:

1. // good
2. $(this).trigger('listingUpdated',{ listingId: listing.id });
3.  
4. // ...
5.  
6. $(this).on('listingUpdated',(e, data)=>{
7.     // do something with data.listingId
8. });

jQuery


– 
26.1 jQuery 对象变量命名以 $ 为前缀。 jscs: requireDollarBeforejQueryAssignment

1. // bad
2. const sidebar = $('.sidebar');
3.  
4. // good
5. const $sidebar = $('.sidebar');
6.  
7. // good
8. const $sidebarBtn = $('.sidebar-btn');


– 
26.2 缓存 jQuery 选择器的查询结果。

1. // bad
2. function setSidebar(){
3.     $('.sidebar').hide();
4.  
5.     // ...
6.  
7.     $('.sidebar').css({
8.         'background-color':'pink',
9.     });
10.  }
11.   
12.  // good
13.  function setSidebar(){
14.      const $sidebar = $('.sidebar');
15.      $sidebar.hide();
16.   
17.      // ...
18.   
19.      $sidebar.css({
20.          'background-color':'pink',
21.      });
22.  }


– 
26.3 DOM 查询使用后代选择器 $('.sidebar ul') 或者父类 > 子类 $('.sidebar &gt; ul')选择器。jsPerf


– 
26.4 在某个 jQuery 对象范围内查询使用 find 

1. // bad
2. $('ul','.sidebar').hide();
3.  
4. // bad
5. $('.sidebar').find('ul').hide();
6.  
7. // good
8. $('.sidebar ul').hide();
9.  
10.  // good
11.  $('.sidebar > ul').hide();
12.   
13.  // good
14.  $sidebar.find('ul').hide();

ECMAScript 5 兼容性Compatibility


– 
27.1 参考 Kangax  ES5 compatibility table.

ECMAScript 6+ (ES 2015+) 编码风格


– 
28.1 这是一个各种 ES6+ 新特性的链接集合。

1.   Arrow Functions

2.   Classes

3.   Object Shorthand

4.   Object Concise

5.   Object Computed Properties

6.   Template Strings

7.   Destructuring

8.   Default Parameters

9.   Rest

10.  Array Spreads

11.  Let and Const

12.  Exponentiation Operator

13.  Iterators and Generators

14.  Modules


– 
28.2 不要使用 TC39 proposals 还未实现的 stage3 的功能。

为什么?他们没有最终确定,他们可能会改变或完全撤回。我们想要使用JavaScript,而且建议性的提案还不是 JavaScript

标准库 Standard Library

标准库包含有问题,但由于历史遗留问题而保留下来的功能。


– 
29.1 使用 Number.isNaN 代替全局 isNaN eslint: no-restricted-globals

为什么?全局的 isNaN 方法会将非数字转换为数字, 任何被转换为 NaN 的东西都会返回 true
如果需要这种行为,请明确使用。

1. // bad
2. isNaN('1.2');// false
3. isNaN('1.2.3');// true
4.  
5. // good
6. Number.isNaN('1.2.3');// false
7. Number.isNaN(Number('1.2.3'));// true


– 
29.2 使用 Number.isFinite 代替全局 isFinite eslint: no-restricted-globals

为什么?全局的 isFinite 方法会将非数字转换为数字, 任何被转换为有限大的数字都会返回 true
如果需要这种行为,请明确使用。

1. // bad
2. isFinite('2e3');// true
3.  
4. // good
5. Number.isFinite('2e3');// false
6. Number.isFinite(parseInt('2e3',10));// true

测试


– 
30.1 是的。

1. function foo(){
2.     returntrue;
3. }


– 
30.2 不要这么做,很严重:
不论你用哪一个测试框架,都应该写测试用例!
尽力写一些简单的纯函数, 并尽量减可变性发生的地方。
谨慎的使用 stubs mocks – 它们会使测试变得脆弱.
我们主要在 Airbnb 中主要使用 mocha  tape 也偶尔会用于小型独立模块。
– 100%
的测试覆盖率是很好的追求目标,即使它并不总是实际可行的。
每当你修复一个 bug ,写一个回归测试。未经回归测试的bug修复几乎会在将在再次出现.

React编码规范

基本规范

·        原则上每个文件只写一个组件, 多个无状态组件可以放在单个文件中. eslint: react/no-multi-comp.

·        推荐使用 JSX 语法编写 React 组件, 而不是 React.createElement

创建组件的三种方式

·        如果你的组件有内部状态或者是refs, 推荐使用 classextends React.Component 而不是 React.createClass.
eslint: react/prefer-es6-class react/prefer-stateless-function

·        // bad

·        constListing=React.createClass({

·          // ...

·          render(){

·            return<div>{this.state.hello}</div>;

·          }

·        });

·        // good

·        classListingextendsReact.Component{

·          // ...

·          render(){

·            return<div>{this.state.hello}</div>;

·          }

·        }

如果你的组件没有状态或是没有引用refs, 推荐使用普通函数(非箭头函数)而不是类:

// bad

classListingextendsReact.Component{

  render(){

    return<div>{this.props.hello}</div>;

  }

}

 

// bad (relying onfunction name inference is discouraged)

constListing=({ hello })=>(

  <div>{hello}</div>

);

 

// good

functionListing({ hello }){

  return<div>{hello}</div>;

}

Mixins

·        不要使用 mixins.

因为 Mixins 会增加隐式依赖, 导致命名冲突, 并且会增加复杂度.大多数情况下, 可以更好的方法代替 Mixins, 如:组件化, 高阶组件, 工具组件等.

命名

·        扩展名: React 组件使用 .jsx 扩展名.
 - 文件名: 文件名使用帕斯卡命名. 如, ReservationCard.jsx.
 - 引用命名: React组件名使用帕斯卡命名, 实例使用骆驼式命名. eslint: react/jsx-pascal-case

·        // bad

·        importreservationCard from'./ReservationCard';

·         

·        // good

·        importReservationCardfrom'./ReservationCard';

·         

·        // bad

·        constReservationItem=<ReservationCard/>;

·         

·        // good

·        constreservationItem =<ReservationCard/>;

·        组件命名: 组件名与当前文件名一致. 如 ReservationCard.jsx 应该包含名为 ReservationCard的组件. 如果整个目录是一个组件, 使用 index.js作为入口文件, 然后直接使用 index.js 或者目录名作为组件的名称:

·        // bad

·        importFooterfrom'./Footer/Footer';

·         

·        // bad

·        importFooterfrom'./Footer/index';

·         

·        // good

·        importFooterfrom'./Footer';

·        高阶组件命名: 生成一个新的组件时, 其中的组件名 displayName 应该为高阶组件名和传入组件名的组合. 例如, 高阶组件 withFoo(),当传入一个 Bar 组件的时候, 生成的组件名 displayName 应该为 withFoo(Bar).

因为一个组件的 displayName 可能在调试工具或错误信息中使用到. 清晰合理的命名, 能帮助我们更好的理解组件执行过程, 更好的 Debug.

// bad

exportdefaultfunction withFoo(WrappedComponent){

  returnfunctionWithFoo(props){

    return<WrappedComponent{...props} foo />;

  }

}

 

// good

exportdefaultfunction withFoo(WrappedComponent){

  functionWithFoo(props){

    return<WrappedComponent{...props} foo />;

  }

  const wrappedComponentName =WrappedComponent.displayName

    ||WrappedComponent.name

    ||'Component';

 

  WithFoo.displayName =`withFoo(${wrappedComponentName})`;

  returnWithFoo;

}

·        属性命名: 避免使用DOM 相关的属性来用命名自定义属性

因为对于style 和 className 这样的属性名, 我们都会默认它们代表一些特殊的含义.

// bad

<MyComponent style="fancy"/>

 

// good

<MyComponent variant="fancy"/>

声明组件

·        不要使用 displayName 来命名React 组件, 而是使用引用来命名组件, 如 class 名称.

·        // bad

·        exportdefaultReact.createClass({

·          displayName:'ReservationCard',

·          // stuff goes here

·        });

·         

·        // good

·        exportdefaultclassReservationCardextendsReact.Component{}

代码缩进

·        遵循以下的 JSX 语法缩进/格式. eslint: react/jsx-closing-bracket-location

·        // bad

·        <Foo superLongParam="bar"

·             anotherSuperLongParam="baz"/>

·         

·        // good, 有多行属性的话, 新建一行关闭标签

·        <Foo

·          superLongParam="bar"

·          anotherSuperLongParam="baz"

·        />

·         

·        // 若能在一行中显示, 直接写成一行

·        <Foo bar="bar"/>

·         

·        // 子元素按照常规方式缩进

·        <Foo

·          superLongParam="bar"

·          anotherSuperLongParam="baz"

·        > 

·          <Quux/>

·        </Foo>

使用双引号

·        对于 JSX 属性值总是使用双引号("), 其他均使用单引号('). eslint: jsx-quotes

因为 HTML 属性也是用双引号, 因此 JSX 的属性也遵循此约定.

// bad

<Foo bar='bar'/>

 

// good

<Foo bar="bar"/>

 

// bad

<Foo style={{ left:"20px"}}/>

// good

<Foo style={{ left:'20px'}}/>

空格

·        总是在自动关闭的标签前加一个空格, 正常情况下不需要换行.eslint: no-multi-spacesreact/jsx-tag-spacing

·        // bad

·        <Foo/>

·        // very bad

·        <Foo                 />

·        // bad

·        <Foo

·         />

·        // good

·        <Foo/>

·        不要在JSX {} 引用括号里两边加空格.eslint: react/jsx-curly-spacing

·        // bad

·        <Foo bar={ baz }/>

·         

·        // good

·        <Foo bar={baz}/>

属性

·        JSX 属性名使用骆驼式风格camelCase.

·        // bad

·        <Foo

·          UserName="hello"

·          phone_number={12345678}

·        />

·         

·        // good

·        <Foo

·          userName="hello"

·          phoneNumber={12345678}

·        />

·        如果属性值为 true, 可以直接省略.eslint: react/jsx-boolean-value

·        // bad

·        <Foo hidden={true}/>

·         

·        // good

·        <Foo hidden />

·        <img> 标签总是添加 alt 属性. 如果图片以陈述方式显示, alt 可为空, 或者<img> 要包含role="presentation".eslint: jsx-a11y/alt-text

·        // bad

·        <imgsrc="hello.jpg"/>

·         

·        // good

·        <imgsrc="hello.jpg" alt="Me wavinghello"/>

·         

·        // good

·        <imgsrc="hello.jpg" alt=""/>

·         

·        // good

·        <imgsrc="hello.jpg" role="presentation"/>

·        不要在 alt 值里使用如"image", "photo", or "picture" 包括图片含义这样的词, 中文同理. eslint: jsx-a11y/img-redundant-alt

因为屏幕助读器已经把 img 标签标注为图片了, 所以没有必要再在 alt 里重复说明.

// bad

<img src="hello.jpg" alt="Picture of mewaving hello"/>

 

// good

<img src="hello.jpg" alt="Me wavinghello"/>

·        使用有效正确的 aria role属性值 ARIA roles. eslint: jsx-a11y/aria-role

·        // bad - not an ARIA role

·        <divrole="datepicker"/>

·         

·        // bad - abstract ARIA role

·        <divrole="range"/>

·         

·        // good

·        <divrole="button"/>

·        不要在标签上使用 accessKey 属性.eslint: jsx-a11y/no-access-key

因为屏幕助读器在键盘快捷键与键盘命令时造成的不统一性会导致阅读性更加复杂.

// bad

<div accessKey="h"/>

 

// good

<div />

·        避免使用数组的 index 来作为属性key的值, 推荐使用唯一ID. (Index as a key is an anti pattern)

·        // bad

·        {todos.map((todo, index)=>

·        <Todo

·          {...todo}

·          key={index}

·        />

·        )}

·         

·        // good

·        {todos.map(todo =>(

·        <Todo

·          {...todo}

·          key={todo.id}

·        />

·        ))}

·        对于所有非必填写属性, 总是手动去定义defaultProps属性.

因为 propTypes 可以作为组件的文档说明, 而声明 defaultProps 使阅读代码的人不需要去假设默认值. 另外, 显式的声明默认属性可以让你的组件跳过属性类型的检查.

 

// bad

function SFC({ foo, bar, children }){

  return<div>{foo}{bar}{children}</div>;

}

 

SFC.propTypes ={

  foo:PropTypes.number.isRequired,

  bar:PropTypes.string,

  children:PropTypes.node,

};

 

// good

function SFC({ foo, bar, children }){

  return<div>{foo}{bar}{children}</div>;

}

SFC.propTypes ={

  foo:PropTypes.number.isRequired,

  bar:PropTypes.string,

  children:PropTypes.node,

};

SFC.defaultProps ={

  bar:'',

  children:null,

};

Refs

·        总是使用回调函数方式定义 ref. eslint: react/no-string-refs

·        // bad

·        <Foo

·          ref="myRef"

·        />

·         

·        // good

·        <Foo

·          ref={(ref)=>{this.myRef =ref;}}

·        />

括号

·        将多行 JSX 标签写在 ()里. eslint: react/jsx-wrap-multilines

·        // bad

·        render(){

·          return<MyComponent className="long body" foo="bar">

·                   <MyChild/>

·                 </MyComponent>;

·        }

·         

·        // good

·        render(){

·          return(

·            <MyComponent className="long body" foo="bar">

·              <MyChild/>

·            </MyComponent>

·          );

·        }

·         

·        // good,单行可以不需要

·        render(){

·          const body =<div>hello</div>;

·          return<MyComponent>{body}</MyComponent>;

·        }

标签

·        对于没有子元素的标签, 总是自关闭标签.eslint: react/self-closing-comp

·        // bad

·        <Foo className="stuff"></Foo>

·         

·        // good

·        <Foo className="stuff"/>

·        如果组件有多行的属性, 关闭标签时新建一行.eslint: react/jsx-closing-bracket-location

·        // bad

·        <Foo

·          bar="bar"

·          baz="baz"/>

·         

·        // good

·        <Foo

·          bar="bar"

·          baz="baz"

·        />

函数/方法

·        使用箭头函数来获取本地变量.

·        functionItemList(props){

·          return(

·            <ul>

·              {props.items.map((item, index)=>(

·                <Item

·                  key={item.key}

·                  onClick={()=> doSomethingWith(item.name, index)}

·                />

·              ))}

·            </ul>

·          );

·         }

·        当在 render() 里使用事件处理方法时,提前在构造函数里把 this 绑定上去.eslint: react/jsx-no-bind

因为在每次 render 过程中, 再调用 bind 都会新建一个新的函数, 浪费资源.

// bad

classextendsReact.Component{

  onClickDiv(){

    // do stuff

  }

 

  render(){

    return<divonClick={this.onClickDiv.bind(this)}/>;

  }

}

 

// good

classextendsReact.Component{

  constructor(props){

    super(props);

    this.onClickDiv =this.onClickDiv.bind(this);

  }

 

  onClickDiv(){

    // do stuff

  }

 

  render(){

    return<divonClick={this.onClickDiv}/>;

  }

}

·        在React组件中, 不要给所谓的私有函数添加 _ 前缀, 本质上它并不是私有的.

因为_ 下划线前缀在某些语言中通常被用来表示私有变量或者函数. 但是不像其他的一些语言, 在 JS 中没有原生支持所谓的私有变量, 所有的变量函数都是共有的. 了解更多详情请查看 Issue #1024, 和 #490 .

 

// bad

React.createClass({

  _onClickSubmit(){

    // do stuff

  },

  // other stuff

});

 

// good

classextendsReact.Component{

  onClickSubmit(){

    // do stuff

  }

  // other stuff

}

·        在 render 方法中总是确保 return 返回值. eslint: react/require-render-return

·        // bad

·        render(){

·          (<div />);

·        }

·         

·        // good

·        render(){

·          return(<div />);

·        }

组件生命周期书写顺序

·        class extends React.Component 的生命周期函数:

1.   static 方法(可选)

2.   constructor 构造函数

3.   getChildContext 获取子元素内容

4.   componentWillMount 组件渲染前

5.   componentDidMount 组件渲染后

6.   componentWillReceiveProps 组件将接受新的数据

7.   shouldComponentUpdate 判断组件是否需要重新渲染

8.   componentWillUpdate 上面的方法返回 true时, 组件将重新渲染

9.   componentDidUpdate 组件渲染结束

事件绑定 如 onClickSubmit() 或 onChangeDescription()

render 里的getter方法如 getSelectReason() 或 getFooterContent()

可选的 render 方法 如 renderNavigation() 或 renderProfilePicture()

render render() 方法

·        如何定义 propTypes, defaultProps, contextTypes 等属性...

·         

·        importReact,{PropTypes}from'react';

·         

·        constpropTypes ={

·          id:PropTypes.number.isRequired,

·          url:PropTypes.string.isRequired,

·          text:PropTypes.string,

·        };

·         

·        constdefaultProps ={

·          text:'Hello World',

·        };

·         

·        classLinkextendsReact.Component{

·          static methodsAreOk(){

·            returntrue;

·          }

·         

·          render(){

·            return<a href={this.props.url}

·            data-id={this.props.id}>{this.props.text}</a>;

·          }

·        }

·         

·        Link.propTypes = propTypes;

·        Link.defaultProps = defaultProps;

·         exportdefaultLink;

·        React.createClass 方式的生命周期函数(不推荐)与 ES6 class 方式稍有不同:eslint: react/sort-comp

1.   displayName 设定组件名称

2.   propTypes 设置属性的类型

3.   contextTypes 设置上下文类型

4.   childContextTypes 设置子元素上下文类型

5.   mixins 添加一些 mixins

6.   statics

7.   defaultProps 设置默认的属性值

8.   getDefaultProps 获取默认属性值

9.   getInitialState 获取初始状态

10.                               getChildContext

11.                               componentWillMount

12.                               componentDidMount

13.                               componentWillReceiveProps

14.                               shouldComponentUpdate

15.                               componentWillUpdate

16.                               componentDidUpdate

17.                               componentWillUnmount

18.                               clickHandlers or eventHandlers like onClickSubmit() or onChangeDescription()

19.                               getter methods for render like getSelectReason() or getFooterContent()

20.                               Optional render methods like renderNavigation() or renderProfilePicture()

21.                               render

isMounted

·        不要再使用 isMounted. eslint: react/no-is-mounted

因为isMounted 设计模式 在 ES6 class 中无法使用, 官方将在未来的版本里删除此方法.

Airbnb CSS-in-JavaScript编码规范

命名

·        使用camelCase作为对象键(即选择器

为什么?我们将这些键作为styles组件中对象的属性来访问,因此使用camelCase最为方便// bad

{
  'bermuda-triangle': {
    display:'none',
  },
}
 
 
// good
{
  bermudaTriangle: {
    display:'none',
  },
}

·        将修饰符的下划线用于其他样式

为什么?与BEM类似,此命名约定明确表示这些样式旨在修改以下划线开头的元素。下划线不需要被引用,因此它们优于其他字符,如破折号

// bad

{
  bruceBanner: {
    color:'pink',
    transition:'color 10s',
  },
 
  bruceBannerTheHulk: {
    color:'green',
  },
}
 
// good
{
  bruceBanner: {
    color:'pink',
    transition:'color 10s',
  },
 
  bruceBanner_theHulk: {
    color:'green',
  },
}

·        使用selectorName_fallback的套后备风格。

 

为什么?与修饰符类似,保持命名一致有助于揭示这些样式与在更适合的浏览器中覆盖它们的样式之间的关系.

// bad
{
  muscles: {
    display:'flex',
  },
 
  muscles_sadBears: {
    width:'100%',
  },
}
 
// good
{
  muscles: {
    display:'flex',
  },
 
  muscles_fallback: {
    width:'100%',
  },
}

·        使用单独的选择器设置备用样式集.

为什么?保持单独对象中包含的回退式样可以明确其目的,从而提高可读性.

// bad
{
  muscles: {
    display:'flex',
  },
 
  left: {
    flexGrow:1,
    display:'inline-block',
  },
 
  right: {
    display:'inline-block',
  },
}
 
// good
{
  muscles: {
    display:'flex',
  },
 
  left: {
    flexGrow:1,
  },
 
  left_fallback: {
    display:'inline-block',
  },
 
  right_fallback: {
    display:'inline-block',
  },
}

·        使用设备不可知的名称(e.g. "small", "medium", and"large")来命名媒体查询断点.

// bad
constbreakpoints= {
  mobile:'@media (max-width: 639px)',
  tablet:'@media (max-width: 1047px)',
  desktop:'@media (min-width: 1048px)',
};
 
// good
constbreakpoints= {
  small:'@media (max-width: 639px)',
  medium:'@media (max-width: 1047px)',
  large:'@media (min-width: 1048px)',
};

顺序

·        在组件后面定义样式.

为什么?我们使用高阶组件来为我们的样式设定主题,这些样式在组件定义后自然使用。将样式对象直接传递给此函数会减少间接性。

 

// bad
const styles = {
  container: {
    display:'inline-block',
  },
};
 
functionMyComponent({ styles }) {
  return (
    <div {...css(styles.container)}>
      Never doubt that a small group of thoughtful, committed citizens can change the world. Indeed, it’s the only thing that ever has.
    </div>
  );
}
 
export defaultwithStyles(() => styles)(MyComponent);
 
// good
functionMyComponent({ styles }) {
  return (
    <div {...css(styles.container)}>
      Never doubt that a small group of thoughtful, committed citizens can change the world. Indeed, it’s the only thing that ever has.
    </div>
  );
}
 
export defaultwithStyles(() => ({
  container: {
    display:'inline-block',
  },
}))(MyComponent);

嵌套

在相同的缩进级别在相邻块之间留出空行。

 

 

为什么?空格提高了可读性并降低了合并冲突的可能性

// bad
{
  bigBang: {
    display:'inline-block',
    '::before': {
      content:"''",
    },
  },
  universe: {
    border:'none',
  },
}
 
// good
{
  bigBang: {
    display:'inline-block',
 
    '::before': {
      content:"''",
    },
  },
 
  universe: {
    border:'none',
  },
}

内联样式

·        对具有高基数的样式(例如,使用prop的值)使用内联样式,而不适用于基数较低的样式.

// bad
export defaultfunctionMyComponent({ spacing }) {
  return (
    <divstyle={{display:'table',margin: spacing }} />
  );
}
 
// good
functionMyComponent({ styles, spacing }) {
  return (
    <div {...css(styles.periodic, {margin: spacing })} />
  );
}
export defaultwithStyles(() => ({
  periodic: {
    display:'table',
  },
}))(MyComponent);

主题

  • 使用一个抽象层,例如可以启用主题的react-with-stylesreact-with-styles为我们提供了诸如withStyles()ThemedStyleSheetcss()在本文档中的一些示例中使用的内容.

为什么?有一组共享变量用于构建组件样式是很有用的。使用抽象层使得这更方便。此外,这可以帮助防止组件与任何特定的底层实现紧密耦合,从而为您提供更多的自由.

·        仅在主题中定义颜色.

·         // bad
·         exportdefault withStyles(() => ({
·           chuckNorris: {
·             color:'#bada55',
·           },
·         }))(MyComponent);
·          
·         // good
·         exportdefault withStyles(({ color }) => ({
·           chuckNorris: {
·             color: color.badass,
·           },
·         }))(MyComponent);

·        仅在主题中定义字体.

·         // bad
·         exportdefault withStyles(() => ({
·           towerOfPisa: {
·             fontStyle:'italic',
·           },
·         }))(MyComponent);
·          
·         // good
·         exportdefault withStyles(({ font }) => ({
·           towerOfPisa: {
·             fontStyle: font.italic,
·           },
·         }))(MyComponent);

·        将字体定义为相关样式集.

·         // bad
·         exportdefault withStyles(() => ({
·           towerOfPisa: {
·             fontFamily:'Italiana, "Times New Roman", serif',
·             fontSize:'2em',
·             fontStyle:'italic',
·             lineHeight:1.5,
·           },
·         }))(MyComponent);
·          
·         // good
·         exportdefault withStyles(({ font }) => ({
·           towerOfPisa: {
·             ...font.italian,
·           },
·         }))(MyComponent);

·        在主题中定义基本网格单元(作为值或乘法器的函数.

·         // bad
·         exportdefault withStyles(() => ({
·           rip: {
·             bottom:'-6912px', // 6 feet
·           },
·         }))(MyComponent);
·          
·         // good
·         exportdefault withStyles(({ units }) => ({
·           rip: {
·             bottom:units(864), // 6英尺,假设我们的单位是8px
·           },
·         }))(MyComponent);
·          
·         // good
·         exportdefault withStyles(({ unit }) => ({
·           rip: {
·             bottom:864* unit, // 6 feet, assuming our unit is 8px
·           },
·         }))(MyComponent);

·        仅在主题中定义媒体查询.

·         // bad
·         exportdefault withStyles(() => ({
·           container: {
·             width:'100%',
·          
·             '@media (max-width: 1047px)': {
·               width:'50%',
·             },
·           },
·         }))(MyComponent);
·          
·         // good
·         exportdefault withStyles(({ breakpoint }) => ({
·           container: {
·             width:'100%',
·          
·             [breakpoint.medium]: {
·               width:'50%',
·             },
·           },
·         }))(MyComponent);

·        在主题中定义棘手的回退属性.

为什么?许多CSS-in-JavaScript实现将样式对象合并在一起,这使得为同一属性指定回退(例如display)有点棘手。为了保持统一的方法,将这些回退放在主题中.

// bad
exportdefault withStyles(() => ({
  .muscles {
    display:'flex',
  },
 
  .muscles_fallback {
    'display ':'table',
  },
}))(MyComponent);
 
// good
exportdefault withStyles(({ fallbacks }) => ({
  .muscles {
    display:'flex',
  },
 
  .muscles_fallback {
    [fallbacks.display]:'table',
  },
}))(MyComponent);
 
// good
exportdefault withStyles(({ fallback }) => ({
  .muscles {
    display:'flex',
  },
 
  .muscles_fallback {
    [fallback('display')]:'table',
  },
}))(MyComponent);

·        尽可能少地创建自定义主题。许多应用程序可能只有一个主题.

·        命名空间自定义主题设置下的嵌套对象与一个独特的描述性的关键.

·         // bad
·         ThemedStyleSheet.registerTheme('mySection', {
·           mySectionPrimaryColor:'green',
·         });
·          
·         // good
·         ThemedStyleSheet.registerTheme('mySection', {
·           mySection: {
·             primaryColor:'green',
·           },
·         });

 


阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页