diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..604c595 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +*.md text eol=lf \ No newline at end of file diff --git a/README.md b/README.md index 9601a4f..a44433f 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,17 @@ This repository contains the specifications. -- [Javascript编码规范](javascript-style-guide.md) [1.3] +- [JavaScript编码规范](javascript-style-guide.md) [1.3] +- [JavaScript编码规范 - ESNext补充篇](es-next-style-guide.md) [draft] - [HTML编码规范](html-style-guide.md) [1.2] - [CSS编码规范](css-style-guide.md) [1.2] -- [Less编码规范](less-code-style.md) [1.0] +- [Less编码规范](less-code-style.md) [1.1] - [E-JSON数据传输标准](e-json.md) [1.0] - [模块和加载器规范](module.md) [1.1] - [包结构规范](package.md) [1.1] - [项目目录结构规范](directory.md) [1.1] - [图表库标准](chart.md) [1.0] +- [react编码规范](react-style-guide.md) [draft] + + +Lint and fix tool:[FECS](http://fecs.baidu.com/) diff --git a/css-style-guide.md b/css-style-guide.md index 8c60504..393a108 100644 --- a/css-style-guide.md +++ b/css-style-guide.md @@ -1,1062 +1,1064 @@ - -# CSS编码规范 - - - - -[1 前言](#1-%E5%89%8D%E8%A8%80) - -[2 代码风格](#2-%E4%BB%A3%E7%A0%81%E9%A3%8E%E6%A0%BC) - -  [2.1 文件](#21-%E6%96%87%E4%BB%B6) - -  [2.2 缩进](#22-%E7%BC%A9%E8%BF%9B) - -  [2.3 空格](#23-%E7%A9%BA%E6%A0%BC) - -  [2.4 行长度](#24-%E8%A1%8C%E9%95%BF%E5%BA%A6) - -  [2.5 选择器](#25-%E9%80%89%E6%8B%A9%E5%99%A8) - -  [2.6 属性](#26-%E5%B1%9E%E6%80%A7) - -[3 通用](#3-%E9%80%9A%E7%94%A8) - -  [3.1 选择器](#31-%E9%80%89%E6%8B%A9%E5%99%A8) - -  [3.2 属性缩写](#32-%E5%B1%9E%E6%80%A7%E7%BC%A9%E5%86%99) - -  [3.3 属性书写顺序](#33-%E5%B1%9E%E6%80%A7%E4%B9%A6%E5%86%99%E9%A1%BA%E5%BA%8F) - -  [3.4 清除浮动](#34-%E6%B8%85%E9%99%A4%E6%B5%AE%E5%8A%A8) - -  [3.5 !important](#35-important) - -  [3.6 z-index](#36-z-index) - -[4 值与单位](#4-%E5%80%BC%E4%B8%8E%E5%8D%95%E4%BD%8D) - -  [4.1 文本](#41-%E6%96%87%E6%9C%AC) - -  [4.2 数值](#42-%E6%95%B0%E5%80%BC) - -  [4.3 url()](#43-url) - -  [4.4 长度](#44-%E9%95%BF%E5%BA%A6) - -  [4.5 颜色](#45-%E9%A2%9C%E8%89%B2) - -  [4.6 2D 位置](#46-2d-%E4%BD%8D%E7%BD%AE) - -[5 文本编排](#5-%E6%96%87%E6%9C%AC%E7%BC%96%E6%8E%92) - -  [5.1 字体族](#51-%E5%AD%97%E4%BD%93%E6%97%8F) - -  [5.2 字号](#52-%E5%AD%97%E5%8F%B7) - -  [5.3 字体风格](#53-%E5%AD%97%E4%BD%93%E9%A3%8E%E6%A0%BC) - -  [5.4 字重](#54-%E5%AD%97%E9%87%8D) - -  [5.5 行高](#55-%E8%A1%8C%E9%AB%98) - -[6 变换与动画](#6-%E5%8F%98%E6%8D%A2%E4%B8%8E%E5%8A%A8%E7%94%BB) - -[7 响应式](#7-%E5%93%8D%E5%BA%94%E5%BC%8F) - -[8 兼容性](#8-%E5%85%BC%E5%AE%B9%E6%80%A7) - -  [8.1 属性前缀](#81-%E5%B1%9E%E6%80%A7%E5%89%8D%E7%BC%80) - -  [8.2 Hack](#82-hack) - -  [8.3 Expression](#83-expression) - - - - - -## 1 前言 - - -CSS作为网页样式的描述语言,在百度一直有着广泛的应用。本文档的目标是使CSS代码风格保持一致,容易被理解和被维护。 - -虽然本文档是针对CSS设计的,但是在使用各种CSS的预编译器(如less、sass、stylus等)时,适用的部分也应尽量遵循本文档的约定。 - - -## 2 代码风格 - - -### 2.1 文件 - - -#### [建议] `CSS` 文件使用无 `BOM` 的 `UTF-8` 编码。 - -解释: - -UTF-8 编码具有更广泛的适应性。BOM 在使用程序或工具处理文件时可能造成不必要的干扰。 - -### 2.2 缩进 - - -#### [强制] 使用 `4` 个空格做为一个缩进层级,不允许使用 `2` 个空格 或 `tab` 字符。 - - -示例: - -```css -.selector { - margin: 0; - padding: 0; -} -``` - -### 2.3 空格 - - -#### [强制] `选择器` 与 `{` 之间必须包含空格。 - -示例: - -```css -.selector { -} -``` - -#### [强制] `属性名` 与之后的 `:` 之间不允许包含空格, `:` 与 `属性值` 之间必须包含空格。 - -示例: - -```css -margin: 0; -``` - -#### [强制] `列表型属性值` 书写在单行时,`,` 后必须跟一个空格。 - -示例: - -```css -font-family: Arial, sans-serif; -``` - -### 2.4 行长度 - - -#### [强制] 每行不得超过 `120` 个字符,除非单行不可分割。 - -解释: - -常见不可分割的场景为URL超长。 - - -#### [建议] 对于超长的样式,在样式值的 `空格` 处或 `,` 后换行,建议按逻辑分组。 - -示例: - -```css -/* 不同属性值按逻辑分组 */ -background: - transparent url(aVeryVeryVeryLongUrlIsPlacedHere) - no-repeat 0 0; - -/* 可重复多次的属性,每次重复一行 */ -background-image: - url(aVeryVeryVeryLongUrlIsPlacedHere) - url(anotherVeryVeryVeryLongUrlIsPlacedHere); - -/* 类似函数的属性值可以根据函数调用的缩进进行 */ -background-image: -webkit-gradient( - linear, - left bottom, - left top, - color-stop(0.04, rgb(88,94,124)), - color-stop(0.52, rgb(115,123,162)) -); -``` - -### 2.5 选择器 - - -#### [强制] 当一个 rule 包含多个 selector 时,每个选择器声明必须独占一行。 - -示例: - -```css -/* good */ -.post, -.page, -.comment { - line-height: 1.5; -} - -/* bad */ -.post, .page, .comment { - line-height: 1.5; -} -``` - -#### [强制] `>`、`+`、`~` 选择器的两边各保留一个空格。 - -示例: - -```css -/* good */ -main > nav { - padding: 10px; -} - -label + input { - margin-left: 5px; -} - -input:checked ~ button { - background-color: #69C; -} - -/* bad */ -main>nav { - padding: 10px; -} - -label+input { - margin-left: 5px; -} - -input:checked~button { - background-color: #69C; -} -``` - -#### [强制] 属性选择器中的值必须用双引号包围。 - -解释: - -不允许使用单引号,不允许不使用引号。 - - -示例: - -```css -/* good */ -article[character="juliet"] { - voice-family: "Vivien Leigh", victoria, female -} - -/* bad */ -article[character='juliet'] { - voice-family: "Vivien Leigh", victoria, female -} -``` - -### 2.6 属性 - - -#### [强制] 属性定义必须另起一行。 - -示例: - -```css -/* good */ -.selector { - margin: 0; - padding: 0; -} - -/* bad */ -.selector { margin: 0; padding: 0; } -``` - -#### [强制] 属性定义后必须以分号结尾。 - -示例: - -```css -/* good */ -.selector { - margin: 0; -} - -/* bad */ -.selector { - margin: 0 -} -``` - - - - - - -## 3 通用 - - - - -### 3.1 选择器 - - -#### [强制] 如无必要,不得为 `id`、`class` 选择器添加类型选择器进行限定。 - -解释: - -在性能和维护性上,都有一定的影响。 - - -示例: - - -```css -/* good */ -#error, -.danger-message { - font-color: #c00; -} - -/* bad */ -dialog#error, -p.danger-message { - font-color: #c00; -} -``` - -#### [建议] 选择器的嵌套层级应不大于 3 级,位置靠后的限定条件应尽可能精确。 - -示例: - -```css -/* good */ -#username input {} -.comment .avatar {} - -/* bad */ -.page .header .login #username input {} -.comment div * {} -``` - - - -### 3.2 属性缩写 - - - -#### [建议] 在可以使用缩写的情况下,尽量使用属性缩写。 - -示例: - -```css -/* good */ -.post { - font: 12px/1.5 arial, sans-serif; -} - -/* bad */ -.post { - font-family: arial, sans-serif; - font-size: 12px; - line-height: 1.5; -} -``` - -#### [建议] 使用 `border` / `margin` / `padding` 等缩写时,应注意隐含值对实际数值的影响,确实需要设置多个方向的值时才使用缩写。 - -解释: - -border / margin / padding 等缩写会同时设置多个属性的值,容易覆盖不需要覆盖的设定。如某些方向需要继承其他声明的值,则应该分开设置。 - - -示例: - -```css -/* centering
horizontally and highlight featured ones */ -article { - margin: 5px; - border: 1px solid #999; -} - -/* good */ -.page { - margin-right: auto; - margin-left: auto; -} - -.featured { - border-color: #69c; -} - -/* bad */ -.page { - margin: 5px auto; /* introducing redundancy */ -} - -.featured { - border: 1px solid #69c; /* introducing redundancy */ -} -``` - - -### 3.3 属性书写顺序 - - -#### [建议] 同一 rule set 下的属性在书写时,应按功能进行分组,并以 **Formatting Model(布局方式、位置) > Box Model(尺寸) > Typographic(文本相关) > Visual(视觉效果)** 的顺序书写,以提高代码的可读性。 - -解释: - -- Formatting Model 相关属性包括:`position` / `top` / `right` / `bottom` / `left` / `float` / `display` / `overflow` 等 -- Box Model 相关属性包括:`border` / `margin` / `padding` / `width` / `height` 等 -- Typographic 相关属性包括:`font` / `line-height` / `text-align` / `word-wrap` 等 -- Visual 相关属性包括:`background` / `color` / `transition` / `list-style` 等 - -另外,如果包含 `content` 属性,应放在最前面。 - - -示例: - -```css -.sidebar { - /* formatting model: positioning schemes / offsets / z-indexes / display / ... */ - position: absolute; - top: 50px; - left: 0; - overflow-x: hidden; - - /* box model: sizes / margins / paddings / borders / ... */ - width: 200px; - padding: 5px; - border: 1px solid #ddd; - - /* typographic: font / aligns / text styles / ... */ - font-size: 14px; - line-height: 20px; - - /* visual: colors / shadows / gradients / ... */ - background: #f5f5f5; - color: #333; - -webkit-transition: color 1s; - -moz-transition: color 1s; - transition: color 1s; -} -``` - - -### 3.4 清除浮动 - - - -#### [建议] 当元素需要撑起高度以包含内部的浮动元素时,通过对伪类设置 `clear` 或触发 `BFC` 的方式进行 `clearfix`。尽量不使用增加空标签的方式。 - -解释: - -触发 BFC 的方式很多,常见的有: - -* float 非 none -* position 非 static -* overflow 非 visible - -如希望使用更小副作用的清除浮动方法,参见 [A new micro clearfix hack](http://nicolasgallagher.com/micro-clearfix-hack/) 一文。 - -另需注意,对已经触发 BFC 的元素不需要再进行 clearfix。 - - -### 3.5 !important - - -#### [建议] 尽量不使用 `!important` 声明。 - - -#### [建议] 当需要强制指定样式且不允许任何场景覆盖时,通过标签内联和 `!important` 定义样式。 - -解释: - -必须注意的是,仅在设计上 `确实不允许任何其它场景覆盖样式` 时,才使用内联的 `!important` 样式。通常在第三方环境的应用中使用这种方案。下面的 z-index 章节是其中一个特殊场景的典型样例。 - - - -### 3.6 z-index - - - -#### [建议] 将 `z-index` 进行分层,对文档流外绝对定位元素的视觉层级关系进行管理。 - -解释: - -同层的多个元素,如多个由用户输入触发的 Dialog,在该层级内使用相同的 `z-index` 或递增 `z-index`。 - -建议每层包含100个 `z-index` 来容纳足够的元素,如果每层元素较多,可以调整这个数值。 - - -#### [建议] 在可控环境下,期望显示在最上层的元素,`z-index` 指定为 `999999`。 - -解释: - -可控环境分成两种,一种是自身产品线环境;还有一种是可能会被其他产品线引用,但是不会被外部第三方的产品引用。 - -不建议取值为 `2147483647`。以便于自身产品线被其他产品线引用时,当遇到层级覆盖冲突的情况,留出向上调整的空间。 - - -#### [建议] 在第三方环境下,期望显示在最上层的元素,通过标签内联和 `!important`,将 `z-index` 指定为 `2147483647`。 - -解释: - -第三方环境对于开发者来说完全不可控。在第三方环境下的元素,为了保证元素不被其页面其他样式定义覆盖,需要采用此做法。 - - - - -## 4 值与单位 - - -### 4.1 文本 - - -#### [强制] 文本内容必须用双引号包围。 - -解释: - -文本类型的内容可能在选择器、属性值等内容中。 - - -示例: - -```css -/* good */ -html[lang|="zh"] q:before { - font-family: "Microsoft YaHei", sans-serif; - content: "“"; -} - -html[lang|="zh"] q:after { - font-family: "Microsoft YaHei", sans-serif; - content: "”"; -} - -/* bad */ -html[lang|=zh] q:before { - font-family: 'Microsoft YaHei', sans-serif; - content: '“'; -} - -html[lang|=zh] q:after { - font-family: "Microsoft YaHei", sans-serif; - content: "”"; -} -``` - -### 4.2 数值 - - -#### [强制] 当数值为 0 - 1 之间的小数时,省略整数部分的 `0`。 - -示例: - -```css -/* good */ -panel { - opacity: .8 -} - -/* bad */ -panel { - opacity: 0.8 -} -``` - -### 4.3 url() - - -#### [强制] `url()` 函数中的路径不加引号。 - -示例: - -```css -body { - background: url(bg.png); -} -``` - - -#### [建议] `url()` 函数中的绝对路径可省去协议名。 - - -示例: - -```css -body { - background: url(//baidu.com/img/bg.png) no-repeat 0 0; -} -``` - - -### 4.4 长度 - - -#### [强制] 长度为 `0` 时须省略单位。 (也只有长度单位可省) - -示例: - -```css -/* good */ -body { - padding: 0 5px; -} - -/* bad */ -body { - padding: 0px 5px; -} -``` - - -### 4.5 颜色 - - -#### [强制] RGB颜色值必须使用十六进制记号形式 `#rrggbb`。不允许使用 `rgb()`。 - -解释: - -带有alpha的颜色信息可以使用 `rgba()`。使用 `rgba()` 时每个逗号后必须保留一个空格。 - - -示例: - -```css -/* good */ -.success { - box-shadow: 0 0 2px rgba(0, 128, 0, .3); - border-color: #008000; -} - -/* bad */ -.success { - box-shadow: 0 0 2px rgba(0,128,0,.3); - border-color: rgb(0, 128, 0); -} -``` - -#### [强制] 颜色值可以缩写时,必须使用缩写形式。 - -示例: - -```css -/* good */ -.success { - background-color: #aca; -} - -/* bad */ -.success { - background-color: #aaccaa; -} -``` - -#### [强制] 颜色值不允许使用命名色值。 - -示例: - -```css -/* good */ -.success { - color: #90ee90; -} - -/* bad */ -.success { - color: lightgreen; -} -``` - -#### [建议] 颜色值中的英文字符采用小写。如不用小写也需要保证同一项目内保持大小写一致。 - - -示例: - -```css -/* good */ -.success { - background-color: #aca; - color: #90ee90; -} - -/* good */ -.success { - background-color: #ACA; - color: #90EE90; -} - -/* bad */ -.success { - background-color: #ACA; - color: #90ee90; -} -``` - - -### 4.6 2D 位置 - - -#### [强制] 必须同时给出水平和垂直方向的位置。 - -解释: - -2D 位置初始值为 `0% 0%`,但在只有一个方向的值时,另一个方向的值会被解析为 center。为避免理解上的困扰,应同时给出两个方向的值。[background-position属性值的定义](http://www.w3.org/TR/CSS21/colors.html#propdef-background-position) - - -示例: - -```css -/* good */ -body { - background-position: center top; /* 50% 0% */ -} - -/* bad */ -body { - background-position: top; /* 50% 0% */ -} -``` - - - - - -## 5 文本编排 - - -### 5.1 字体族 - - -#### [强制] `font-family` 属性中的字体族名称应使用字体的英文 `Family Name`,其中如有空格,须放置在引号中。 - -解释: - -所谓英文 Family Name,为字体文件的一个元数据,常见名称如下: - -字体 | 操作系统 | Family Name ------|----------|------------ -宋体 (中易宋体) | Windows | SimSun -黑体 (中易黑体) | Windows | SimHei -微软雅黑 | Windows | Microsoft YaHei -微软正黑 | Windows | Microsoft JhengHei -华文黑体 | Mac/iOS | STHeiti -冬青黑体 | Mac/iOS | Hiragino Sans GB -文泉驿正黑 | Linux | WenQuanYi Zen Hei -文泉驿微米黑 | Linux | WenQuanYi Micro Hei - - -示例: - -```css -h1 { - font-family: "Microsoft YaHei"; -} -``` - - -#### [强制] `font-family` 按「西文字体在前、中文字体在后」、「效果佳 (质量高/更能满足需求) 的字体在前、效果一般的字体在后」的顺序编写,最后必须指定一个通用字体族( `serif` / `sans-serif` )。 - -解释: - -更详细说明可参考[本文](http://www.zhihu.com/question/19911793/answer/13329819)。 - -示例: - -```css -/* Display according to platform */ -.article { - font-family: Arial, sans-serif; -} - -/* Specific for most platforms */ -h1 { - font-family: "Helvetica Neue", Arial, "Hiragino Sans GB", "WenQuanYi Micro Hei", "Microsoft YaHei", sans-serif; -} -``` - -#### [强制] `font-family` 不区分大小写,但在同一个项目中,同样的 `Family Name` 大小写必须统一。 - -示例: - -```css -/* good */ -body { - font-family: Arial, sans-serif; -} - -h1 { - font-family: Arial, "Microsoft YaHei", sans-serif; -} - -/* bad */ -body { - font-family: arial, sans-serif; -} - -h1 { - font-family: Arial, "Microsoft YaHei", sans-serif; -} -``` - -### 5.2 字号 - - -#### [强制] 需要在 Windows 平台显示的中文内容,其字号应不小于 `12px`。 - -解释: - -由于 Windows 的字体渲染机制,小于 12px 的文字显示效果极差、难以辨认。 - - -### 5.3 字体风格 - - -#### [建议] 需要在 Windows 平台显示的中文内容,不要使用除 `normal` 外的 `font-style`。其他平台也应慎用。 - -解释: - -由于中文字体没有 italic 风格的实现,所有浏览器下都会 fallback 到 obilique 实现 (自动拟合为斜体),小字号下 (特别是 Windows 下会在小字号下使用点阵字体的情况下) 显示效果差,造成阅读困难。 - - -### 5.4 字重 - - -#### [强制] `font-weight` 属性必须使用数值方式描述。 - -解释: - -CSS 的字重分 100 – 900 共九档,但目前受字体本身质量和浏览器的限制,实际上支持 400 和 700 两档,分别等价于关键词 normal 和 bold。 - -浏览器本身使用一系列[启发式规则](http://www.w3.org/TR/CSS21/fonts.html#propdef-font-weight)来进行匹配,在 <700 时一般匹配字体的 Regular 字重,>=700 时匹配 Bold 字重。 - -但已有浏览器开始支持 =600 时匹配 Semibold 字重 (见[此表](http://justineo.github.io/slideshows/font/#/3/15)),故使用数值描述增加了灵活性,也更简短。 - -示例: - -```css -/* good */ -h1 { - font-weight: 700; -} - -/* bad */ -h1 { - font-weight: bold; -} -``` - -### 5.5 行高 - - -#### [建议] `line-height` 在定义文本段落时,应使用数值。 - -解释: - -将 line-height 设置为数值,浏览器会基于当前元素设置的 font-size 进行再次计算。在不同字号的文本段落组合中,能达到较为舒适的行间间隔效果,避免在每个设置了 font-size 都需要设置 line-height。 - -当 line-height 用于控制垂直居中时,还是应该设置成与容器高度一致。 - - -示例: - -```css -.container { - line-height: 1.5; -} -``` - - - -## 6 变换与动画 - - - -#### [强制] 使用 `transition` 时应指定 `transition-property`。 - -示例: - -```css -/* good */ -.box { - transition: color 1s, border-color 1s; -} - -/* bad */ -.box { - transition: all 1s; -} -``` - -#### [建议] 尽可能在浏览器能高效实现的属性上添加过渡和动画。 - -解释: - -见[本文](http://www.html5rocks.com/en/tutorials/speed/high-performance-animations/),在可能的情况下应选择这样四种变换: - -* `transform: translate(npx, npx);` -* `transform: scale(n);` -* `transform: rotate(ndeg);` -* `opacity: 0..1;` - -典型的,可以使用 translate 来代替 left 作为动画属性。 - -示例: - -```css -/* good */ -.box { - transition: transform 1s; -} -.box:hover { - transform: translate(20px); /* move right for 20px */ -} - -/* bad */ -.box { - left: 0; - transition: left 1s; -} -.box:hover { - left: 20px; /* move right for 20px */ -} -``` - - - - -## 7 响应式 - - - -#### [强制] `Media Query` 不得单独编排,必须与相关的规则一起定义。 - -示例: - -```css -/* Good */ -/* header styles */ -@media (...) { - /* header styles */ -} - -/* main styles */ -@media (...) { - /* main styles */ -} - -/* footer styles */ -@media (...) { - /* footer styles */ -} - - -/* Bad */ -/* header styles */ -/* main styles */ -/* footer styles */ - -@media (...) { - /* header styles */ - /* main styles */ - /* footer styles */ -} -``` - -#### [强制] `Media Query` 如果有多个逗号分隔的条件时,应将每个条件放在单独一行中。 - -示例: - -```css -@media -(-webkit-min-device-pixel-ratio: 2), /* Webkit-based browsers */ -(min--moz-device-pixel-ratio: 2), /* Older Firefox browsers (prior to Firefox 16) */ -(min-resolution: 2dppx), /* The standard way */ -(min-resolution: 192dpi) { /* dppx fallback */ - /* Retina-specific stuff here */ -} -``` - -#### [建议] 尽可能给出在高分辨率设备 (Retina) 下效果更佳的样式。 - - - -## 8 兼容性 - - -### 8.1 属性前缀 - - -#### [强制] 带私有前缀的属性由长到短排列,按冒号位置对齐。 - -解释: - -标准属性放在最后,按冒号对齐方便阅读,也便于在编辑器内进行多行编辑。 - - -示例: - -```css -.box { - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; -} -``` - - -### 8.2 Hack - - -#### [建议] 需要添加 `hack` 时应尽可能考虑是否可以采用其他方式解决。 - -解释: - -如果能通过合理的 HTML 结构或使用其他的 CSS 定义达到理想的样式,则不应该使用 hack 手段解决问题。通常 hack 会导致维护成本的增加。 - -#### [建议] 尽量使用 `选择器 hack` 处理兼容性,而非 `属性 hack`。 - -解释: - -尽量使用符合 CSS 语法的 selector hack,可以避免一些第三方库无法识别 hack 语法的问题。 - - -示例: - -```css -/* IE 7 */ -*:first-child + html #header { - margin-top: 3px; - padding: 5px; -} - -/* IE 6 */ -* html #header { - margin-top: 5px; - padding: 4px; -} -``` - - -#### [建议] 尽量使用简单的 `属性 hack`。 - -示例: - -```css -.box { - _display: inline; /* fix double margin */ - float: left; - margin-left: 20px; -} - -.container { - overflow: hidden; - *zoom: 1; /* triggering hasLayout */ -} -``` - -### 8.3 Expression - - -#### [强制] 禁止使用 `Expression`。 - - - - - + +# CSS编码规范 + + + + +[1 前言](#user-content-1-%E5%89%8D%E8%A8%80) + +[2 代码风格](#user-content-2-%E4%BB%A3%E7%A0%81%E9%A3%8E%E6%A0%BC) + +  [2.1 文件](#user-content-21-%E6%96%87%E4%BB%B6) + +  [2.2 缩进](#user-content-22-%E7%BC%A9%E8%BF%9B) + +  [2.3 空格](#user-content-23-%E7%A9%BA%E6%A0%BC) + +  [2.4 行长度](#user-content-24-%E8%A1%8C%E9%95%BF%E5%BA%A6) + +  [2.5 选择器](#user-content-25-%E9%80%89%E6%8B%A9%E5%99%A8) + +  [2.6 属性](#user-content-26-%E5%B1%9E%E6%80%A7) + +[3 通用](#user-content-3-%E9%80%9A%E7%94%A8) + +  [3.1 选择器](#user-content-31-%E9%80%89%E6%8B%A9%E5%99%A8) + +  [3.2 属性缩写](#user-content-32-%E5%B1%9E%E6%80%A7%E7%BC%A9%E5%86%99) + +  [3.3 属性书写顺序](#user-content-33-%E5%B1%9E%E6%80%A7%E4%B9%A6%E5%86%99%E9%A1%BA%E5%BA%8F) + +  [3.4 清除浮动](#user-content-34-%E6%B8%85%E9%99%A4%E6%B5%AE%E5%8A%A8) + +  [3.5 !important](#user-content-35-important) + +  [3.6 z-index](#user-content-36-z-index) + +[4 值与单位](#user-content-4-%E5%80%BC%E4%B8%8E%E5%8D%95%E4%BD%8D) + +  [4.1 文本](#user-content-41-%E6%96%87%E6%9C%AC) + +  [4.2 数值](#user-content-42-%E6%95%B0%E5%80%BC) + +  [4.3 url()](#user-content-43-url) + +  [4.4 长度](#user-content-44-%E9%95%BF%E5%BA%A6) + +  [4.5 颜色](#user-content-45-%E9%A2%9C%E8%89%B2) + +  [4.6 2D 位置](#user-content-46-2d-%E4%BD%8D%E7%BD%AE) + +[5 文本编排](#user-content-5-%E6%96%87%E6%9C%AC%E7%BC%96%E6%8E%92) + +  [5.1 字体族](#user-content-51-%E5%AD%97%E4%BD%93%E6%97%8F) + +  [5.2 字号](#user-content-52-%E5%AD%97%E5%8F%B7) + +  [5.3 字体风格](#user-content-53-%E5%AD%97%E4%BD%93%E9%A3%8E%E6%A0%BC) + +  [5.4 字重](#user-content-54-%E5%AD%97%E9%87%8D) + +  [5.5 行高](#user-content-55-%E8%A1%8C%E9%AB%98) + +[6 变换与动画](#user-content-6-%E5%8F%98%E6%8D%A2%E4%B8%8E%E5%8A%A8%E7%94%BB) + +[7 响应式](#user-content-7-%E5%93%8D%E5%BA%94%E5%BC%8F) + +[8 兼容性](#user-content-8-%E5%85%BC%E5%AE%B9%E6%80%A7) + +  [8.1 属性前缀](#user-content-81-%E5%B1%9E%E6%80%A7%E5%89%8D%E7%BC%80) + +  [8.2 Hack](#user-content-82-hack) + +  [8.3 Expression](#user-content-83-expression) + + + + + +## 1 前言 + + +CSS 作为网页样式的描述语言,在百度一直有着广泛的应用。本文档的目标是使 CSS 代码风格保持一致,容易被理解和被维护。 + +虽然本文档是针对 CSS 设计的,但是在使用各种 CSS 的预编译器(如 less、sass、stylus 等)时,适用的部分也应尽量遵循本文档的约定。 + + + + +## 2 代码风格 + + +### 2.1 文件 + + +#### [建议] `CSS` 文件使用无 `BOM` 的 `UTF-8` 编码。 + +解释: + +UTF-8 编码具有更广泛的适应性。BOM 在使用程序或工具处理文件时可能造成不必要的干扰。 + +### 2.2 缩进 + + +#### [强制] 使用 `4` 个空格做为一个缩进层级,不允许使用 `2` 个空格 或 `tab` 字符。 + + +示例: + +```css +.selector { + margin: 0; + padding: 0; +} +``` + +### 2.3 空格 + + +#### [强制] `选择器` 与 `{` 之间必须包含空格。 + +示例: + +```css +.selector { +} +``` + +#### [强制] `属性名` 与之后的 `:` 之间不允许包含空格, `:` 与 `属性值` 之间必须包含空格。 + +示例: + +```css +margin: 0; +``` + +#### [强制] `列表型属性值` 书写在单行时,`,` 后必须跟一个空格。 + +示例: + +```css +font-family: Arial, sans-serif; +``` + +### 2.4 行长度 + + +#### [强制] 每行不得超过 `120` 个字符,除非单行不可分割。 + +解释: + +常见不可分割的场景为URL超长。 + + +#### [建议] 对于超长的样式,在样式值的 `空格` 处或 `,` 后换行,建议按逻辑分组。 + +示例: + +```css +/* 不同属性值按逻辑分组 */ +background: + transparent url(aVeryVeryVeryLongUrlIsPlacedHere) + no-repeat 0 0; + +/* 可重复多次的属性,每次重复一行 */ +background-image: + url(aVeryVeryVeryLongUrlIsPlacedHere) + url(anotherVeryVeryVeryLongUrlIsPlacedHere); + +/* 类似函数的属性值可以根据函数调用的缩进进行 */ +background-image: -webkit-gradient( + linear, + left bottom, + left top, + color-stop(0.04, rgb(88,94,124)), + color-stop(0.52, rgb(115,123,162)) +); +``` + +### 2.5 选择器 + + +#### [强制] 当一个 rule 包含多个 selector 时,每个选择器声明必须独占一行。 + +示例: + +```css +/* good */ +.post, +.page, +.comment { + line-height: 1.5; +} + +/* bad */ +.post, .page, .comment { + line-height: 1.5; +} +``` + +#### [强制] `>`、`+`、`~` 选择器的两边各保留一个空格。 + +示例: + +```css +/* good */ +main > nav { + padding: 10px; +} + +label + input { + margin-left: 5px; +} + +input:checked ~ button { + background-color: #69C; +} + +/* bad */ +main>nav { + padding: 10px; +} + +label+input { + margin-left: 5px; +} + +input:checked~button { + background-color: #69C; +} +``` + +#### [强制] 属性选择器中的值必须用双引号包围。 + +解释: + +不允许使用单引号,不允许不使用引号。 + + +示例: + +```css +/* good */ +article[character="juliet"] { + voice-family: "Vivien Leigh", victoria, female; +} + +/* bad */ +article[character='juliet'] { + voice-family: "Vivien Leigh", victoria, female; +} +``` + +### 2.6 属性 + + +#### [强制] 属性定义必须另起一行。 + +示例: + +```css +/* good */ +.selector { + margin: 0; + padding: 0; +} + +/* bad */ +.selector { margin: 0; padding: 0; } +``` + +#### [强制] 属性定义后必须以分号结尾。 + +示例: + +```css +/* good */ +.selector { + margin: 0; +} + +/* bad */ +.selector { + margin: 0 +} +``` + + + + + + +## 3 通用 + + + + +### 3.1 选择器 + + +#### [强制] 如无必要,不得为 `id`、`class` 选择器添加类型选择器进行限定。 + +解释: + +在性能和维护性上,都有一定的影响。 + + +示例: + + +```css +/* good */ +#error, +.danger-message { + font-color: #c00; +} + +/* bad */ +dialog#error, +p.danger-message { + font-color: #c00; +} +``` + +#### [建议] 选择器的嵌套层级应不大于 `3` 级,位置靠后的限定条件应尽可能精确。 + +示例: + +```css +/* good */ +#username input {} +.comment .avatar {} + +/* bad */ +.page .header .login #username input {} +.comment div * {} +``` + + + +### 3.2 属性缩写 + + + +#### [建议] 在可以使用缩写的情况下,尽量使用属性缩写。 + +示例: + +```css +/* good */ +.post { + font: 12px/1.5 arial, sans-serif; +} + +/* bad */ +.post { + font-family: arial, sans-serif; + font-size: 12px; + line-height: 1.5; +} +``` + +#### [建议] 使用 `border` / `margin` / `padding` 等缩写时,应注意隐含值对实际数值的影响,确实需要设置多个方向的值时才使用缩写。 + +解释: + +`border` / `margin` / `padding` 等缩写会同时设置多个属性的值,容易覆盖不需要覆盖的设定。如某些方向需要继承其他声明的值,则应该分开设置。 + + +示例: + +```css +/* centering
horizontally and highlight featured ones */ +article { + margin: 5px; + border: 1px solid #999; +} + +/* good */ +.page { + margin-right: auto; + margin-left: auto; +} + +.featured { + border-color: #69c; +} + +/* bad */ +.page { + margin: 5px auto; /* introducing redundancy */ +} + +.featured { + border: 1px solid #69c; /* introducing redundancy */ +} +``` + + +### 3.3 属性书写顺序 + + +#### [建议] 同一 rule set 下的属性在书写时,应按功能进行分组,并以 **Formatting Model(布局方式、位置) > Box Model(尺寸) > Typographic(文本相关) > Visual(视觉效果)** 的顺序书写,以提高代码的可读性。 + +解释: + +- Formatting Model 相关属性包括:`position` / `top` / `right` / `bottom` / `left` / `float` / `display` / `overflow` 等 +- Box Model 相关属性包括:`border` / `margin` / `padding` / `width` / `height` 等 +- Typographic 相关属性包括:`font` / `line-height` / `text-align` / `word-wrap` 等 +- Visual 相关属性包括:`background` / `color` / `transition` / `list-style` 等 + +另外,如果包含 `content` 属性,应放在最前面。 + + +示例: + +```css +.sidebar { + /* formatting model: positioning schemes / offsets / z-indexes / display / ... */ + position: absolute; + top: 50px; + left: 0; + overflow-x: hidden; + + /* box model: sizes / margins / paddings / borders / ... */ + width: 200px; + padding: 5px; + border: 1px solid #ddd; + + /* typographic: font / aligns / text styles / ... */ + font-size: 14px; + line-height: 20px; + + /* visual: colors / shadows / gradients / ... */ + background: #f5f5f5; + color: #333; + -webkit-transition: color 1s; + -moz-transition: color 1s; + transition: color 1s; +} +``` + + +### 3.4 清除浮动 + + + +#### [建议] 当元素需要撑起高度以包含内部的浮动元素时,通过对伪类设置 `clear` 或触发 `BFC` 的方式进行 `clearfix`。尽量不使用增加空标签的方式。 + +解释: + +触发 BFC 的方式很多,常见的有: + +* float 非 none +* position 非 static +* overflow 非 visible + +如希望使用更小副作用的清除浮动方法,参见 [A new micro clearfix hack](http://nicolasgallagher.com/micro-clearfix-hack/) 一文。 + +另需注意,对已经触发 BFC 的元素不需要再进行 clearfix。 + + +### 3.5 !important + + +#### [建议] 尽量不使用 `!important` 声明。 + + +#### [建议] 当需要强制指定样式且不允许任何场景覆盖时,通过标签内联和 `!important` 定义样式。 + +解释: + +必须注意的是,仅在设计上 `确实不允许任何其它场景覆盖样式` 时,才使用内联的 `!important` 样式。通常在第三方环境的应用中使用这种方案。下面的 `z-index` 章节是其中一个特殊场景的典型样例。 + + + +### 3.6 z-index + + + +#### [建议] 将 `z-index` 进行分层,对文档流外绝对定位元素的视觉层级关系进行管理。 + +解释: + +同层的多个元素,如多个由用户输入触发的 Dialog,在该层级内使用相同的 `z-index` 或递增 `z-index`。 + +建议每层包含100个 `z-index` 来容纳足够的元素,如果每层元素较多,可以调整这个数值。 + + +#### [建议] 在可控环境下,期望显示在最上层的元素,`z-index` 指定为 `999999`。 + +解释: + +可控环境分成两种,一种是自身产品线环境;还有一种是可能会被其他产品线引用,但是不会被外部第三方的产品引用。 + +不建议取值为 `2147483647`。以便于自身产品线被其他产品线引用时,当遇到层级覆盖冲突的情况,留出向上调整的空间。 + + +#### [建议] 在第三方环境下,期望显示在最上层的元素,通过标签内联和 `!important`,将 `z-index` 指定为 `2147483647`。 + +解释: + +第三方环境对于开发者来说完全不可控。在第三方环境下的元素,为了保证元素不被其页面其他样式定义覆盖,需要采用此做法。 + + + + +## 4 值与单位 + + +### 4.1 文本 + + +#### [强制] 文本内容必须用双引号包围。 + +解释: + +文本类型的内容可能在选择器、属性值等内容中。 + + +示例: + +```css +/* good */ +html[lang|="zh"] q:before { + font-family: "Microsoft YaHei", sans-serif; + content: "“"; +} + +html[lang|="zh"] q:after { + font-family: "Microsoft YaHei", sans-serif; + content: "”"; +} + +/* bad */ +html[lang|=zh] q:before { + font-family: 'Microsoft YaHei', sans-serif; + content: '“'; +} + +html[lang|=zh] q:after { + font-family: "Microsoft YaHei", sans-serif; + content: "”"; +} +``` + +### 4.2 数值 + + +#### [强制] 当数值为 0 - 1 之间的小数时,省略整数部分的 `0`。 + +示例: + +```css +/* good */ +panel { + opacity: .8; +} + +/* bad */ +panel { + opacity: 0.8; +} +``` + +### 4.3 url() + + +#### [强制] `url()` 函数中的路径不加引号。 + +示例: + +```css +body { + background: url(bg.png); +} +``` + + +#### [建议] `url()` 函数中的绝对路径可省去协议名。 + + +示例: + +```css +body { + background: url(//baidu.com/img/bg.png) no-repeat 0 0; +} +``` + + +### 4.4 长度 + + +#### [强制] 长度为 `0` 时须省略单位。 (也只有长度单位可省) + +示例: + +```css +/* good */ +body { + padding: 0 5px; +} + +/* bad */ +body { + padding: 0px 5px; +} +``` + + +### 4.5 颜色 + + +#### [强制] RGB颜色值必须使用十六进制记号形式 `#rrggbb`。不允许使用 `rgb()`。 + +解释: + +带有alpha的颜色信息可以使用 `rgba()`。使用 `rgba()` 时每个逗号后必须保留一个空格。 + + +示例: + +```css +/* good */ +.success { + box-shadow: 0 0 2px rgba(0, 128, 0, .3); + border-color: #008000; +} + +/* bad */ +.success { + box-shadow: 0 0 2px rgba(0,128,0,.3); + border-color: rgb(0, 128, 0); +} +``` + +#### [强制] 颜色值可以缩写时,必须使用缩写形式。 + +示例: + +```css +/* good */ +.success { + background-color: #aca; +} + +/* bad */ +.success { + background-color: #aaccaa; +} +``` + +#### [强制] 颜色值不允许使用命名色值。 + +示例: + +```css +/* good */ +.success { + color: #90ee90; +} + +/* bad */ +.success { + color: lightgreen; +} +``` + +#### [建议] 颜色值中的英文字符采用小写。如不用小写也需要保证同一项目内保持大小写一致。 + + +示例: + +```css +/* good */ +.success { + background-color: #aca; + color: #90ee90; +} + +/* good */ +.success { + background-color: #ACA; + color: #90EE90; +} + +/* bad */ +.success { + background-color: #ACA; + color: #90ee90; +} +``` + + +### 4.6 2D 位置 + + +#### [强制] 必须同时给出水平和垂直方向的位置。 + +解释: + +2D 位置初始值为 `0% 0%`,但在只有一个方向的值时,另一个方向的值会被解析为 center。为避免理解上的困扰,应同时给出两个方向的值。[background-position属性值的定义](http://www.w3.org/TR/CSS21/colors.html#propdef-background-position) + + +示例: + +```css +/* good */ +body { + background-position: center top; /* 50% 0% */ +} + +/* bad */ +body { + background-position: top; /* 50% 0% */ +} +``` + + + + + +## 5 文本编排 + + +### 5.1 字体族 + + +#### [强制] `font-family` 属性中的字体族名称应使用字体的英文 `Family Name`,其中如有空格,须放置在引号中。 + +解释: + +所谓英文 Family Name,为字体文件的一个元数据,常见名称如下: + +字体 | 操作系统 | Family Name +-----|----------|------------ +宋体 (中易宋体) | Windows | SimSun +黑体 (中易黑体) | Windows | SimHei +微软雅黑 | Windows | Microsoft YaHei +微软正黑 | Windows | Microsoft JhengHei +华文黑体 | Mac/iOS | STHeiti +冬青黑体 | Mac/iOS | Hiragino Sans GB +文泉驿正黑 | Linux | WenQuanYi Zen Hei +文泉驿微米黑 | Linux | WenQuanYi Micro Hei + + +示例: + +```css +h1 { + font-family: "Microsoft YaHei"; +} +``` + + +#### [强制] `font-family` 按「西文字体在前、中文字体在后」、「效果佳 (质量高/更能满足需求) 的字体在前、效果一般的字体在后」的顺序编写,最后必须指定一个通用字体族( `serif` / `sans-serif` )。 + +解释: + +更详细说明可参考[本文](http://www.zhihu.com/question/19911793/answer/13329819)。 + +示例: + +```css +/* Display according to platform */ +.article { + font-family: Arial, sans-serif; +} + +/* Specific for most platforms */ +h1 { + font-family: "Helvetica Neue", Arial, "Hiragino Sans GB", "WenQuanYi Micro Hei", "Microsoft YaHei", sans-serif; +} +``` + +#### [强制] `font-family` 不区分大小写,但在同一个项目中,同样的 `Family Name` 大小写必须统一。 + +示例: + +```css +/* good */ +body { + font-family: Arial, sans-serif; +} + +h1 { + font-family: Arial, "Microsoft YaHei", sans-serif; +} + +/* bad */ +body { + font-family: arial, sans-serif; +} + +h1 { + font-family: Arial, "Microsoft YaHei", sans-serif; +} +``` + +### 5.2 字号 + + +#### [强制] 需要在 Windows 平台显示的中文内容,其字号应不小于 `12px`。 + +解释: + +由于 Windows 的字体渲染机制,小于 `12px` 的文字显示效果极差、难以辨认。 + + +### 5.3 字体风格 + + +#### [建议] 需要在 Windows 平台显示的中文内容,不要使用除 `normal` 外的 `font-style`。其他平台也应慎用。 + +解释: + +由于中文字体没有 `italic` 风格的实现,所有浏览器下都会 fallback 到 `obilique` 实现 (自动拟合为斜体),小字号下 (特别是 Windows 下会在小字号下使用点阵字体的情况下) 显示效果差,造成阅读困难。 + + +### 5.4 字重 + + +#### [强制] `font-weight` 属性必须使用数值方式描述。 + +解释: + +CSS 的字重分 100 – 900 共九档,但目前受字体本身质量和浏览器的限制,实际上支持 `400` 和 `700` 两档,分别等价于关键词 `normal` 和 `bold`。 + +浏览器本身使用一系列[启发式规则](http://www.w3.org/TR/CSS21/fonts.html#propdef-font-weight)来进行匹配,在 `<700` 时一般匹配字体的 Regular 字重,`>=700` 时匹配 Bold 字重。 + +但已有浏览器开始支持 `=600` 时匹配 Semibold 字重 (见[此表](http://justineo.github.io/slideshows/font/#/3/15)),故使用数值描述增加了灵活性,也更简短。 + +示例: + +```css +/* good */ +h1 { + font-weight: 700; +} + +/* bad */ +h1 { + font-weight: bold; +} +``` + +### 5.5 行高 + + +#### [建议] `line-height` 在定义文本段落时,应使用数值。 + +解释: + +将 `line-height` 设置为数值,浏览器会基于当前元素设置的 `font-size` 进行再次计算。在不同字号的文本段落组合中,能达到较为舒适的行间间隔效果,避免在每个设置了 `font-size` 都需要设置 `line-height`。 + +当 `line-height` 用于控制垂直居中时,还是应该设置成与容器高度一致。 + + +示例: + +```css +.container { + line-height: 1.5; +} +``` + + + +## 6 变换与动画 + + + +#### [强制] 使用 `transition` 时应指定 `transition-property`。 + +示例: + +```css +/* good */ +.box { + transition: color 1s, border-color 1s; +} + +/* bad */ +.box { + transition: all 1s; +} +``` + +#### [建议] 尽可能在浏览器能高效实现的属性上添加过渡和动画。 + +解释: + +见[本文](http://www.html5rocks.com/en/tutorials/speed/high-performance-animations/),在可能的情况下应选择这样四种变换: + +* `transform: translate(npx, npx);` +* `transform: scale(n);` +* `transform: rotate(ndeg);` +* `opacity: 0..1;` + +典型的,可以使用 `translate` 来代替 `left` 作为动画属性。 + +示例: + +```css +/* good */ +.box { + transition: transform 1s; +} +.box:hover { + transform: translate(20px); /* move right for 20px */ +} + +/* bad */ +.box { + left: 0; + transition: left 1s; +} +.box:hover { + left: 20px; /* move right for 20px */ +} +``` + + + + +## 7 响应式 + + + +#### [强制] `Media Query` 不得单独编排,必须与相关的规则一起定义。 + +示例: + +```css +/* Good */ +/* header styles */ +@media (...) { + /* header styles */ +} + +/* main styles */ +@media (...) { + /* main styles */ +} + +/* footer styles */ +@media (...) { + /* footer styles */ +} + + +/* Bad */ +/* header styles */ +/* main styles */ +/* footer styles */ + +@media (...) { + /* header styles */ + /* main styles */ + /* footer styles */ +} +``` + +#### [强制] `Media Query` 如果有多个逗号分隔的条件时,应将每个条件放在单独一行中。 + +示例: + +```css +@media +(-webkit-min-device-pixel-ratio: 2), /* Webkit-based browsers */ +(min--moz-device-pixel-ratio: 2), /* Older Firefox browsers (prior to Firefox 16) */ +(min-resolution: 2dppx), /* The standard way */ +(min-resolution: 192dpi) { /* dppx fallback */ + /* Retina-specific stuff here */ +} +``` + +#### [建议] 尽可能给出在高分辨率设备 (Retina) 下效果更佳的样式。 + + + +## 8 兼容性 + + +### 8.1 属性前缀 + + +#### [强制] 带私有前缀的属性由长到短排列,按冒号位置对齐。 + +解释: + +标准属性放在最后,按冒号对齐方便阅读,也便于在编辑器内进行多行编辑。 + + +示例: + +```css +.box { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +``` + + +### 8.2 Hack + + +#### [建议] 需要添加 `hack` 时应尽可能考虑是否可以采用其他方式解决。 + +解释: + +如果能通过合理的 HTML 结构或使用其他的 CSS 定义达到理想的样式,则不应该使用 hack 手段解决问题。通常 hack 会导致维护成本的增加。 + +#### [建议] 尽量使用 `选择器 hack` 处理兼容性,而非 `属性 hack`。 + +解释: + +尽量使用符合 CSS 语法的 selector hack,可以避免一些第三方库无法识别 hack 语法的问题。 + + +示例: + +```css +/* IE 7 */ +*:first-child + html #header { + margin-top: 3px; + padding: 5px; +} + +/* IE 6 */ +* html #header { + margin-top: 5px; + padding: 4px; +} +``` + + +#### [建议] 尽量使用简单的 `属性 hack`。 + +示例: + +```css +.box { + _display: inline; /* fix double margin */ + float: left; + margin-left: 20px; +} + +.container { + overflow: hidden; + *zoom: 1; /* triggering hasLayout */ +} +``` + +### 8.3 Expression + + +#### [强制] 禁止使用 `Expression`。 + + + + + diff --git a/es-next-style-guide.md b/es-next-style-guide.md new file mode 100644 index 0000000..df6000c --- /dev/null +++ b/es-next-style-guide.md @@ -0,0 +1,1234 @@ + +# JavaScript 编码规范 - ESNext 补充篇(草案) + + + + +[1 前言](#user-content-1-%E5%89%8D%E8%A8%80) + +[2 代码风格](#user-content-2-%E4%BB%A3%E7%A0%81%E9%A3%8E%E6%A0%BC) + +  [2.1 文件](#user-content-21-%E6%96%87%E4%BB%B6) + +  [2.2 结构](#user-content-22-%E7%BB%93%E6%9E%84) + +    [2.2.1 缩进](#user-content-221-%E7%BC%A9%E8%BF%9B) + +    [2.2.2 空格](#user-content-222-%E7%A9%BA%E6%A0%BC) + +    [2.2.3 语句](#user-content-223-%E8%AF%AD%E5%8F%A5) + +[3 语言特性](#user-content-3-%E8%AF%AD%E8%A8%80%E7%89%B9%E6%80%A7) + +  [3.1 变量](#user-content-31-%E5%8F%98%E9%87%8F) + +  [3.2 解构](#user-content-32-%E8%A7%A3%E6%9E%84) + +  [3.3 模板字符串](#user-content-33-%E6%A8%A1%E6%9D%BF%E5%AD%97%E7%AC%A6%E4%B8%B2) + +  [3.4 函数](#user-content-34-%E5%87%BD%E6%95%B0) + +  [3.5 箭头函数](#user-content-35-%E7%AE%AD%E5%A4%B4%E5%87%BD%E6%95%B0) + +  [3.6 对象](#user-content-36-%E5%AF%B9%E8%B1%A1) + +  [3.7 类](#user-content-37-%E7%B1%BB) + +  [3.8 模块](#user-content-38-%E6%A8%A1%E5%9D%97) + +  [3.9 集合](#user-content-39-%E9%9B%86%E5%90%88) + +  [3.10 异步](#user-content-310-%E5%BC%82%E6%AD%A5) + +[4 环境](#user-content-4-%E7%8E%AF%E5%A2%83) + +  [4.1 运行环境](#user-content-41-%E8%BF%90%E8%A1%8C%E7%8E%AF%E5%A2%83) + +  [4.2 预编译](#user-content-42-%E9%A2%84%E7%BC%96%E8%AF%91) + + + + + +## 1 前言 + + +随着 ECMAScript 的不断发展,越来越多更新的语言特性将被使用,给应用的开发带来方便。本文档的目标是使 ECMAScript 新特性的代码风格保持一致,并给予一些实践建议。 + +本文档仅包含新特性部分。基础部分请遵循 [JavaScript Style Guide](javascript-style-guide.md)。 + +由于 ECMAScript 依然在快速的不断发展,本文档也将可能随时保持更新。更新内容主要涉及对新增的语言特性的格式规范化、实践指导,引擎与编译器环境变化的使用指导。 + +虽然本文档是针对 ECMAScript 设计的,但是在使用各种基于 ECMAScript 扩展的语言时(如 JSX、TypeScript 等),适用的部分也应尽量遵循本文档的约定。 + + + + + +## 2 代码风格 + + + + + +### 2.1 文件 + + +##### [建议] ESNext 语法的 JavaScript 文件使用 `.js` 扩展名。 + +##### [强制] 当文件无法使用 `.js` 扩展名时,使用 `.es` 扩展名。 + +解释: + +某些应用开发时,可能同时包含 ES 5和 ESNext 文件,运行环境仅支持 ES5,ESNext 文件需要经过预编译。部分场景下,编译工具的选择可能需要通过扩展名区分,需要重新定义ESNext文件的扩展名。此时,ESNext 文件必须使用 `.es` 扩展名。 + +但是,更推荐使用其他条件作为是否需要编译的区分: + +1. 基于文件内容。 +2. 不同类型文件放在不同目录下。 + + + + + +### 2.2 结构 + + +#### 2.2.1 缩进 + + +##### [建议] 使用多行模板字符串时遵循缩进原则。当空行与空白字符敏感时,不使用多行模板字符串。 + +解释: + +`4` 空格为一个缩进,换行后添加一层缩进。将起始和结束的 `` ` `` 符号单独放一行,有助于生成 HTML 时的标签对齐。 + +为避免破坏缩进的统一,当空行与空白字符敏感时,建议使用 `多个模板字符串` 或 `普通字符串` 进行连接运算,也可使用数组 `join` 生成字符串。 + +示例: + +```javascript +// good +function foo() { + let html = ` +
+

+

+
+ `; +} + +// Good +function greeting(name) { + return 'Hello, \n' + + `${name.firstName} ${name.lastName}`; +} + +// Bad +function greeting(name) { + return `Hello, +${name.firstName} ${name.lastName}`; +} +``` + + +#### 2.2.2 空格 + + +##### [强制] 使用 `generator` 时,`*` 前面不允许有空格,`*` 后面必须有一个空格。 + +示例: + +```javascript +// good +function* caller() { + yield 'a'; + yield* callee(); + yield 'd'; +} + +// bad +function * caller() { + yield 'a'; + yield *callee(); + yield 'd'; +} +``` + + +#### 2.2.3 语句 + + +##### [强制] 类声明结束不允许添加分号。 + +解释: + +与函数声明保持一致。 + + +##### [强制] 类成员定义中,方法定义后不允许添加分号,成员属性定义后必须添加分号。 + +解释: + +成员属性是当前 **Stage 0** 的标准,如果使用的话,则定义后加上分号。 + +示例: + +```javascript +// good +class Foo { + foo = 3; + + bar() { + + } +} + +// bad +class Foo { + foo = 3 + + bar() { + + } +} +``` + +##### [强制] `export` 语句后,不允许出现表示空语句的分号。 + +解释: + +`export` 关键字不影响后续语句类型。 + +示例: + +```javascript +// good +export function foo() { +} + +export default function bar() { +} + + +// bad +export function foo() { +}; + +export default function bar() { +}; +``` + + +##### [强制] 属性装饰器后,可以不加分号的场景,不允许加分号。 + +解释: + +只有一种场景是必须加分号的:当属性 `key` 是 `computed property key` 时,其装饰器必须加分号,否则修饰 `key` 的 `[]` 会做为之前表达式的 `property accessor`。 + +上面描述的场景,装饰器后需要加分号。其余场景下的属性装饰器后不允许加分号。 + +示例: + +```javascript +// good +class Foo { + @log('INFO') + bar() { + + } + + @log('INFO'); + ['bar' + 2]() { + + } +} + +// bad +class Foo { + @log('INFO'); + bar() { + + } + + @log('INFO') + ['bar' + 2]() { + + } +} +``` + + +##### [强制] 箭头函数的参数只有一个,并且不包含解构时,参数部分的括号必须省略。 + +示例: + +```javascript +// good +list.map(item => item * 2); + +// good +let fetchName = async id => { + let user = await request(`users/${id}`); + return user.fullName; +}; + +// bad +list.map((item) => item * 2); + +// bad +let fetchName = async (id) => { + let user = await request(`users/${id}`); + return user.fullName; +}; +``` + +##### [建议] 箭头函数的函数体只有一个单行表达式语句,且作为返回值时,省略 `{}` 和 `return`。 + +如果单个表达式过长,可以使用 `()` 进行包裹。 + +示例: + +```javascript +// good +list.map(item => item * 2); + +let foo = () => ( + condition + ? returnValueA() + : returnValueB() +); + +// bad +list.map(item => { + return item * 2; +}); +``` + +##### [建议] 箭头函数的函数体只有一个 `Object Literal`,且作为返回值时,使用 `()` 包裹。 + +示例: + +```javascript +// good +list.map(item => ({name: item[0], email: item[1]})); +``` + +##### [强制] 解构多个变量时,如果超过行长度限制,每个解构的变量必须单独一行。 + +解释: + +太多的变量解构会让一行的代码非常长,极有可能超过单行长度控制,使代码可读性下降。 + +示例: + +```javascript +// good +let { + name: personName, + email: personEmail, + sex: personSex, + age: personAge +} = person; + +// bad +let {name: personName, email: personEmail, + sex: personSex, age: personAge +} = person; +``` + + + + + + +## 3 语言特性 + + + + + +### 3.1 变量 + + +#### [强制] 使用 `let` 和 `const` 定义变量,不使用 `var`。 + +解释: + +使用 `let` 和 `const` 定义时,变量作用域范围更明确。 + +示例: + +```javascript +// good +for (let i = 0; i < 10; i++) { + +} + +// bad +for (var i = 0; i < 10; i++) { + +} +``` + + + +### 3.2 解构 + + +#### [强制] 不要使用3层及以上的解构。 + +解释: + +过多层次的解构会让代码变得难以阅读。 + +示例: + +```javascript +// bad +let {documentElement: {firstElementChild: {nextSibling}}} = window; +``` + +#### [建议] 使用解构减少中间变量。 + +解释: + +常见场景如变量值交换,可能产生中间变量。这种场景推荐使用解构。 + +示例: + +```javascript +// good +[x, y] = [y, x]; + +// bad +let temp = x; +x = y; +y = temp; +``` + +#### [强制] 仅定义一个变量时不允许使用解构。 + +解释: + +在这种场景下,使用解构将降低代码可读性。 + +示例: + +```javascript +// good +let len = myString.length; + +// bad +let {length: len} = myString; +``` + +#### [强制] 如果不节省编写时产生的中间变量,解构表达式 `=` 号右边不允许是 `ObjectLiteral` 和 `ArrayLiteral`。 + +解释: + +在这种场景下,使用解构将降低代码可读性,通常也并无收益。 + +示例: + +```javascript +// good +let {first: firstName, last: lastName} = person; +let one = 1; +let two = 2; + +// bad +let [one, two] = [1, 2]; +``` + +#### [强制] 使用剩余运算符时,剩余运算符之前的所有元素必需具名。 + +解释: + +剩余运算符之前的元素省略名称可能带来较大的程序阅读障碍。如果仅仅为了取数组后几项,请使用 `slice` 方法。 + +示例: + +```javascript +// good +let [one, two, ...anyOther] = myArray; +let other = myArray.slice(3); + +// bad +let [,,, ...other] = myArray; +``` + + + +### 3.3 模板字符串 + + + +#### [强制] 字符串内变量替换时,不要使用 `2` 次及以上的函数调用。 + +解释: + +在变量替换符内有太多的函数调用等复杂语法会导致可读性下降。 + +示例: + +```javascript +// good +let fullName = getFullName(getFirstName(), getLastName()); +let s = `Hello ${fullName}`; + +// bad +let s = `Hello ${getFullName(getFirstName(), getLastName())}`; +``` + + + +### 3.4 函数 + + +#### [建议] 使用变量默认语法代替基于条件判断的默认值声明。 + +解释: + +添加默认值有助于引擎的优化,在未来 `strong mode` 下也会有更好的效果。 + +示例: + +```javascript +// good +function foo(text = 'hello') { +} + +// bad +function foo(text) { + text = text || 'hello'; +} +``` + + +#### [强制] 不要使用 `arguments` 对象,应使用 `...args` 代替。 + +解释: + +在未来 `strong mode` 下 `arguments` 将被禁用。 + +示例: + +```javascript +// good +function foo(...args) { + console.log(args.join('')); +} + +// bad +function foo() { + console.log([].join.call(arguments)); +} +``` + + + + +### 3.5 箭头函数 + + + +#### [强制] 一个函数被设计为需要 `call` 和 `apply` 的时候,不能是箭头函数。 + +解释: + +箭头函数会强制绑定当前环境下的 `this`。 + + + +### 3.6 对象 + + +#### [建议] 定义对象时,如果所有键均指向同名变量,则所有键都使用缩写;如果有一个键无法指向同名变量,则所有键都不使用缩写。 + +解释: + +目的在于保持所有键值对声明的一致性。 + +```javascript +// good +let foo = {x, y, z}; + +let foo2 = { + x: 1, + y: 2, + z: z +}; + + +// bad +let foo = { + x: x, + y: y, + z: z +}; + +let foo2 = { + x: 1, + y: 2, + z +}; +``` + +#### [强制] 定义方法时使用 `MethodDefinition` 语法,不使用 `PropertyName: FunctionExpression` 语法。 + +解释: + +`MethodDefinition` 语法更清晰简洁。 + +示例: + +```javascript +// good +let foo = { + bar(x, y) { + return x + y; + } +}; + +// bad +let foo = { + bar: function (x, y) { + return x + y; + } +}; +``` + +#### [建议] 使用 `Object.keys` 或 `Object.entries` 进行对象遍历。 + +解释: + +不建议使用 `for .. in` 进行对象的遍历,以避免遗漏 `hasOwnProperty` 产生的错误。 + +示例: + +```javascript +// good +for (let key of Object.keys(foo)) { + let value = foo[key]; +} + +// good +for (let [key, value] of Object.entries(foo)) { + // ... +} +``` + +#### [建议] 定义对象的方法不应使用箭头函数。 + +解释: + +箭头函数将 `this` 绑定到当前环境,在 `obj.method()` 调用时容易导致不期待的 `this`。除非明确需要绑定 `this`,否则不应使用箭头函数。 + +示例: + +```javascript +// good +let foo = { + bar(x, y) { + return x + y; + } +}; + +// bad +let foo = { + bar: (x, y) => x + y +}; +``` + + +#### [建议] 尽量使用计算属性键在一个完整的字面量中完整地定义一个对象,避免对象定义后直接增加对象属性。 + +解释: + +在一个完整的字面量中声明所有的键值,而不需要将代码分散开来,有助于提升代码可读性。 + +示例: + +```javascript +// good +const MY_KEY = 'bar'; +let foo = { + [MY_KEY + 'Hash']: 123 +}; + +// bad +const MY_KEY = 'bar'; +let foo = {}; +foo[MY_KEY + 'Hash'] = 123; +``` + + + + + +### 3.7 类 + + + +#### [强制] 使用 `class` 关键字定义一个类。 + +解释: + +直接使用 `class` 定义类更清晰。不要再使用 `function` 和 `prototype` 形式的定义。 + +```javascript +// good +class TextNode { + constructor(value, engine) { + this.value = value; + this.engine = engine; + } + + clone() { + return this; + } +} + +// bad +function TextNode(value, engine) { + this.value = value; + this.engine = engine; +} + +TextNode.prototype.clone = function () { + return this; +}; +``` + +#### [强制] 使用 `super` 访问父类成员,而非父类的 `prototype`。 + +解释: + +使用 `super` 和 `super.foo` 可以快速访问父类成员,而不必硬编码父类模块而导致修改和维护的不便,同时更节省代码。 + +```javascript +// good +class TextNode extends Node { + constructor(value, engine) { + super(value); + this.engine = engine; + } + + setNodeValue(value) { + super.setNodeValue(value); + this.textContent = value; + } +} + +// bad +class TextNode extends Node { + constructor(value, engine) { + Node.apply(this, arguments); + this.engine = engine; + } + + setNodeValue(value) { + Node.prototype.setNodeValue.call(this, value); + this.textContent = value; + } +} +``` + + + +### 3.8 模块 + + + +#### [强制] `export` 与内容定义放在一起。 + +解释: + +何处声明要导出的东西,就在何处使用 `export` 关键字,不在声明后再统一导出。 + +示例: + +```javascript +// good +export function foo() { +} + +export const bar = 3; + + +// bad +function foo() { +} + +const bar = 3; + +export {foo}; +export {bar}; +``` + +#### [建议] 相互之间无关联的内容使用命名导出。 + +解释: + +举个例子,工具对象中的各个方法,相互之间并没有强关联,通常外部会选择几个使用,则应该使用命名导出。 + +简而言之,当一个模块只扮演命名空间的作用时,使用命名导出。 + + + +#### [强制] 所有 `import` 语句写在模块开始处。 + +示例: + +```javascript +// good +import {bar} from './bar'; + +function foo() { + bar.work(); +} + +// bad +function foo() { + import {bar} from './bar'; + bar.work(); +} +``` + + + + +### 3.9 集合 + + +#### [建议] 对数组进行连接操作时,使用数组展开语法。 + +解释: + +用数组展开代替 `concat` 方法,数组展开对 `Iterable` 有更好的兼容性。 + +示例: + +```javascript +// good +let foo = [...foo, newValue]; +let bar = [...bar, ...newValues]; + +// bad +let foo = foo.concat(newValue); +let bar = bar.concat(newValues); +``` + +#### [建议] 不要使用数组展开进行数组的复制操作。 + +解释: + +使用数组展开语法进行复制,代码可读性较差。推荐使用 `Array.from` 方法进行复制操作。 + +示例: + +```javascript +// good +let otherArr = Array.from(arr); + +// bad +let otherArr = [...arr]; +``` + +#### [建议] 尽可能使用 `for .. of` 进行遍历。 + +解释: + +使用 `for .. of` 可以更好地接受任何的 `Iterable` 对象,如 `Map#values` 生成的迭代器,使得方法的通用性更强。 + +以下情况除外: + +1. 遍历确实成为了性能瓶颈,需要使用原生 `for` 循环提升性能。 +2. 需要遍历过程中的索引。 + + +#### [强制] 当键值有可能不是字符串时,必须使用 `Map`;当元素有可能不是字符串时,必须使用 `Set`。 + +解释: + +使用普通 Object,对非字符串类型的 `key`,需要自己实现序列化。并且运行过程中的对象变化难以通知 Object。 + + +#### [建议] 需要一个不可重复的集合时,应使用 `Set`。 + +解释: + +不要使用 `{foo: true}` 这样的普通 `Object`。 + +示例: + +```javascript +// good +let members = new Set(['one', 'two', 'three']); + +// bad +let members = { + one: true, + two: true, + three: true +}; +``` + + +#### [建议] 当需要遍历功能时,使用 `Map` 和 `Set`。 + +解释: + +`Map` 和 `Set` 是可遍历对象,能够方便地使用 `for...of` 遍历。不要使用使用普通 Object。 + +示例: + +```javascript +// good +let membersAge = new Map([ + ['one', 10], + ['two', 20], + ['three', 30] +]); +for (let [key, value] of map) { +} + +// bad +let membersAge = { + one: 10, + two: 20, + three: 30 +}; +for (let key in membersAge) { + if (membersAge.hasOwnProperty(key)) { + let value = membersAge[key]; + } +} +``` + +#### [建议] 程序运行过程中有添加或移除元素的操作时,使用 `Map` 和 `Set`。 + +解释: + +使用 `Map` 和 `Set`,程序的可理解性更好;普通 Object 的语义更倾向于表达固定的结构。 + +示例: + +```javascript +// good +let membersAge = new Map(); +membersAge.set('one', 10); +membersAge.set('two', 20); +membersAge.set('three', 30); +membersAge.delete('one'); + +// bad +let membersAge = {}; +membersAge.one = 10; +membersAge.two = 20; +membersAge.three = 30; +delete membersAge['one']; +``` + + + + +### 3.10 异步 + + +#### [强制] 回调函数的嵌套不得超过3层。 + +解释: + +深层次的回调函数的嵌套会让代码变得难以阅读。 + +示例: + +```javascript +// bad +getUser(userId, function (user) { + validateUser(user, function (isValid) { + if (isValid) { + saveReport(report, user, function () { + notice('Saved!'); + }); + } + }); +}); +``` + +#### [建议] 使用 `Promise` 代替 `callback`。 + +解释: + +相比 `callback`,使用 `Promise` 能够使复杂异步过程的代码更清晰。 + +示例: + +```javascript +// good +let user; +getUser(userId) + .then(function (userObj) { + user = userObj; + return validateUser(user); + }) + .then(function (isValid) { + if (isValid) { + return saveReport(report, user); + } + + return Promise.reject('Invalid!'); + }) + .then( + function () { + notice('Saved!'); + }, + function (message) { + notice(message); + } + ); +``` + + +#### [强制] 使用标准的 `Promise` API。 + +解释: + +1. 不允许使用非标准的 `Promise` API,如 `jQuery` 的 `Deferred`、`Q.js` 的 `defer` 等。 +2. 不允许使用非标准的 `Promise` 扩展 API,如 `bluebird` 的 `Promise.any` 等。 + +使用标准的 `Promise` API,当运行环境都支持时,可以把 Promise Lib 直接去掉。 + + +#### [强制] 不允许直接扩展 `Promise` 对象的 `prototype`。 + +解释: + +理由和 **不允许修改和扩展任何原生对象和宿主对象的原型** 是一样的。如果想要使用更方便,可以用 utility 函数的形式。 + + +#### [强制] 不得为了编写的方便,将可以并行的IO过程串行化。 + +解释: + +并行 IO 消耗时间约等于 IO 时间最大的那个过程,串行的话消耗时间将是所有过程的时间之和。 + +示例: + +```javascript +requestData().then(function (data) { + renderTags(data.tags); + renderArticles(data.articles); +}); + +// good +async function requestData() { + const [tags, articles] = await Promise.all([ + requestTags(), + requestArticles() + ]); + return {tags, articles}; +} + +// bad +async function requestData() { + let tags = await requestTags(); + let articles = await requestArticles(); + + return Promise.resolve({tags, articles}); +} +``` + +#### [建议] 使用 `async/await` 代替 `generator` + `co`。 + +解释: + +使用语言自身的能力可以使代码更清晰,也无需引入 `co` 库。 + +示例: + +```javascript +addReport(report, userId).then( + function () { + notice('Saved!'); + }, + function (message) { + notice(message); + } +); + +// good +async function addReport(report, userId) { + let user = await getUser(userId); + let isValid = await validateUser(user); + + if (isValid) { + let savePromise = saveReport(report, user); + return savePromise(); + } + + return Promise.reject('Invalid'); +} + +// bad +function addReport(report, userId) { + return co(function* () { + let user = yield getUser(userId); + let isValid = yield validateUser(user); + + if (isValid) { + let savePromise = saveReport(report, user); + return savePromise(); + } + + return Promise.reject('Invalid'); + }); +} +``` + + + + + + + + + +## 4 环境 + + + + + + +### 4.1 运行环境 + + +#### [建议] 持续跟进与关注运行环境对语言特性的支持程度。 + +解释: + +[查看环境对语言特性的支持程度](https://kangax.github.io/compat-table/es6/) + +ES 标准的制定还在不断进行中,各种环境对语言特性的支持也日新月异。了解项目中用到了哪些 ESNext 的特性,了解项目的运行环境,并持续跟进这些特性在运行环境中的支持程度是很有必要的。这意味着: + +1. 如果有任何一个运行环境(比如 chrome)支持了项目里用到的所有特性,你可以在开发时抛弃预编译。 +2. 如果所有环境都支持了某一特性(比如 Promise),你可以抛弃相关的 shim,或无需在预编译时进行转换。 +3. 如果所有环境都支持了项目里用到的所有特性,你可以完全抛弃预编译。 + +无论如何,在选择预编译工具时,你都需要清晰的知道你现阶段将在项目里使用哪些语言特性,然后了解预编译工具对语言特性的支持程度,做出选择。 + + +#### [强制] 在运行环境中没有 `Promise` 时,将 `Promise` 的实现 `shim` 到 `global` 中。 + +解释: + +当前运行环境下没有 `Promise` 时,可以引入 `shim` 的扩展。如果自己实现,需要实现在 `global` 下,并且与标准 API 保持一致。 + +这样,未来运行环境支持时,可以随时把 `Promise` 扩展直接扔掉,而应用代码无需任何修改。 + + + + + +### 4.2 预编译 + + +#### [建议] 使用 `babel` 做为预编译工具时,建议使用 `5.x` 版本。 + +解释: + +由于 `babel` 最新的 `6` 暂时还不稳定,建议暂时使用 `5.x`。不同的产品,对于浏览器支持的情况不同,使用 `babel` 的时候,需要设置的参数也有一些区别。下面在示例中给出一些建议的参数。 + +示例: + +```shell +# 建议的参数 +--loose all --modules amd --blacklist strict + +# 如果需要使用 es7.classProperties、es7.decorators 等一些特性,需要额外的 --stage 0 参数 +--loose all --modules amd --blacklist strict --stage 0 +``` + + +#### [建议] 使用 `babel` 做为预编译工具时,通过 `external-helpers` 减少生成文件的大小。 + +解释: + +当 `babel` 在转换代码的过程中发现需要一些特性时,会在该文件头部生成对应的 `helper` 代码。默认情况下,对于每一个经由 `babel` 处理的文件,均会在文件头部生成对应需要的辅助函数,多份文件辅助函数存在重复,占用了不必要的代码体积。 + +因此推荐打开`externalHelpers: true`选项,使 `babel` 在转换后内容中不写入 `helper` 相关的代码,而是使用一个外部的 `.js`统一提供所有的 `helper`。对于[external-helpers](https://github.com/babel/babel.github.io/blob/5.0.0/docs/usage/external-helpers.md)的使用,可以有两种方式: + +1. 默认方式:需要通过 ` ``` #### [建议] 每行不得超过 `120` 个字符。 @@ -105,7 +119,7 @@ HTML作为描述网页结构的超文本标记语言,在百度一直有着广 解释: -同一个页面中,不同的元素包含相同的 id,不符合 id 的属性含义。并且使用 document.getElementById 时可能导致难以追查的问题。 +同一个页面中,不同的元素包含相同的 `id`,不符合 `id` 的属性含义。并且使用 `document.getElementById` 时可能导致难以追查的问题。 #### [建议] `id` 建议单词全字母小写,单词间以 `-` 分隔。同项目必须保持风格一致。 @@ -136,18 +150,18 @@ HTML作为描述网页结构的超文本标记语言,在百度一直有着广 解释: -不允许 class 只用于让 JavaScript 选择某些元素,class 应该具有明确的语义和样式。否则容易导致 css class 泛滥。 +不允许 `class` 只用于让 JavaScript 选择某些元素,`class` 应该具有明确的语义和样式。否则容易导致 CSS class 泛滥。 -使用 id、属性选择作为 hook 是更好的方式。 +使用 `id`、属性选择作为 hook 是更好的方式。 #### [强制] 同一页面,应避免使用相同的 `name` 与 `id`。 解释: -IE 浏览器会混淆元素的 id 和 name 属性, document.getElementById 可能获得不期望的元素。所以在对元素的 id 与 name 属性的命名需要非常小心。 +IE 浏览器会混淆元素的 `id` 和 `name` 属性, `document.getElementById` 可能获得不期望的元素。所以在对元素的 `id` 与 `name` 属性的命名需要非常小心。 -一个比较好的实践是,为 id 和 name 使用不同的命名法。 +一个比较好的实践是,为 `id` 和 `name` 使用不同的命名法。 示例: @@ -180,7 +194,7 @@ alert(document.getElementById('foo').tagName); 解释: -常见无需自闭合标签有input、br、img、hr等。 +常见无需自闭合标签有 `input`、`br`、`img`、`hr` 等。 示例: @@ -221,12 +235,12 @@ alert(document.getElementById('foo').tagName); 解释: -比如 div 不得置于 p 中,tbody 必须置于 table 中。 +比如 `div` 不得置于 `p` 中,`tbody` 必须置于 `table` 中。 详细的标签嵌套规则参见[HTML DTD](http://www.cs.tut.fi/~jkorpela/html5.dtd)中的 `Elements` 定义部分。 -#### [建议] `HTML` 标签的使用应该遵循标签的语义。 +#### [建议] HTML 标签的使用应该遵循标签的语义。 解释: @@ -258,7 +272,7 @@ alert(document.getElementById('foo').tagName); ``` -#### [建议] 在 `CSS` 可以实现相同需求的情况下不得使用表格进行布局。 +#### [建议] 在 CSS 可以实现相同需求的情况下不得使用表格进行布局。 解释: @@ -340,7 +354,6 @@ alert(document.getElementById('foo').tagName); - ## 3 通用 @@ -363,7 +376,7 @@ alert(document.getElementById('foo').tagName); ``` -#### [建议] 在 `html` 标签上设置正确的 lang 属性。 +#### [建议] 在 `html` 标签上设置正确的 `lang` 属性。 解释: @@ -404,11 +417,11 @@ alert(document.getElementById('foo').tagName); 解释: -UTF-8 编码具有更广泛的适应性。BOM 在使用程序或工具处理文件时可能造成不必要的干扰。 +`UTF-8` 编码具有更广泛的适应性。`BOM` 在使用程序或工具处理文件时可能造成不必要的干扰。 -### 3.3 CSS和JavaScript引入 +### 3.3 CSS 和 JavaScript 引入 #### [强制] 引入 `CSS` 时必须指明 `rel="stylesheet"`。 @@ -416,7 +429,7 @@ UTF-8 编码具有更广泛的适应性。BOM 在使用程序或工具处理文 示例: ```html - + ``` @@ -424,7 +437,7 @@ UTF-8 编码具有更广泛的适应性。BOM 在使用程序或工具处理文 解释: -`text/css` 和 `text/javascript` 是 type 的默认值。 +`text/css` 和 `text/javascript` 是 `type` 的默认值。 #### [建议] 展现定义放置于外部 `CSS` 中,行为定义放置于外部 `JavaScript` 中。 @@ -445,7 +458,7 @@ UTF-8 编码具有更广泛的适应性。BOM 在使用程序或工具处理文 解释: -将 script 放在页面中间将阻断页面的渲染。出于性能方面的考虑,如非必要,请遵守此条建议。 +将 `script` 放在页面中间将阻断页面的渲染。出于性能方面的考虑,如非必要,请遵守此条建议。 示例: @@ -488,7 +501,7 @@ UTF-8 编码具有更广泛的适应性。BOM 在使用程序或工具处理文 解释: -title 中如果包含 ascii 之外的字符,浏览器需要知道字符编码类型才能进行解码,否则可能导致乱码。 +`title` 中如果包含 ASCII 之外的字符,浏览器需要知道字符编码类型才能进行解码,否则可能导致乱码。 示例: @@ -507,10 +520,10 @@ title 中如果包含 ascii 之外的字符,浏览器需要知道字符编码 解释: -在未指定 favicon 时,大多数浏览器会请求 Web Server 根目录下的 favicon.ico 。为了保证favicon可访问,避免404,必须遵循以下两种方法之一: +在未指定 favicon 时,大多数浏览器会请求 Web Server 根目录下的 `favicon.ico` 。为了保证 favicon 可访问,避免 404,必须遵循以下两种方法之一: -1. 在 Web Server 根目录放置 favicon.ico 文件。 -2. 使用 link 指定 favicon。 +1. 在 Web Server 根目录放置 `favicon.ico` 文件。 +2. 使用 `link` 指定 favicon。 示例: @@ -526,9 +539,9 @@ title 中如果包含 ascii 之外的字符,浏览器需要知道字符编码 解释: -viewport meta tag可以设置可视区域的宽度和初始缩放大小,避免在移动设备上出现页面展示不正常。 +viewport meta tag 可以设置可视区域的宽度和初始缩放大小,避免在移动设备上出现页面展示不正常。 -比如,在页面宽度小于 980px 时,若需 iOS 设备友好,应当设置 viewport 的 width 值来适应你的页面宽度。同时因为不同移动设备分辨率不同,在设置时,应当使用 device-width 和 device-height 变量。 +比如,在页面宽度小于 `980px` 时,若需 iOS 设备友好,应当设置 viewport 的 `width` 值来适应你的页面宽度。同时因为不同移动设备分辨率不同,在设置时,应当使用 `device-width` 和 `device-height` 变量。 另外,为了使 viewport 正常工作,在页面内容样式布局设计上也要做相应调整,如避免绝对定位等。关于 viewport 的更多介绍,可以参见 [Safari Web Content Guide的介绍](https://developer.apple.com/library/mac/documentation/AppleApplications/Reference/SafariWebContent/UsingtheViewport/UsingtheViewport.html#//apple_ref/doc/uid/TP40006509-SW26) @@ -543,14 +556,14 @@ viewport meta tag可以设置可视区域的宽度和初始缩放大小,避免 解释: -src 取值为空,会导致部分浏览器重新加载一次当前页面,参考: +`src` 取值为空,会导致部分浏览器重新加载一次当前页面,参考: #### [建议] 避免为 `img` 添加不必要的 `title` 属性。 -解释: +解释: -多余的 title 影响看图体验,并且增加了页面尺寸。 +多余的 `title` 影响看图体验,并且增加了页面尺寸。 #### [建议] 为重要图片添加 `alt` 属性。 @@ -560,12 +573,12 @@ src 取值为空,会导致部分浏览器重新加载一次当前页面,参 #### [建议] 添加 `width` 和 `height` 属性,以避免页面抖动。 -#### [建议] 有下载需求的图片采用 `img` 标签实现,无下载需求的图片采用 `CSS` 背景图实现。 +#### [建议] 有下载需求的图片采用 `img` 标签实现,无下载需求的图片采用 CSS 背景图实现。 解释: -1. 产品 logo、用户头像、用户产生的图片等有潜在下载需求的图片,以 img 形式实现,能方便用户下载。 -2. 无下载需求的图片,比如:icon、背景、代码使用的图片等,尽可能采用 css 背景图实现。 +1. 产品 logo、用户头像、用户产生的图片等有潜在下载需求的图片,以 `img` 形式实现,能方便用户下载。 +2. 无下载需求的图片,比如:icon、背景、代码使用的图片等,尽可能采用 CSS 背景图实现。 @@ -581,10 +594,10 @@ src 取值为空,会导致部分浏览器重新加载一次当前页面,参 有两种方式: -1. 将控件置于 label 内。 -2. label 的 for 属性指向控件的 id。 +1. 将控件置于 `label` 内。 +2. `label` 的 `for` 属性指向控件的 `id`。 -推荐使用第一种,减少不必要的 id。如果 DOM 结构不允许直接嵌套,则应使用第二种。 +推荐使用第一种,减少不必要的 `id`。如果 DOM 结构不允许直接嵌套,则应使用第二种。 示例: @@ -603,7 +616,7 @@ src 取值为空,会导致部分浏览器重新加载一次当前页面,参 解释: -button 元素的默认 type 为 submit,如果被置于 form 元素中,点击后将导致表单提交。为显示区分其作用方便理解,必须给出 type 属性。 +`button` 元素的默认 `type` 为 `submit`,如果被置于 `form` 元素中,点击后将导致表单提交。为显示区分其作用方便理解,必须给出 `type` 属性。 示例: @@ -617,13 +630,13 @@ button 元素的默认 type 为 submit,如果被置于 form 元素中,点击 解释: -由于浏览器兼容性问题,使用按钮的 name 属性会带来许多难以发现的问题。具体情况可参考[此文](http://w3help.org/zh-cn/causes/CM2001)。 +由于浏览器兼容性问题,使用按钮的 `name` 属性会带来许多难以发现的问题。具体情况可参考[此文](http://w3help.org/zh-cn/causes/CM2001)。 ### 6.3 可访问性 (A11Y) -#### [建议] 负责主要功能的按钮在 `DOM` 中的顺序应靠前。 +#### [建议] 负责主要功能的按钮在 DOM 中的顺序应靠前。 解释: @@ -660,11 +673,11 @@ button 元素的默认 type 为 submit,如果被置于 form 元素中,点击 ``` -#### [建议] 当使用 `JavaScript` 进行表单提交时,如果条件允许,应使原生提交功能正常工作。 +#### [建议] 当使用 JavaScript 进行表单提交时,如果条件允许,应使原生提交功能正常工作。 解释: -当浏览器 JS 运行错误或关闭 JS 时,提交功能将无法工作。如果正确指定了 form 元素的 action 属性和表单控件的 name 属性时,提交仍可继续进行。 +当浏览器 JS 运行错误或关闭 JS 时,提交功能将无法工作。如果正确指定了 `form` 元素的 `action` 属性和表单控件的 `name` 属性时,提交仍可继续进行。 示例: @@ -754,7 +767,7 @@ button 元素的默认 type 为 submit,如果被置于 form 元素中,点击 ## 8 模板中的 HTML -#### [建议] 模板代码的缩进优先保证 `HTML` 代码的缩进规则。 +#### [建议] 模板代码的缩进优先保证 HTML 代码的缩进规则。 示例: @@ -782,7 +795,7 @@ button 元素的默认 type 为 submit,如果被置于 form 元素中,点击 {/if} ``` -#### [建议] 模板代码应以保证 `HTML` 单个标签语法的正确性为基本原则。 +#### [建议] 模板代码应以保证 HTML 单个标签语法的正确性为基本原则。 示例: diff --git a/javascript-style-guide.md b/javascript-style-guide.md index 959a5dd..08fae91 100644 --- a/javascript-style-guide.md +++ b/javascript-style-guide.md @@ -4,117 +4,117 @@ -[1 前言](#1-%E5%89%8D%E8%A8%80) +[1 前言](#user-content-1-%E5%89%8D%E8%A8%80) -[2 代码风格](#2-%E4%BB%A3%E7%A0%81%E9%A3%8E%E6%A0%BC) +[2 代码风格](#user-content-2-%E4%BB%A3%E7%A0%81%E9%A3%8E%E6%A0%BC) -  [2.1 文件](#21-%E6%96%87%E4%BB%B6) +  [2.1 文件](#user-content-21-%E6%96%87%E4%BB%B6) -  [2.2 结构](#22-%E7%BB%93%E6%9E%84) +  [2.2 结构](#user-content-22-%E7%BB%93%E6%9E%84) -    [2.2.1 缩进](#221-%E7%BC%A9%E8%BF%9B) +    [2.2.1 缩进](#user-content-221-%E7%BC%A9%E8%BF%9B) -    [2.2.2 空格](#222-%E7%A9%BA%E6%A0%BC) +    [2.2.2 空格](#user-content-222-%E7%A9%BA%E6%A0%BC) -    [2.2.3 换行](#223-%E6%8D%A2%E8%A1%8C) +    [2.2.3 换行](#user-content-223-%E6%8D%A2%E8%A1%8C) -    [2.2.4 语句](#224-%E8%AF%AD%E5%8F%A5) +    [2.2.4 语句](#user-content-224-%E8%AF%AD%E5%8F%A5) -  [2.3 命名](#23-%E5%91%BD%E5%90%8D) +  [2.3 命名](#user-content-23-%E5%91%BD%E5%90%8D) -  [2.4 注释](#24-%E6%B3%A8%E9%87%8A) +  [2.4 注释](#user-content-24-%E6%B3%A8%E9%87%8A) -    [2.4.1 单行注释](#241-%E5%8D%95%E8%A1%8C%E6%B3%A8%E9%87%8A) +    [2.4.1 单行注释](#user-content-241-%E5%8D%95%E8%A1%8C%E6%B3%A8%E9%87%8A) -    [2.4.2 多行注释](#242-%E5%A4%9A%E8%A1%8C%E6%B3%A8%E9%87%8A) +    [2.4.2 多行注释](#user-content-242-%E5%A4%9A%E8%A1%8C%E6%B3%A8%E9%87%8A) -    [2.4.3 文档化注释](#243-%E6%96%87%E6%A1%A3%E5%8C%96%E6%B3%A8%E9%87%8A) +    [2.4.3 文档化注释](#user-content-243-%E6%96%87%E6%A1%A3%E5%8C%96%E6%B3%A8%E9%87%8A) -    [2.4.4 类型定义](#244-%E7%B1%BB%E5%9E%8B%E5%AE%9A%E4%B9%89) +    [2.4.4 类型定义](#user-content-244-%E7%B1%BB%E5%9E%8B%E5%AE%9A%E4%B9%89) -    [2.4.5 文件注释](#245-%E6%96%87%E4%BB%B6%E6%B3%A8%E9%87%8A) +    [2.4.5 文件注释](#user-content-245-%E6%96%87%E4%BB%B6%E6%B3%A8%E9%87%8A) -    [2.4.6 命名空间注释](#246-%E5%91%BD%E5%90%8D%E7%A9%BA%E9%97%B4%E6%B3%A8%E9%87%8A) +    [2.4.6 命名空间注释](#user-content-246-%E5%91%BD%E5%90%8D%E7%A9%BA%E9%97%B4%E6%B3%A8%E9%87%8A) -    [2.4.7 类注释](#247-%E7%B1%BB%E6%B3%A8%E9%87%8A) +    [2.4.7 类注释](#user-content-247-%E7%B1%BB%E6%B3%A8%E9%87%8A) -    [2.4.8 函数/方法注释](#248-%E5%87%BD%E6%95%B0/%E6%96%B9%E6%B3%95%E6%B3%A8%E9%87%8A) +    [2.4.8 函数/方法注释](#user-content-248-%E5%87%BD%E6%95%B0/%E6%96%B9%E6%B3%95%E6%B3%A8%E9%87%8A) -    [2.4.9 事件注释](#249-%E4%BA%8B%E4%BB%B6%E6%B3%A8%E9%87%8A) +    [2.4.9 事件注释](#user-content-249-%E4%BA%8B%E4%BB%B6%E6%B3%A8%E9%87%8A) -    [2.4.10 常量注释](#2410-%E5%B8%B8%E9%87%8F%E6%B3%A8%E9%87%8A) +    [2.4.10 常量注释](#user-content-2410-%E5%B8%B8%E9%87%8F%E6%B3%A8%E9%87%8A) -    [2.4.11 复杂类型注释](#2411-%E5%A4%8D%E6%9D%82%E7%B1%BB%E5%9E%8B%E6%B3%A8%E9%87%8A) +    [2.4.11 复杂类型注释](#user-content-2411-%E5%A4%8D%E6%9D%82%E7%B1%BB%E5%9E%8B%E6%B3%A8%E9%87%8A) -    [2.4.12 AMD 模块注释](#2412-amd-%E6%A8%A1%E5%9D%97%E6%B3%A8%E9%87%8A) +    [2.4.12 AMD 模块注释](#user-content-2412-amd-%E6%A8%A1%E5%9D%97%E6%B3%A8%E9%87%8A) -    [2.4.13 细节注释](#2413-%E7%BB%86%E8%8A%82%E6%B3%A8%E9%87%8A) +    [2.4.13 细节注释](#user-content-2413-%E7%BB%86%E8%8A%82%E6%B3%A8%E9%87%8A) -[3 语言特性](#3-%E8%AF%AD%E8%A8%80%E7%89%B9%E6%80%A7) +[3 语言特性](#user-content-3-%E8%AF%AD%E8%A8%80%E7%89%B9%E6%80%A7) -  [3.1 变量](#31-%E5%8F%98%E9%87%8F) +  [3.1 变量](#user-content-31-%E5%8F%98%E9%87%8F) -  [3.2 条件](#32-%E6%9D%A1%E4%BB%B6) +  [3.2 条件](#user-content-32-%E6%9D%A1%E4%BB%B6) -  [3.3 循环](#33-%E5%BE%AA%E7%8E%AF) +  [3.3 循环](#user-content-33-%E5%BE%AA%E7%8E%AF) -  [3.4 类型](#34-%E7%B1%BB%E5%9E%8B) +  [3.4 类型](#user-content-34-%E7%B1%BB%E5%9E%8B) -    [3.4.1 类型检测](#341-%E7%B1%BB%E5%9E%8B%E6%A3%80%E6%B5%8B) +    [3.4.1 类型检测](#user-content-341-%E7%B1%BB%E5%9E%8B%E6%A3%80%E6%B5%8B) -    [3.4.2 类型转换](#342-%E7%B1%BB%E5%9E%8B%E8%BD%AC%E6%8D%A2) +    [3.4.2 类型转换](#user-content-342-%E7%B1%BB%E5%9E%8B%E8%BD%AC%E6%8D%A2) -  [3.5 字符串](#35-%E5%AD%97%E7%AC%A6%E4%B8%B2) +  [3.5 字符串](#user-content-35-%E5%AD%97%E7%AC%A6%E4%B8%B2) -  [3.6 对象](#36-%E5%AF%B9%E8%B1%A1) +  [3.6 对象](#user-content-36-%E5%AF%B9%E8%B1%A1) -  [3.7 数组](#37-%E6%95%B0%E7%BB%84) +  [3.7 数组](#user-content-37-%E6%95%B0%E7%BB%84) -  [3.8 函数](#38-%E5%87%BD%E6%95%B0) +  [3.8 函数](#user-content-38-%E5%87%BD%E6%95%B0) -    [3.8.1 函数长度](#381-%E5%87%BD%E6%95%B0%E9%95%BF%E5%BA%A6) +    [3.8.1 函数长度](#user-content-381-%E5%87%BD%E6%95%B0%E9%95%BF%E5%BA%A6) -    [3.8.2 参数设计](#382-%E5%8F%82%E6%95%B0%E8%AE%BE%E8%AE%A1) +    [3.8.2 参数设计](#user-content-382-%E5%8F%82%E6%95%B0%E8%AE%BE%E8%AE%A1) -    [3.8.3 闭包](#383-%E9%97%AD%E5%8C%85) +    [3.8.3 闭包](#user-content-383-%E9%97%AD%E5%8C%85) -    [3.8.4 空函数](#384-%E7%A9%BA%E5%87%BD%E6%95%B0) +    [3.8.4 空函数](#user-content-384-%E7%A9%BA%E5%87%BD%E6%95%B0) -  [3.9 面向对象](#39-%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1) +  [3.9 面向对象](#user-content-39-%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1) -  [3.10 动态特性](#310-%E5%8A%A8%E6%80%81%E7%89%B9%E6%80%A7) +  [3.10 动态特性](#user-content-310-%E5%8A%A8%E6%80%81%E7%89%B9%E6%80%A7) -    [3.10.1 eval](#3101-eval) +    [3.10.1 eval](#user-content-3101-eval) -    [3.10.2 动态执行代码](#3102-%E5%8A%A8%E6%80%81%E6%89%A7%E8%A1%8C%E4%BB%A3%E7%A0%81) +    [3.10.2 动态执行代码](#user-content-3102-%E5%8A%A8%E6%80%81%E6%89%A7%E8%A1%8C%E4%BB%A3%E7%A0%81) -    [3.10.3 with](#3103-with) +    [3.10.3 with](#user-content-3103-with) -    [3.10.4 delete](#3104-delete) +    [3.10.4 delete](#user-content-3104-delete) -    [3.10.5 对象属性](#3105-%E5%AF%B9%E8%B1%A1%E5%B1%9E%E6%80%A7) +    [3.10.5 对象属性](#user-content-3105-%E5%AF%B9%E8%B1%A1%E5%B1%9E%E6%80%A7) -[4 浏览器环境](#4-%E6%B5%8F%E8%A7%88%E5%99%A8%E7%8E%AF%E5%A2%83) +[4 浏览器环境](#user-content-4-%E6%B5%8F%E8%A7%88%E5%99%A8%E7%8E%AF%E5%A2%83) -  [4.1 模块化](#41-%E6%A8%A1%E5%9D%97%E5%8C%96) +  [4.1 模块化](#user-content-41-%E6%A8%A1%E5%9D%97%E5%8C%96) -    [4.1.1 AMD](#411-amd) +    [4.1.1 AMD](#user-content-411-amd) -    [4.1.2 define](#412-define) +    [4.1.2 define](#user-content-412-define) -    [4.1.3 require](#413-require) +    [4.1.3 require](#user-content-413-require) -  [4.2 DOM](#42-dom) +  [4.2 DOM](#user-content-42-dom) -    [4.2.1 元素获取](#421-%E5%85%83%E7%B4%A0%E8%8E%B7%E5%8F%96) +    [4.2.1 元素获取](#user-content-421-%E5%85%83%E7%B4%A0%E8%8E%B7%E5%8F%96) -    [4.2.2 样式获取](#422-%E6%A0%B7%E5%BC%8F%E8%8E%B7%E5%8F%96) +    [4.2.2 样式获取](#user-content-422-%E6%A0%B7%E5%BC%8F%E8%8E%B7%E5%8F%96) -    [4.2.3 样式设置](#423-%E6%A0%B7%E5%BC%8F%E8%AE%BE%E7%BD%AE) +    [4.2.3 样式设置](#user-content-423-%E6%A0%B7%E5%BC%8F%E8%AE%BE%E7%BD%AE) -    [4.2.4 DOM 操作](#424-dom-%E6%93%8D%E4%BD%9C) +    [4.2.4 DOM 操作](#user-content-424-dom-%E6%93%8D%E4%BD%9C) -    [4.2.5 DOM 事件](#425-dom-%E4%BA%8B%E4%BB%B6) +    [4.2.5 DOM 事件](#user-content-425-dom-%E4%BA%8B%E4%BB%B6) @@ -123,9 +123,11 @@ ## 1 前言 -JavaScript在百度一直有着广泛的应用,特别是在浏览器端的行为管理。本文档的目标是使JavaScript代码风格保持一致,容易被理解和被维护。 +JavaScript 在百度一直有着广泛的应用,特别是在浏览器端的行为管理。本文档的目标是使 JavaScript 代码风格保持一致,容易被理解和被维护。 + +虽然本文档是针对 JavaScript 设计的,但是在使用各种 JavaScript 的预编译语言时(如 TypeScript 等)时,适用的部分也应尽量遵循本文档的约定。 + -虽然本文档是针对JavaScript设计的,但是在使用各种JavaScript的预编译语言时(如TypeScript等)时,适用的部分也应尽量遵循本文档的约定。 @@ -139,7 +141,7 @@ JavaScript在百度一直有着广泛的应用,特别是在浏览器端的行 ### 2.1 文件 -##### [建议] `JavaScript` 文件使用无 `BOM` 的 `UTF-8` 编码。 +##### [建议] JavaScript 文件使用无 `BOM` 的 `UTF-8` 编码。 解释: @@ -309,7 +311,7 @@ var funcName = function funcName () { funcName (); ``` -##### [强制] `,` 和 `;` 前不允许有空格。 +##### [强制] `,` 和 `;` 前不允许有空格。如果不位于行尾,`,` 和 `;` 后必须跟一个空格。 示例: @@ -430,7 +432,7 @@ var result = number1 + number2 + number3 + number4 + number5; ``` -##### [强制] 在函数声明、函数表达式、函数调用、对象创建、数组创建、for语句等场景中,不允许在 `,` 或 `;` 前换行。 +##### [强制] 在函数声明、函数表达式、函数调用、对象创建、数组创建、`for` 语句等场景中,不允许在 `,` 或 `;` 前换行。 示例: @@ -484,7 +486,7 @@ function setStyle(element, property, value) { ```javascript // 较复杂的逻辑条件组合,将每个条件独立一行,逻辑运算符放置在行首进行分隔,或将部分逻辑按逻辑组合进行分隔。 -// 建议最终将右括号 ) 与左大括号 { 放在独立一行,保证与 if 内语句块能容易视觉辨识。 +// 建议最终将右括号 ) 与左大括号 { 放在独立一行,保证与 `if` 内语句块能容易视觉辨识。 if (user.isAuthenticated() && user.isInRole('admin') && user.hasAuthority('add-admin') @@ -495,15 +497,15 @@ if (user.isAuthenticated() // 按一定长度截断字符串,并使用 + 运算符进行连接。 // 分隔字符串尽量按语义进行,如不要在一个完整的名词中间断开。 -// 特别的,对于HTML片段的拼接,通过缩进,保持和HTML相同的结构。 -var html = '' // 此处用一个空字符串,以便整个HTML片段都在新行严格对齐 +// 特别的,对于 HTML 片段的拼接,通过缩进,保持和 HTML 相同的结构。 +var html = '' // 此处用一个空字符串,以便整个 HTML 片段都在新行严格对齐 + '
' + '

Title here

' + '

This is a paragraph

' + '
Complete
' + '
'; -// 也可使用数组来进行拼接,相对 + 更容易调整缩进。 +// 也可使用数组来进行拼接,相对 `+` 更容易调整缩进。 var html = [ '
', '

Title here

', @@ -522,14 +524,14 @@ foo( ); // 也可以按逻辑对参数进行组合。 -// 最经典的是baidu.format函数,调用时将参数分为“模板”和“数据”两块 +// 最经典的是 baidu.format 函数,调用时将参数分为“模板”和“数据”两块 baidu.format( dateFormatTemplate, year, month, date, hour, minute, second ); // 当函数调用时,如果有一个或以上参数跨越多行,应当每一个参数独立一行。 -// 这通常出现在匿名函数或者对象初始化等作为参数时,如setTimeout函数等。 +// 这通常出现在匿名函数或者对象初始化等作为参数时,如 `setTimeout` 函数等。 setTimeout( function () { alert('hello'); @@ -538,11 +540,11 @@ setTimeout( ); order.data.read( - 'id=' + me.model.id, + 'id=' + me.model.id, function (data) { me.attchToModel(data.result); callback(); - }, + }, 300 ); @@ -560,7 +562,7 @@ var result = condition ? thisIsAVeryVeryLongResult : resultB; -// 数组和对象初始化的混用,严格按照每个对象的 { 和结束 } 在独立一行的风格书写。 +// 数组和对象初始化的混用,严格按照每个对象的 `{` 和结束 `}` 在独立一行的风格书写。 var array = [ { // ... @@ -715,7 +717,7 @@ function TextNode(options) { } ``` -##### [强制] 类的 `方法 / 属性` 使用 `Camel命名法`。 +##### [强制] 类的 `方法` / `属性` 使用 `Camel命名法`。 示例: @@ -846,7 +848,7 @@ loadingData.then(callback); #### 2.4.4 类型定义 -##### [强制] 类型定义都是以`{`开始, 以`}`结束。 +##### [强制] 类型定义都是以 `{` 开始, 以 `}` 结束。 解释: @@ -873,6 +875,7 @@ loadingData.then(callback); |不允许为null|{!Object}|Object 类型, 但不是 null| |Function类型|{function(number, boolean)}|函数, 形参类型| |Function带返回值|{function(number, boolean):string}|函数, 形参, 返回值类型| +|Promise|Promise.<resolveType, rejectType>|Promise,成功返回的数据类型,失败返回的错误类型| |参数可选|@param {string=} name|可选参数, =为类型后缀| |可变参数|@param {...number} args|变长参数, ...为类型前缀| |任意类型|{*}|任意类型| @@ -997,14 +1000,14 @@ function Fronteer() { util.extend( Fronteer.prototype, /** @lends Fronteer.prototype */{ - _getLevel: function () { + getLevel: function () { // TODO } } ); ``` -##### [强制] 类的属性或方法等成员信息使用 `@public` / `@protected` / `@private` 中的任意一个,指明可访问性。 +##### [强制] 类的属性或方法等成员信息不是 `public` 的,应使用 `@protected` 或 `@private` 标识可访问性。 解释: @@ -1028,7 +1031,7 @@ var Fronteer = function () { * @type {string} * @private */ - this._level = 'T12'; + this.level = 'T12'; // constructor body }; @@ -1040,7 +1043,7 @@ util.inherits(Fronteer, Developer); * @private * @return {string} 返回值描述 */ -Fronteer.prototype._getLevel = function () { +Fronteer.prototype.getLevel = function () { }; ``` @@ -1050,7 +1053,12 @@ Fronteer.prototype._getLevel = function () { ##### [强制] 函数/方法注释必须包含函数说明,有参数和返回值时必须使用注释标识。 -##### [强制] 参数和返回值注释必须包含类型信息和说明。 +解释: + +当 `return` 关键字仅作退出函数/方法使用时,无须对返回值作注释标识。 + + +##### [强制] 参数和返回值注释必须包含类型信息,且不允许省略参数的说明。 ##### [建议] 当函数是内部函数,外部不可访问时,可以使用 `@inner` 标识。 @@ -1110,13 +1118,18 @@ function foo(option) { /** * 值变更时触发 * - * @event + * @event Select#change * @param {Object} e e描述 * @param {string} e.before before描述 * @param {string} e.after after描述 */ -onchange: function (e) { -} +this.fire( + 'change', + { + before: 'foo', + after: 'bar' + } +); ``` ##### [强制] 在会广播事件的函数前使用 `@fires` 标识广播的事件,在广播事件代码前使用 `@event` 标识事件。 @@ -1133,6 +1146,7 @@ onchange: function (e) { * @private */ Select.prototype.clickHandler = function () { + /** * 值变更时触发 * @@ -1462,7 +1476,7 @@ function foo(p1, p2, opt_p3) { ### 3.1 变量 -##### [强制] 变量在使用前必须通过 `var` 定义。 +##### [强制] 变量、函数在使用前必须先定义。 解释: @@ -1479,11 +1493,20 @@ var name = 'MyName'; name = 'MyName'; ``` +原则上不建议使用全局变量,对于已有的全局变量或第三方框架引入的全局变量,需要根据检查工具的语法标识。 + +示例: + +```javascript +/* globals jQuery */ +var element = jQuery('#element-id'); +``` + ##### [强制] 每个 `var` 只能声明一个变量。 解释: -一个 var 声明多个变量,容易导致较长的行长度,并且在修改时容易造成逗号和分号的混淆。 +一个 `var` 声明多个变量,容易导致较长的行长度,并且在修改时容易造成逗号和分号的混淆。 示例: @@ -1503,14 +1526,14 @@ var hangModules = [], ##### [强制] 变量必须 `即用即声明`,不得在函数或其它形式的代码块起始位置统一声明所有变量。 -解释: +解释: 变量声明与使用的距离越远,出现的跨度越大,代码的阅读与维护成本越高。虽然JavaScript的变量是函数作用域,还是应该根据编程中的意图,缩小变量出现的距离空间。 示例: -```javascript +```javascript // good function kv2List(source) { var list = []; @@ -1521,6 +1544,7 @@ function kv2List(source) { k: key, v: source[key] }; + list.push(item); } } @@ -1540,6 +1564,7 @@ function kv2List(source) { k: key, v: source[key] }; + list.push(item); } } @@ -1556,11 +1581,11 @@ function kv2List(source) { ### 3.2 条件 -##### [强制] 在 Equality Expression 中使用类型严格的 `===`。仅当判断 null 或 undefined 时,允许使用 `== null`。 +##### [强制] 在 Equality Expression 中使用类型严格的 `===`。仅当判断 `null` 或 `undefined` 时,允许使用 `== null`。 解释: -使用 === 可以避免等于判断中隐式的类型转换。 +使用 `===` 可以避免等于判断中隐式的类型转换。 示例: @@ -1684,7 +1709,7 @@ switch (typeof variable) { var type = typeof variable; if (type === 'object') { // ...... -} +} else if (type === 'number' || type === 'boolean' || type === 'string') { // ...... } @@ -1777,7 +1802,7 @@ for (var i = 0, len = elements.length; i < len; i++) { 解释: -虽然现代浏览器都对数组长度进行了缓存,但对于一些宿主对象和老旧浏览器的数组对象,在每次 length 访问时会动态计算元素个数,此时缓存 length 能有效提高程序性能。 +虽然现代浏览器都对数组长度进行了缓存,但对于一些宿主对象和老旧浏览器的数组对象,在每次 `length` 访问时会动态计算元素个数,此时缓存 `length` 能有效提高程序性能。 示例: @@ -1911,7 +1936,7 @@ var num = 3.14; !!num; ``` -##### [建议] `number` 去除小数点,使用 `Math.floor / Math.round / Math.ceil`,不使用 `parseInt`。 +##### [建议] `number` 去除小数点,使用 `Math.floor` / `Math.round` / `Math.ceil`,不使用 `parseInt`。 示例: @@ -1935,7 +1960,7 @@ parseInt(num, 10); 解释: -1. 输入单引号不需要按住 shift,方便输入。 +1. 输入单引号不需要按住 `shift`,方便输入。 2. 实际使用中,字符串经常用来拼接 HTML。为方便 HTML 中包含双引号而不需要转义写法。 示例: @@ -1949,8 +1974,8 @@ var html = '
拼接HTML可以省去双引号转义
'; 解释: -1. 使用 + 拼接字符串,如果拼接的全部是 StringLiteral,压缩工具可以对其进行自动合并的优化。所以,静态字符串建议使用 + 拼接。 -2. 在现代浏览器下,使用 + 拼接字符串,性能较数组的方式要高。 +1. 使用 `+` 拼接字符串,如果拼接的全部是 StringLiteral,压缩工具可以对其进行自动合并的优化。所以,静态字符串建议使用 `+` 拼接。 +2. 在现代浏览器下,使用 `+` 拼接字符串,性能较数组的方式要高。 3. 如需要兼顾老旧浏览器,应尽量使用数组拼接字符串。 示例: @@ -1965,7 +1990,7 @@ var str = [ '' ].join(''); -// 使用 + 拼接字符串 +// 使用 `+` 拼接字符串 var str2 = '' // 建议第一个为空字符串, 第二个换行开始并缩进开始, 对齐代码, 方便阅读 + '
    ', + '
  • 第一项
  • ', @@ -1973,6 +1998,30 @@ var str2 = '' // 建议第一个为空字符串, 第二个换行开始并缩进 + '
'; ``` +##### [建议] 使用字符串拼接的方式生成HTML,需要根据语境进行合理的转义。 + +解释: + +在 `JavaScript` 中拼接,并且最终将输出到页面中的字符串,需要进行合理转义,以防止安全漏洞。下面的示例代码为场景说明,不能直接运行。 + + +示例: + +```javascript +// HTML 转义 +var str = '

' + htmlEncode(content) + '

'; + +// HTML 转义 +var str = ''; + +// URL 转义 +var str = 'link'; + +// JavaScript字符串 转义 + HTML 转义 +var str = ''; +``` + + ##### [建议] 复杂的数据到视图字符串的转换过程,选用一种模板引擎。 解释: @@ -1981,6 +2030,7 @@ var str2 = '' // 建议第一个为空字符串, 第二个换行开始并缩进 1. 在开发过程中专注于数据,将视图生成的过程由另外一个层级维护,使程序逻辑结构更清晰。 2. 优秀的模板引擎,通过模板编译技术和高质量的编译产物,能获得比手工拼接字符串更高的性能。 +3. 模板引擎能方便的对动态数据进行相应的转义,部分模板引擎默认进行HTML转义,安全性更好。 - artTemplate: 体积较小,在所有环境下性能高,语法灵活。 - dot.js: 体积小,在现代浏览器下性能高,语法灵活。 @@ -1997,7 +2047,7 @@ var str2 = '' // 建议第一个为空字符串, 第二个换行开始并缩进 ##### [强制] 使用对象字面量 `{}` 创建新 `Object`。 -示例: +示例: ```javascript // good @@ -2007,9 +2057,9 @@ var obj = {}; var obj = new Object(); ``` -##### [强制] 对象创建时,如果一个对象的所有 `属性` 均可以不添加引号,则所有 `属性` 不得添加引号。 +##### [建议] 对象创建时,如果一个对象的所有 `属性` 均可以不添加引号,建议所有 `属性` 不添加引号。 -示例: +示例: ```javascript var info = { @@ -2018,14 +2068,14 @@ var info = { }; ``` -##### [强制] 对象创建时,如果任何一个 `属性` 需要添加引号,则所有 `属性` 必须添加 `'`。 +##### [建议] 对象创建时,如果任何一个 `属性` 需要添加引号,则所有 `属性` 建议添加 `'`。 解释: 如果属性不符合 Identifier 和 NumberLiteral 的形式,就需要以 StringLiteral 的形式提供。 -示例: +示例: ```javascript // good @@ -2045,7 +2095,7 @@ var info = { ##### [强制] 不允许修改和扩展任何原生对象和宿主对象的原型。 -示例: +示例: ```javascript // 以下行为绝对禁止 @@ -2059,10 +2109,10 @@ String.prototype.trim = function () { 属性名符合 Identifier 的要求,就可以通过 `.` 来访问,否则就只能通过 `[expr]` 方式访问。 -通常在 JavaScript 中声明的对象,属性命名是使用 Camel 命名法,用 `.` 来访问更清晰简洁。部分特殊的属性(比如来自后端的JSON),可能采用不寻常的命名方式,可以通过 `[expr]` 方式访问。 +通常在 JavaScript 中声明的对象,属性命名是使用 Camel 命名法,用 `.` 来访问更清晰简洁。部分特殊的属性(比如来自后端的 JSON ),可能采用不寻常的命名方式,可以通过 `[expr]` 方式访问。 -示例: +示例: ```javascript info.age; @@ -2104,13 +2154,15 @@ var arr = new Array(); 解释: -数组对象可能存在数字以外的属性, 这种情况下 for in 不会得到正确结果. +数组对象可能存在数字以外的属性, 这种情况下 `for in` 不会得到正确结果。 示例: ```javascript var arr = ['a', 'b', 'c']; -arr.other = 'other things'; // 这里仅作演示, 实际中应使用Object类型 + +// 这里仅作演示, 实际中应使用 Object 类型 +arr.other = 'other things'; // 正确的遍历方式 for (var i = 0, len = arr.length; i < len; i++) { @@ -2118,7 +2170,7 @@ for (var i = 0, len = arr.length; i < len; i++) { } // 错误的遍历方式 -for (i in arr) { +for (var i in arr) { console.log(i); } ``` @@ -2127,7 +2179,7 @@ for (i in arr) { 解释: -自己实现的常规排序算法,在性能上并不优于数组默认的 sort 方法。以下两种场景可以自己实现排序: +自己实现的常规排序算法,在性能上并不优于数组默认的 `sort` 方法。以下两种场景可以自己实现排序: 1. 需要稳定的排序算法,达到严格一致的排序结果。 2. 数据特点鲜明,适合使用桶排。 @@ -2165,14 +2217,14 @@ function syncViewStateOnUserAction() { y.checked = false; } - if (!a.value) { - warning.innerText = 'Please enter it'; - submitButton.disabled = true; - } - else { + if (a.value) { warning.innerText = ''; submitButton.disabled = false; } + else { + warning.innerText = 'Please enter it'; + submitButton.disabled = true; + } } // 直接阅读该函数会难以明确其主线逻辑,因此下方是一种更合理的表达方式: @@ -2183,21 +2235,19 @@ function syncViewStateOnUserAction() { } function syncXStateToView() { + y.checked = x.checked; + if (x.checked) { - y.checked = true; z.value = ''; } - else { - y.checked = false; - } } function checkAAvailability() { - if (!a.value) { - displayWarningForAMissing(); + if (a.value) { + clearWarnignForA(); } else { - clearWarnignForA(); + displayWarningForAMissing(); } } ``` @@ -2210,16 +2260,16 @@ function checkAAvailability() { 解释: -除去不定长参数以外,函数具备不同逻辑意义的参数建议控制在 6 个以内,过多参数会导致维护难度增大。 +除去不定长参数以外,函数具备不同逻辑意义的参数建议控制在 `6` 个以内,过多参数会导致维护难度增大。 -某些情况下,如使用 AMD Loader 的 require 加载多个模块时,其 callback 可能会存在较多参数,因此对函数参数的个数不做强制限制。 +某些情况下,如使用 AMD Loader 的 `require` 加载多个模块时,其 `callback` 可能会存在较多参数,因此对函数参数的个数不做强制限制。 ##### [建议] 通过 `options` 参数传递非数据输入型参数。 解释: -有些函数的参数并不是作为算法的输入,而是对算法的某些分支条件判断之用,此类参数建议通过一个 options 参数传递。 +有些函数的参数并不是作为算法的输入,而是对算法的某些分支条件判断之用,此类参数建议通过一个 `options` 参数传递。 如下函数: @@ -2232,6 +2282,7 @@ function checkAAvailability() { */ function removeElement(element, removeEventListeners) { element.parent.removeChild(element); + if (removeEventListeners) { element.clearEventListeners(); } @@ -2250,6 +2301,7 @@ function removeElement(element, removeEventListeners) { */ function removeElement(element, options) { element.parent.removeChild(element); + if (options.removeEventListeners) { element.clearEventListeners(); } @@ -2258,8 +2310,8 @@ function removeElement(element, options) { 这种模式有几个显著的优势: -- boolean 型的配置项具备名称,从调用的代码上更易理解其表达的逻辑意义。 -- 当配置项有增长时,无需无休止地增加参数个数,不会出现 removeElement(element, true, false, false, 3) 这样难以理解的调用代码。 +- `boolean` 型的配置项具备名称,从调用的代码上更易理解其表达的逻辑意义。 +- 当配置项有增长时,无需无休止地增加参数个数,不会出现 `removeElement(element, true, false, false, 3)` 这样难以理解的调用代码。 - 当部分配置参数可选时,多个参数的形式非常难处理重载逻辑,而使用一个 options 对象只需判断属性是否存在,实现得以简化。 @@ -2273,7 +2325,7 @@ function removeElement(element, options) { 在 JavaScript 中,无需特别的关键词就可以使用闭包,一个函数可以任意访问在其定义的作用域外的变量。需要注意的是,函数的作用域是静态的,即在定义时决定,与调用的时机和方式没有任何关系。 -闭包会阻止一些变量的垃圾回收,对于较老旧的JavaScript引擎,可能导致外部所有变量均无法回收。 +闭包会阻止一些变量的垃圾回收,对于较老旧的 JavaScript 引擎,可能导致外部所有变量均无法回收。 首先一个较为明确的结论是,以下内容会影响到闭包内变量的回收: @@ -2281,23 +2333,23 @@ function removeElement(element, options) { - 嵌套的函数中是否有 **直接调用eval**。 - 是否使用了 with 表达式。 -Chakra、V8 和 SpiderMonkey 将受以上因素的影响,表现出不尽相同又较为相似的回收策略,而JScript.dll和Carakan则完全没有这方面的优化,会完整保留整个 LexicalEnvironment 中的所有变量绑定,造成一定的内存消耗。 +Chakra、V8 和 SpiderMonkey 将受以上因素的影响,表现出不尽相同又较为相似的回收策略,而 JScript.dll 和 Carakan 则完全没有这方面的优化,会完整保留整个 LexicalEnvironment 中的所有变量绑定,造成一定的内存消耗。 -由于对闭包内变量有回收优化策略的 Chakra、V8 和 SpiderMonkey 引擎的行为较为相似,因此可以总结如下,当返回一个函数 fn 时: +由于对闭包内变量有回收优化策略的 Chakra、V8 和 SpiderMonkey 引擎的行为较为相似,因此可以总结如下,当返回一个函数 **fn** 时: -1. 如果 fn 的 [[Scope]] 是ObjectEnvironment(with 表达式生成 ObjectEnvironment,函数和 catch 表达式生成 DeclarativeEnvironment),则: +1. 如果 **fn** 的 `[[Scope]]` 是 ObjectEnvironment(with 表达式生成 ObjectEnvironment,函数和 catch 表达式生成 DeclarativeEnvironment),则: 1. 如果是 V8 引擎,则退出全过程。 2. 如果是 SpiderMonkey,则处理该 ObjectEnvironment 的外层 LexicalEnvironment。 2. 获取当前 LexicalEnvironment 下的所有类型为 Function 的对象,对于每一个 Function 对象,分析其 FunctionBody: - 1. 如果 FunctionBody 中含有 **直接调用eval**,则退出全过程。 + 1. 如果 FunctionBody 中含有 **直接调用 eval**,则退出全过程。 2. 否则得到所有的 Identifier。 - 3. 对于每一个 Identifier,设其为 name,根据查找变量引用的规则,从 LexicalEnvironment 中找出名称为 name 的绑定 binding。 - 4. 对 binding 添加 notSwap 属性,其值为 true。 -3. 检查当前 LexicalEnvironment 中的每一个变量绑定,如果该绑定有 notSwap 属性且值为 true,则: - 1. 如果是V8引擎,删除该绑定。 - 2. 如果是SpiderMonkey,将该绑定的值设为 undefined,将删除 notSwap 属性。 + 3. 对于每一个 Identifier,设其为 **name**,根据查找变量引用的规则,从 LexicalEnvironment 中找出名称为 **name** 的绑定 binding。 + 4. 对 binding 添加 **notSwap** 属性,其值为 `true`。 +3. 检查当前 LexicalEnvironment 中的每一个变量绑定,如果该绑定有 **notSwap** 属性且值为 `true`,则: + 1. 如果是 V8 引擎,删除该绑定。 + 2. 如果是 SpiderMonkey,将该绑定的值设为 `undefined`,将删除 **notSwap** 属性。 -对于Chakra引擎,暂无法得知是按 V8 的模式还是按 SpiderMonkey 的模式进行。 +对于 Chakra 引擎,暂无法得知是按 V8 的模式还是按 SpiderMonkey 的模式进行。 如果有 **非常庞大** 的对象,且预计会在 **老旧的引擎** 中执行,则使用闭包时,注意将闭包不需要的对象置为空引用。 @@ -2381,7 +2433,7 @@ MyClass.prototype.hooks.after = EMPTY_FUNCTION; 解释: -通常使用其他 library 的类继承方案都会进行 constructor 修正。如果是自己实现的类继承方案,需要进行 constructor 修正。 +通常使用其他 library 的类继承方案都会进行 `constructor` 修正。如果是自己实现的类继承方案,需要进行 `constructor` 修正。 示例: @@ -2389,7 +2441,7 @@ MyClass.prototype.hooks.after = EMPTY_FUNCTION; ```javascript /** * 构建类之间的继承关系 - * + * * @param {Function} subClass 子类函数 * @param {Function} superClass 父类函数 */ @@ -2428,7 +2480,7 @@ Animal.prototype.jump = function () { ##### [建议] 属性在构造函数中声明,方法在原型中声明。 -解释: +解释: 原型对象的成员被所有实例共享,能节约内存占用。所以编码时我们应该遵守这样的原则:原型对象包含程序不会修改的成员,如方法函数或配置项。 @@ -2466,8 +2518,8 @@ TextNode.prototype.clone = function () { 常见禁止默认行为的方式有两种: -1. 事件监听函数中 return false。 -2. 事件对象中包含禁止默认行为的方法,如 preventDefault。 +1. 事件监听函数中 `return false`。 +2. 事件对象中包含禁止默认行为的方法,如 `preventDefault`。 @@ -2482,9 +2534,9 @@ TextNode.prototype.clone = function () { 解释: -直接 eval,指的是以函数方式调用 eval 的调用方法。直接 eval 调用执行代码的作用域为本地作用域,应当避免。 +直接 `eval`,指的是以函数方式调用 `eval` 的调用方法。直接 `eval` 调用执行代码的作用域为本地作用域,应当避免。 -如果有特殊情况需要使用直接 eval,需在代码中用详细的注释说明为何必须使用直接 eval,不能使用其它动态执行代码的方式,同时需要其他资深工程师进行 Code Review。 +如果有特殊情况需要使用直接 `eval`,需在代码中用详细的注释说明为何必须使用直接 `eval`,不能使用其它动态执行代码的方式,同时需要其他资深工程师进行 Code Review。 ##### [建议] 尽量避免使用 `eval` 函数。 @@ -2496,7 +2548,7 @@ TextNode.prototype.clone = function () { 解释: -通过 new Function 生成的函数作用域是全局使用域,不会影响当当前的本地作用域。如果有动态代码执行的需求,建议使用 new Function。 +通过 `new Function` 生成的函数作用域是全局使用域,不会影响当当前的本地作用域。如果有动态代码执行的需求,建议使用 `new Function`。 示例: @@ -2515,7 +2567,7 @@ var result = handler($('#x').val(), $('#y').val()); 解释: -使用 with 可能会增加代码的复杂度,不利于阅读和管理;也会对性能有影响。大多数使用 with 的场景都能使用其他方式较好的替代。所以,尽量不要使用 with。 +使用 `with` 可能会增加代码的复杂度,不利于阅读和管理;也会对性能有影响。大多数使用 `with` 的场景都能使用其他方式较好的替代。所以,尽量不要使用 `with`。 @@ -2527,16 +2579,16 @@ var result = handler($('#x').val(), $('#y').val()); 解释: -如果没有特别的需求,减少或避免使用`delete`。`delete`的使用会破坏部分 JavaScript 引擎的性能优化。 +如果没有特别的需求,减少或避免使用 `delete`。`delete` 的使用会破坏部分 JavaScript 引擎的性能优化。 ##### [建议] 处理 `delete` 可能产生的异常。 解释: -对于有被遍历需求,且值 null 被认为具有业务逻辑意义的值的对象,移除某个属性必须使用 delete 操作。 +对于有被遍历需求,且值 `null` 被认为具有业务逻辑意义的值的对象,移除某个属性必须使用 `delete` 操作。 -在严格模式或IE下使用 delete 时,不能被删除的属性会抛出异常,因此在不确定属性是否可以删除的情况下,建议添加 try-catch 块。 +在严格模式或 IE 下使用 `delete` 时,不能被删除的属性会抛出异常,因此在不确定属性是否可以删除的情况下,建议添加 `try-catch` 块。 示例: @@ -2563,7 +2615,7 @@ JavaScript 因其脚本语言的动态特性,当一个对象未被 seal 或 fr 但是随意地对 非自身控制的对象 进行修改,很容易造成代码在不可预知的情况下出现问题。因此,设计良好的组件、函数应该避免对外部传入的对象的修改。 -下面代码的 selectNode 方法修改了由外部传入的 datasource 对象。如果 datasource 用在其它场合(如另一个 Tree 实例)下,会造成状态的混乱。 +下面代码的 **selectNode** 方法修改了由外部传入的 **datasource** 对象。如果 **datasource** 用在其它场合(如另一个 Tree 实例)下,会造成状态的混乱。 ```javascript function Tree(datasource) { @@ -2580,7 +2632,7 @@ Tree.prototype.selectNode = function (id) { }; ``` -对于此类场景,需要使用额外的对象来维护,使用由自身控制,不与外部产生任何交互的 selectedNodeIndex 对象来维护节点的选中状态,不对 datasource 作任何修改。 +对于此类场景,需要使用额外的对象来维护,使用由自身控制,不与外部产生任何交互的 **selectedNodeIndex** 对象来维护节点的选中状态,不对 **datasource** 作任何修改。 ```javascript function Tree(datasource) { @@ -2589,12 +2641,15 @@ function Tree(datasource) { } Tree.prototype.selectNode = function (id) { + // 从datasource中找出节点对象 var node = this.findNode(id); + if (node) { this.selectedNodeIndex[id] = true; this.flushView(); } + }; ``` @@ -2605,8 +2660,8 @@ Tree.prototype.selectNode = function (id) { 解释: -- 如果一个属性被设计为 boolean 类型,则不要使用 1 / 0 作为其值。对于标识性的属性,如对代码体积有严格要求,可以从一开始就设计为 number 类型且将 0 作为否定值。 -- 从 DOM 中取出的值通常为 string 类型,如果有对象或函数的接收类型为 number 类型,提前作好转换,而不是期望对象、函数可以处理多类型的值。 +- 如果一个属性被设计为 `boolean` 类型,则不要使用 `1` 或 `0` 作为其值。对于标识性的属性,如对代码体积有严格要求,可以从一开始就设计为 `number` 类型且将 `0` 作为否定值。 +- 从 DOM 中取出的值通常为 `string` 类型,如果有对象或函数的接收类型为 `number` 类型,提前作好转换,而不是期望对象、函数可以处理多类型的值。 @@ -2659,9 +2714,9 @@ AMD 作为由社区认可的模块定义形式,提供多种重载提供灵活 解释: -在 AMD 的设计思想里,模块名称是和所在路径相关的,匿名的模块更利于封包和迁移。模块依赖应在模块定义内部通过 local require 引用。 +在 AMD 的设计思想里,模块名称是和所在路径相关的,匿名的模块更利于封包和迁移。模块依赖应在模块定义内部通过 `local require` 引用。 -所以,推荐使用 define(factory) 的形式进行模块定义。 +所以,推荐使用 `define(factory)` 的形式进行模块定义。 示例: @@ -2721,15 +2776,15 @@ var foo = require('foo'); 解释: -1. 在模块定义中使用 global require,对封装性是一种破坏。 -2. 在 AMD 里,global require 是可以被重命名的。并且 Loader 甚至没有全局的 require 变量,而是用 Loader 名称做为 global require。模块定义不应该依赖使用的 Loader。 +1. 在模块定义中使用 `global require`,对封装性是一种破坏。 +2. 在 AMD 里,`global require` 是可以被重命名的。并且 Loader 甚至没有全局的 `require` 变量,而是用 Loader 名称做为 `global require`。模块定义不应该依赖使用的 Loader。 -##### [强制] Package在实现时,内部模块的 `require` 必须使用 `relative id`。 +##### [强制] Package 在实现时,内部模块的 `require` 必须使用 `relative id`。 解释: -对于任何可能通过 发布-引入 的形式复用的第三方库、框架、包,开发者所定义的名称不代表使用者使用的名称。因此不要基于任何名称的假设。在实现源码中,require 自身的其它模块时使用 relative id。 +对于任何可能通过 发布-引入 的形式复用的第三方库、框架、包,开发者所定义的名称不代表使用者使用的名称。因此不要基于任何名称的假设。在实现源码中,`require` 自身的其它模块时使用 `relative id`。 示例: @@ -2746,7 +2801,7 @@ define( 解释: -有些模块是依赖的模块,但不会在模块实现中被直接调用,最为典型的是 css / js / tpl 等 Plugin 所引入的外部内容。此类内容建议放在模块定义最开始处统一引用。 +有些模块是依赖的模块,但不会在模块实现中被直接调用,最为典型的是 `css` / `js` / `tpl` 等 Plugin 所引入的外部内容。此类内容建议放在模块定义最开始处统一引用。 示例: diff --git a/less-code-style.md b/less-code-style.md index 14186b8..861481a 100644 --- a/less-code-style.md +++ b/less-code-style.md @@ -1,10 +1,10 @@ -# Less 编码规范 (1.0) +# Less 编码规范 (1.1) ## 简介 该文档主要的设计目标是提高 Less 文档的团队一致性与可维护性。 -Less 代码的基本规范和原则与 [HTML 与 CSS 编码规范](https://github.com/ecomfe/spec/blob/master/html-and-css-code-style.md)中的 CSS 部分保持一致。 +Less 代码的基本规范和原则与 [CSS 编码规范](https://github.com/ecomfe/spec/blob/master/css-style-guide.md) 保持一致。 ### 编撰 @@ -20,7 +20,7 @@ erik、顾轶灵、黄后锦、李玉北、赵雷。 ## 编码 -使用UTF-8编码。HTML、CSS、模板等文件*不得*(MUST NOT)包含BOM信息。 +使用UTF-8编码。*不得*(MUST NOT)包含BOM信息。 *** @@ -183,7 +183,7 @@ margin-top: 0; ### 颜色 -颜色定义*必须*(MUST)使用 `#RRGGBB` 格式定义,并在可能时*尽量*(SHOULD)缩写为 `#RGB` 形式,且避免直接使用颜色名称与 `rgb()` 表达式。 +颜色定义*必须*(MUST)使用 `#rrggbb` 格式定义,并在可能时*尽量*(SHOULD)缩写为 `#rgb` 形式,且避免直接使用颜色名称与 `rgb()` 表达式。 ```less // ✗ @@ -191,8 +191,8 @@ border-color: red; color: rgb(254, 254, 254); // ✓ -border-color: #F00; -color: #FEFEFE; +border-color: #f00; +color: #fefefe; ``` ### 私有属性前缀 @@ -365,7 +365,7 @@ Mixin 的参数分隔符使用 `,` 和 `;` 均可,但在同一项目中*必须 在进行字符串转义时,使用 `~""` 表达式与 `e()` 函数均可,但在同一项目中*必须*(MUST)保持一致。 -字符串两侧的引号*可以*(MAY)使用 `'` 或 `"`,但在同一项目中*必须*(MUST)保持一致。 +字符串两侧的引号*必须*(MUST)使用 `"`。 ## JS 表达式 diff --git a/package.md b/package.md index 22e2fd0..d64bef8 100644 --- a/package.md +++ b/package.md @@ -96,7 +96,7 @@ require.config({ ### 示例 - ```javascript + ```JavaScript { "name": "zrender", "version": "0.0.1", @@ -112,7 +112,8 @@ require.config({ "uglify-js": "*" } } - ``` +``` + ## 包目录结构 diff --git a/react-style-guide.md b/react-style-guide.md new file mode 100644 index 0000000..13e34a5 --- /dev/null +++ b/react-style-guide.md @@ -0,0 +1,459 @@ +# React规范 + +## 文件组织 + +- [强制]同一目录下不得拥有同名的`.js`和`.jsx`文件。 + + 在使用模块导入时,倾向于不添加后缀,如果存在同名但不同后缀的文件,构建工具将无法决定哪一个是需要引入的模块。 + +- [强制]组件文件使用一致的`.js`或 `.jsx`后缀。 + + 所有组件文件的后缀名从`.js`或`.jsx`中任选其一。 + + 不应在项目中出现部分组件为`.js`文件,部分为`.jsx`的情况。 + +- [强制]每一个文件以`export default`的形式暴露一个组件。 + + 允许一个文件中存在多个不同的组件,但仅允许通过`export default`暴露一个组件,其它组件均定义为内部组件。 + +- [强制]每个存放组件的目录使用一个`index.js`以命名导出的形式暴露所有组件。 + + 同目录内的组件相互引用使用`import Foo from './Foo';`进行。 + + 引用其它目录的组件使用`import {Foo} from '../component';`进行。 + + 建议使用[VSCode的export-index插件](https://marketplace.visualstudio.com/items?itemName=BrunoLM.export-index)等插件自动生成`index.js`的内容。 + +## 命名规则 + +- [强制]组件名为PascalCase。 + + 包括函数组件,名称均为PascalCase。 + +- [强制]组件名称与文件名称保持相同。 + + 同时组件名称应当能体现出组件的功能,以便通过观察文件名即确定使用哪一个组件。 + +- [强制]高阶组件使用camelCase命名。 + + 高阶组件事实上并非一个组件,而是一个“生成组件类型”的函数,因此遵守JavaScript函数命名的规范,使用camelCase命名。 + +- [强制]使用`onXxx`形式作为`props`中用于回调的属性名称。 + + 使用统一的命名规则用以区分`props`中回调和非回调部分的属性,在JSX上可以清晰地看到一个组件向上和向下的逻辑交互。 + + 对于不用于回调的函数类型的属性,使用动词作为属性名称。 + + ```javascript + // onClick作为回调以on开头,renderText非回调函数则使用动词 + let Label = ({onClick, renderText}) => {renderText()}; + ``` + +- [建议]使用`withXxx`或`xxxable`形式的词作为高阶组件的名称。 + + 高阶组件是为组件添加行为和功能的函数,因此使用如上形式的词有助于对其功能进行理解。 + +- [建议]作为组件方法的事件处理函数以具备业务含义的词作为名称,不使用`onXxx`形式命名。 + + ```javascript + // Good + class Form { + @autobind + collectAndSubmitData() { + let data = { + name: this.state.name, + age: this.state.age + }; + this.props.onSubmit(data); + } + + @autobind + syncName() { + // ... + } + + @autobind + syncAge() { + // ... + } + + render() { + return ( +
+ + + +
+ ); + } + } + ``` + +## 组件声明 + +- [强制]使用ES Class声明组件,禁止使用`React.createClass`。 + + [React v15.5.0](https://facebook.github.io/react/blog/2017/04/07/react-v15.5.0.html)已经弃用了`React.createClass`函数。 + + ```javascript + // Bad + let Message = React.createClass({ + render() { + return {this.state.message}; + } + }); + + // Good + class Message extends PureComponent { + render() { + return {this.state.message}; + } + } + ``` + +- [强制]不使用`state`的组件声明为函数组件。 + + 函数组件在React中有着特殊的地位,在将来也有可能得到更多的内部优化。 + + ```javascript + // Bad + class NextNumber { + render() { + return {this.props.value + 1} + } + } + + // Good + let NextNumber = ({value}) => {value + 1}; + ``` + +- [强制]所有组件均需声明`propTypes`。 + + `propsTypes`在提升组件健壮性的同时,也是一种类似组件的文档的存在,有助于代码的阅读和理解。 + +- [强制]对于所有非`isRequired`的属性,在`defaultProps`中声明对应的值。 + + 声明初始值有助于对组件初始状态的理解,也可以减少`propTypes`对类型进行校验产生的开销。 + + 对于初始没有值的属性,应当声明初始值为`null`而非`undefined`。 + +- [强制]如无必要,使用静态属性语法声明`propsTypes`、`contextTypes`、`defaultProps`和`state`。 + + 仅当初始`state`需要从`props`计算得到的时候,才将`state`的声明放在构造函数中,其它情况下均使用静态属性声明进行。 + +- [强制]依照规定顺序编排组件中的方法和属性。 + + 按照以下顺序编排组件中的方法和属性: + + 1. `static displayName` + 2. `static propTypes` + 3. `static contextTypes` + 4. `state defaultProps` + 5. `static state` + 6. 其它静态的属性 + 7. 用于事件处理并且以属性的方式(`onClick = e => {...}`)声明的方法 + 8. 其它实例属性 + 9. `constructor` + 10. `getChildContext` + 11. `componentWillMount` + 12. `componentDidMount` + 13. `shouldComponentUpdate` + 14. `componentWillUpdate` + 15. `componentDidUpdate` + 16. `componentWillUnmount` + 17. 事件处理方法 + 18. 其它方法 + 19. `render` + + 其中`shouldComponentUpdate`和`render`是一个组件最容易被阅读的函数,因此放在最下方有助于快速定位。 + +- [建议]无需显式引入React对象。 + + 使用JSX隐式地依赖当前环境下有`React`这一对象,但在源码上并没有显式使用,这种情况下添加`import React from 'react';`会造成一个没有使用的变量存在。 + + 使用[babel-plugin-react-require](https://www.npmjs.com/package/babel-plugin-react-require)插件可以很好地解决这一问题,因此无需显式地编写`import React from 'react';`这一语句。 + +- [建议]使用箭头函数声明函数组件。 + + 箭头函数具备更简洁的语法(无需`function`关键字),且可以在仅有一个语句时省去`return`造成的额外缩进。 + +- [建议]高阶组件返回新的组件类型时,添加`displayName`属性。 + + 同时在`displayName`上声明高阶组件的存在。 + + ```javascript + // Good + let asPureComponent = Component => { + let componentName = Component.displayName || Component.name || 'UnknownComponent'; + return class extends PureComponent { + static displayName = `asPure(${componentName})` + + render() { + return ; + } + }; + }; + ``` + +## 组件实现 + +- [强制]除顶层或路由级组件以外,所有组件均在概念上实现为纯组件(Pure Component)。 + + 本条规则并非要求组件继承自`PureComponent`,“概念上的纯组件”的意思为一个组件在`props`和`state`没有变化(shallowEqual)的情况下,渲染的结果应保持一致,即`shouldComponentUpdate`应当返回`false`。 + + 一个典型的非纯组件是使用了随机数或日期等函数: + + ```javascript + let RandomNumber = () => {Math.random()}; + let Clock = () => {Date.time()}; + ``` + + 非纯组件具备向上的“传染性”,即一个包含非纯组件的组件也必须是非纯组件,依次沿组件树结构向上。由于非纯组件无法通过`shouldComponentUpdate`优化渲染性能且具备传染性,因此要避免在非顶层或路由组件中使用。 + + 如果需要在组件树的某个节点使用随机数、日期等非纯的数据,应当由顶层组件生成这个值并通过`props`传递下来。对于使用Redux等应用状态管理的系统,可以在应用状态中存放相关值(如Redux使用Action Creator生成这些值并通过Action和reducer更新到store中)。 + +- [强制]禁止为继承自`PureComponent`的组件编写`shouldComponentUpdate`实现。 + + 参考[React的相关Issue](https://github.com/facebook/react/issues/9239),在React的实现中,`PureComponent`并不直接实现`shouldComponentUpdate`,而是添加一个`isReactPureComponent`的标记,由`CompositeComponent`通过识别这个标记实现相关的逻辑。因此在`PureComponent`上自定义`shouldComponentUpdate`并无法享受`super.shouldComponentUpdate`的逻辑复用,也会使得这个继承关系失去意义。 + +- [强制]为非继承自`PureComponent`的纯组件实现`shouldComponentUpdate`方法。 + + `shouldComponentUpdate`方法在React的性能中扮演着至关重要的角色,纯组件必定能通过`props`和`state`的变化来决定是否进行渲染,因此如果组件为纯组件且不继承`shouldComponentUpdate`,则应当有自己的`shouldComponentUpdate`实现来减少不必要的渲染。 + +- [建议]为函数组件添加`PureComponent`能力。 + + 函数组件并非一定是纯组件,因此其`shouldComponentUpdate`的实现为`return true;`,这可能导致额外的无意义渲染,因此推荐使用高阶组件为其添加`shouldComponentUpdate`的相关逻辑。 + + 推荐使用[react-pure-stateless-component](https://www.npmjs.com/package/react-pure-stateless-component)库实现这一功能。 + +- [建议]使用`@autobind`进行事件处理方法与`this`的绑定。 + + 由于`PureComponent`使用[`shallowEqual`](https://github.com/facebook/fbjs/blob/master/packages/fbjs/src/core/shallowEqual.js)进行是否渲染的判断,如果在JSX中使用`bind`或箭头函数绑定`this`会造成子组件每次获取的函数都是一个新的引用,这破坏了`shouldComponentUpdate`的逻辑,引入了无意义的重复渲染,因此需要在`render`调用之前就将事件处理方法与`this`绑定,在每次`render`调用中获取同样的引用。 + + 当前比较流行的事前绑定`this`的方法有2种,其一使用类属性的语法: + + ```javascript + class Foo { + onClick = e => { + // ... + } + }; + ``` + + 其二使用`@autobind`的装饰器: + + ```javascript + class Foo { + @autobind + onClick(e) { + // ... + } + } + ``` + + 使用类属性语法虽然可以避免引入一个`autobind`的实现,但存在一定的缺陷: + + 1. 对于新手不容易理解函数内的`this`的定义。 + 2. 无法在函数上使用其它的装饰器(如`memoize`、`deprecated`或检验相关的逻辑等)。 + + 因此,推荐使用`@autobind`装饰器实现`this`的事先绑定,推荐使用[core-decorators](https://www.npmjs.com/package/core-decorators)库提供的相关装饰器实现。 + +## JSX + +- [强制]没有子节点的非DOM组件使用自闭合语法。 + + 对于DOM节点,按照HTML编码规范相关规则进行闭合,**其中void element使用自闭合语法**。 + + ```javascript + // Bad + + + // Good + + ``` + +- [强制]保持起始和结束标签在同一层缩进。 + + 对于标签前面有其它语句(如`return`的情况,使用括号进行换行和缩进)。 + + ```javascript + // Bad + class Message { + render() { + return
+ Hello World +
; + } + } + + // Good + class Message { + render() { + return ( +
+ Hello World +
+ ); + } + } + ``` + + 对于直接`return`的函数组件,可以直接使用括号而省去大括号和`return`关键字: + + ```javascript + let Message = () => ( +
+ Hello World +
+ ); + ``` + +- [强制]对于多属性需要换行,从第一个属性开始,每个属性一行。 + + ```javascript + // 没有子节点 + + + // 有子节点 + + + + + ``` + +- [强制]以字符串字面量作为值的属性使用双引号(`"`),在其它类型表达式中的字符串使用单引号(`'`)。 + + ```javascript + // Bad + + + + // Good + + + ``` + +- [强制]自闭合标签的`/>`前添加一个空格。 + + ```javascript + // Bad + + + + // Good + + ``` + +- [强制]对于值为`true`的属性,省去值部分。 + + ```javascript + // Bad + + + // Good + + ``` + +- [强制]对于需要使用`key`的场合,提供一个唯一标识作为`key`属性的值,禁止使用可能会变化的属性(如索引)。 + + `key`属性是React在进行列表更新时的重要属性,如该属性会发生变化,渲染的性能和**正确性**都无法得到保证。 + + ```javascript + // Bad + {list.map((item, index) => )} + + // Good + {list.map(item => )} + ``` + +- [建议]避免在JSX的属性值中直接使用对象和函数表达式。 + + `PureComponent`使用[`shallowEqual`](https://github.com/facebook/fbjs/blob/master/packages/fbjs/src/core/shallowEqual.js)对`props`和`state`进行比较来决定是否需要渲染,而在JSX的属性值中使用对象、函数表达式会造成每一次的对象引用不同,从而`shallowEqual`会返回`false`,导致不必要的渲染。 + + + ```javascript + // Bad + class WarnButton { + alertMessage(message) { + alert(message); + } + + render() { + return + } + } + + // Good + class WarnButton { + @autobind + alertMessage() { + alert(this.props.message); + } + + render() { + return + } + } + ``` + +- [建议]将JSX的层级控制在3层以内。 + + JSX提供了基于组件的便携的复用形式,因此可以通过将结构中的一部分封装为一个函数组件来很好地拆分大型复杂的结构。层次过深的结构会带来过多缩进、可读性下降等缺点。如同控制函数内代码行数和分支层级一样,对JSX的层级进行控制可以有效提升代码的可维护性。 + + ```javascript + // Bad + let List = ({items}) => ( +
    + { + items.map(item => ( +
  • +
    +

    {item.title}

    + {item.subtitle} +
    +
    {item.content}
    +
    + {item.author}@ +
    +
  • + )) + } +
+ ); + + // Good + let Header = ({title, subtitle}) => ( +
+

{title}

+ {subtitle} +
+ ); + + let Content = ({content}) =>
{content}
; + + let Footer = ({author, postTime}) => ( +
+ {author}@ +
+ ); + + let Item = item => ( +
+
+ +
+
+ ); + + let List = ({items}) => ( +
    + {items.map(Item)} +
+ ); + ```