├── .gitignore ├── editorconfig.md ├── .editorconfig ├── README.md ├── project.md ├── .jscsrc ├── markdown.md ├── .jshintrc ├── security.md ├── normalize.css ├── html.md ├── LICENSE.txt ├── css.md └── javascript.md /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | bower_components 3 | .idea 4 | .DS_Store 5 | .project 6 | .sass-cache 7 | .tmp 8 | npm-debug.log 9 | -------------------------------------------------------------------------------- /editorconfig.md: -------------------------------------------------------------------------------- 1 | 2 | ## 这是什么 3 | 4 | 这是用于统一在代码中使用空格而不是 Tab 的配置 5 | 6 | ## 如何使用 7 | 8 | 首先去 [EditorConfig](http://editorconfig.org/) 下载不同编辑器下的支持插件 9 | 10 | 然后将这里的 `.editorconfig` 文件放到项目根目录 11 | 12 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | # Unix-style newlines with a newline ending every file 4 | [*] 5 | end_of_line = lf 6 | insert_final_newline = true 7 | charset = utf-8 8 | 9 | [*.{html,js,ts,css,scss,xml}] 10 | indent_style = space 11 | indent_size = 4 12 | trim_trailing_whitespace = true 13 | 14 | [*.yml] 15 | indent_style = space 16 | indent_size = 2 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 这里将存放部门统一的文档及代码编写规范,目前还没定稿,欢迎参与讨论。 2 | 3 | ## 规范 4 | 5 | * [开源项目目录规范](./project.md) 6 | * [Javascript 编码规范](./javascript.md) 7 | * [CSS 编码规范](./css.md) 8 | * [HTML 编码规范](./html.md) 9 | * [Markdown 编写规范](./markdown.md) 10 | 11 | ## 计划 12 | 13 | 分三个阶段: 14 | 15 | * 规范阶段(5.10 完成) 16 | * 拟订规范初稿 17 | * 经讨论完成规范定稿 18 | * 工具阶段(待定) 19 | * 完善语言规范的配套检查、格式化工具 20 | * 完成目录结构的脚手架工具 21 | * 实施阶段(待定) 22 | * 工具初步完善后逐步推广使用 23 | 24 | ## 版权 25 | 26 | ![Creative Commons License](http://i.creativecommons.org/l/by/4.0/88x31.png) 27 | 28 | [Creative Commons Attribution 4.0 International License](http://creativecommons.org/licenses/by/4.0/) 29 | -------------------------------------------------------------------------------- /project.md: -------------------------------------------------------------------------------- 1 | 开源项目目录规范 2 | ========================== 3 | 4 | 此为前端开发团队遵循和约定的**开源项目目录规范**,意在实现开源项目目录结构的一致性。 5 | 6 | ## 说明 7 | 文档中使用的关键字「MUST」,「MUST NOT」,「REQUIRED」,「SHALL」,「SHALL 8 | NOT」,「SHOULD」,「SHOULD NOT」,「RECOMMENDED」,「MAY」和「OPTIONAL」在[RFC2119](http://oss.org.cn/man/develop/rfc/RFC2119.txt)中被说明。 9 | 10 | **还未定稿,对规范中提及的点有不赞同的欢迎[提出 issues ](https://github.com/fex-team/styleguide/issues/new)(请添加`目录规范`标签)讨论。** 11 | 12 | ## 目录规范 13 | 14 | 参加的目录结构为: 15 | 16 | ``` 17 | . 18 | ├── .editorconfig 19 | ├── .travis.yml 20 | ├── css 21 | ├── dist 22 | ├── doc 23 | ├── README.md 24 | ├── src 25 | ``` 26 | 27 | ### README.md 28 | 29 | 每个项目都必须「MUST」包含一个`README.md`文件,此文件中应当「SHOULD」概要描述此项目的功能和特点等信息。 30 | 31 | ### .editorconfig 32 | 33 | 每个项目应当「SHOULD」包含`.editorconfig`,用来统一配置编辑器的换行、缩进存储格式,使用方式请参考[editorconfig是什么?](https://github.com/fex-team/styleguide/blob/master/editorconfig.md)。 34 | 35 | ### src 36 | 37 | 项目中所有 JS 源码应当「SHOULD」存放在此目录下,且所有JS文件编写应当「SHOULD」遵循[Javascript 编码规范](https://github.com/fex-team/styleguide/blob/master/javascript.md)。 38 | 39 | ### css、themes、less、sass 40 | 41 | 样式类文件存放应当「SHOULD」遵循以下规律,且文件编写应当「SHOULD」遵循[CSS 编码规范](https://github.com/fex-team/styleguide/blob/master/css.md)。 42 | 43 | * 不带主题的样式文件应当「SHOULD」统一存放在 css 目录下面,且样式中使用的背景图片资源应当「SHOULD」统一存放在 `css/images` 目录下面。 44 | * 带主题的样式文件应当「SHOULD」统一存放在 themes 目录下对应的主题目录下,默认的主题应当「SHOULD」采用 `default` 作为主题名称,且应当「SHOULD」默认提供,样式中对应图片文件应当「SHOULD」存放在样式文件所在的主题目录下的 `images` 目录下。 45 | * less 格式的样式文件应当「SHOULD」统一存放在 `less` 目录下面。 46 | * sass 格式的样式文件应当「SHOULD」统一存放在 `sass` 目录下面。 47 | 48 | ### doc 49 | 50 | 所有项目应当「SHOULD」包含一个 `doc` 目录,用来存放详细的 API 使用文档。 51 | 52 | ### dist 53 | 54 | dist 作为项目输出目录,所有编译生成、提供给用户使用的文件应当「SHOULD」存放在此目录。 55 | 56 | 为了让不太擅长 node.js 的用户可以正常使用编译后的代码,dist 目录应当「SHOULD」包含基本输出结果并提交在 github 中。 57 | 58 | ### build 59 | 60 | 所有工具类脚本应当「SHOULD」放在此目录。 61 | 62 | ### test 63 | 64 | 所有测试相关代码应当「SHOULD」放在此目录。 65 | 66 | ### src_cov 67 | 68 | 为了测试代码覆盖率,所有为测试覆盖率生成的新 JS 文件应当「SHOULD」存放在此目录下面。 69 | -------------------------------------------------------------------------------- /.jscsrc: -------------------------------------------------------------------------------- 1 | /** 2 | * FEX Style Guide (Javascript) 3 | * 4 | * TODO: 5 | * 6 | * 1. 找不到选项:每行只允许一个语句 7 | * 2. 找不到选项:块状代码需要用大括号括起来 8 | */ 9 | { 10 | // 缩进「MUST」使用 4 个空格 11 | "validateIndentation": 4, 12 | 13 | // 大括号(块状代码)前「MUST」使用空格 14 | "requireSpaceBeforeBlockStatements": true, 15 | 16 | // 下列关键字「MUST」使用空格 17 | "requireSpaceAfterKeywords": [ "if", "else", "for", "while", 18 | "do", "try", "catch", "finally" 19 | ], 20 | 21 | // 对象键值的 ":" 后「MUST」使用空格 22 | "requireSpaceBeforeObjectValues": true, 23 | 24 | // 对象键值的 ":" 前「MUST NOT」使用空格 25 | // `,` 和 `;` 前面不允许「MUST NOT」使用空格。 26 | "disallowSpaceBeforeBinaryOperators": [ 27 | ":", 28 | ",", 29 | ";" 30 | ], 31 | 32 | // 二元运算符前后「MUST」使用空格 33 | "requireSpaceBeforeBinaryOperators": [ 34 | "+", 35 | "-", 36 | "*", 37 | "/", 38 | "=", 39 | "==", 40 | "===", 41 | "!=", 42 | "!==", 43 | "|", 44 | "||", 45 | "&", 46 | "&&" 47 | ], 48 | "requireSpaceAfterBinaryOperators": [ 49 | "+", 50 | "-", 51 | "*", 52 | "/", 53 | "=", 54 | "==", 55 | "===", 56 | "!=", 57 | "!==", 58 | "|", 59 | "||", 60 | "&", 61 | "&&" 62 | ], 63 | 64 | // 一元运算符与操作对象间「MUST NOT」使用空格 65 | "disallowSpaceAfterPrefixUnaryOperators": [ "++", "--", "+", "-", "~", "!" ], 66 | 67 | // 函数参数小括号前「MUST NOT」使用空格 68 | "disallowSpacesInFunctionExpression": { 69 | "beforeOpeningRoundBrace": true 70 | }, 71 | 72 | // 小括号里面「MUST NOT」使用空格 73 | "disallowSpacesInsideParentheses": true, 74 | 75 | // 行尾「MUST NOT」使用空格 76 | "disallowTrailingWhitespace": true, 77 | 78 | // 每行「MUST NOT」超过 120 个字符 79 | "maximumLineLength": 120, 80 | 81 | // 一下操作符「MUST NOT」放在一行的最前面,需要放在上一行的后面 82 | "requireOperatorBeforeLineBreak": [ 83 | "?", 84 | "+", 85 | "-", 86 | "/", 87 | "*", 88 | "=", 89 | "==", 90 | "===", 91 | "!=", 92 | "!==", 93 | ">", 94 | ">=", 95 | "<", 96 | "<=", 97 | ",", 98 | ";", 99 | "&&", 100 | "&", 101 | "||", 102 | "|" 103 | ], 104 | 105 | // 文件结尾「MUST」有一个空行 106 | "requireLineFeedAtFileEnd": true, 107 | 108 | // 字符串统一「MUST」使用单引号 109 | "validateQuoteMarks": "'", 110 | 111 | // 「MUST NOT」使用多行字符串 112 | "disallowMultipleLineStrings": true 113 | } 114 | -------------------------------------------------------------------------------- /markdown.md: -------------------------------------------------------------------------------- 1 | Markdown 编写规范 2 | ========================== 3 | 4 | 此为前端开发团队遵循和约定的 **Markdown 编写规范**,意在提高文档的可读性。 5 | 6 | ## 说明 7 | 8 | 文档中使用的关键字「MUST」,「MUST NOT」,「REQUIRED」,「SHALL」,「SHALL 9 | NOT」,「SHOULD」,「SHOULD NOT」,「RECOMMENDED」,「MAY」和「OPTIONAL」在 [RFC2119](http://oss.org.cn/man/develop/rfc/RFC2119.txt) 中有说明。 10 | 11 | **还未定稿,对规范中提及的点有不赞同的欢迎[提出 issues](https://github.com/fex-team/styleguide/issues/new)(请添加 `markdown` 标签)讨论。** 12 | 13 | ## 规则 14 | 15 | * 后缀必须「MUST」使用 `.md`。 16 | * 文件名必须「MUST」使用小写,多个单词之间使用`-`分隔。 17 | * 文件编码必须「MUST」用 UTF-8。 18 | * 文档标题应该「SHOULD」这样写。 19 | 20 | ``` 21 | Markdown 编写规范 22 | ========================== 23 | ``` 24 | * 章节标题必须「MUST」以 `##` 开始,而不是 `#`。 25 | * 章节标题必须「MUST」在 `#` 后加一个空格,且后面没有 `#`。 26 | 27 | ``` 28 | // bad 29 | ##章节1 30 | 31 | // bad 32 | ## 章节1 ## 33 | 34 | // good 35 | ## 章节1 36 | ``` 37 | 38 | * 章节标题和内容间必须「MUST」有一个空行。 39 | 40 | ``` 41 | // bad 42 | ## 章节1 43 | 内容 44 | ## 章节2 45 | 46 | // good 47 | ## 章节1 48 | 49 | 内容 50 | 51 | ## 章节2 52 | ``` 53 | 54 | * 代码段的必须「MUST」使用 Fenced code blocks 风格,如下所示: 55 | 56 | ``` 57 | console.log(""); 58 | ``` 59 | 60 | * 表格的写法应该「SHOULD」参考 [GFM](https://help.github.com/articles/github-flavored-markdown),如下所示: 61 | 62 | ``` 63 | First Header | Second Header 64 | ------------- | ------------- 65 | Content Cell | Content Cell 66 | Content Cell | Content Cell 67 | 68 | | Left-Aligned | Center Aligned | Right Aligned | 69 | | :------------ |:---------------:| -----:| 70 | | col 3 is | some wordy text | $1600 | 71 | | col 2 is | centered | $12 | 72 | | zebra stripes | are neat | $1 | 73 | ``` 74 | 75 | * 中英文混排应该「SHOULD」采用如下规则: 76 | - 英文和数字使用半角字符 77 | - 中文文字之间不加空格 78 | - 中文文字与英文、阿拉伯数字及 @ # $ % ^ & * . ( ) 等符号之间加空格 79 | - 中文标点之间不加空格 80 | - 中文标点与前后字符(无论全角或半角)之间不加空格 81 | - 如果括号内有中文,则使用中文括号 82 | - 如果括号中的内容全部都是英文,则使用半角英文括号 83 | - 当半角符号 / 表示「或者」之意时,与前后的字符之间均不加空格 84 | - 其它具体例子推荐[阅读这里](https://github.com/sparanoid/chinese-copywriting-guidelines) 85 | 86 | * 中文符号应该「SHOULD」使用如下写法: 87 | - 用直角引号(「」)代替双引号(“”),不同输入法的具体设置方法请[参考这里](http://www.zhihu.com/question/19755746) 88 | - 省略号使用「……」,而「。。。」仅用于表示停顿 89 | - 其它可以参考[知乎规范](http://www.zhihu.com/question/20414919) 90 | 91 | * 表达方式,应当「SHOULD」遵循《The Element of Style》: 92 | * 使段落成为文章的单元:一个段落只表达一个主题 93 | * 通常在每一段落开始要点题,在段落结尾要扣题 94 | * 使用主动语态 95 | * 陈述句中使用肯定说法 96 | * 删除不必要的词 97 | * 避免连续使用松散的句子 98 | * 使用相同的结构表达并列的意思 99 | * 将相关的词放在一起 100 | * 在总结中,要用同一种时态(这里指英文中的时态,中文不适用,所以可以不理会) 101 | * 将强调的词放在句末 102 | 103 | ## 扩展阅读 104 | 105 | * Google 后来也出了 [Markdown 规范](https://github.com/google/styleguide/blob/gh-pages/docguide/style.md),很多和这里是一样的,但也增加了一些约定,可以参考 106 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | /** 3 | * 是否阻止位运算符的使用 4 | * 5 | * 有时候为了快速取整或判断,会使用一些位运算符,所以此项设置为 false 6 | */ 7 | "bitwise": false, 8 | 9 | 10 | /** 11 | * 是否要求变量都使用驼峰命名 12 | * 13 | * 默认开启 14 | */ 15 | "camelcase": true, 16 | 17 | 18 | /** 19 | * 是否要求 for/while/if 带花括号 20 | * 21 | * 我们建议多行的时候使用花括号,单行强制写在一行。 22 | * 因为这个选项不管单行多行,所以默认关闭 23 | */ 24 | "curly": false, 25 | 26 | 27 | /** 28 | * 是否强制使用严格等号 29 | * 30 | * 有时候工程师需要判断 null,所以默认不严格要求 31 | */ 32 | "eqeqeq": false, 33 | 34 | 35 | /** 36 | * for-in 语句是否要求过滤原型链上的对象 37 | * 38 | * 默认打开 39 | */ 40 | "forin": true, 41 | 42 | 43 | /** 44 | * 是要求否以 strict 模式检查 45 | * 46 | * 该选项要求文件有 "use strict;" 字符串,而且很多限制有点残酷。不全局要求,需要的模块自行开启 47 | */ 48 | "strict": false, 49 | 50 | 51 | /** 52 | * 是否阻止修改或拓展基本对象(Array、Date 等)的原型链 53 | * 54 | * 原型链污染比较危险,默认打开 55 | */ 56 | "freeze": true, 57 | 58 | 59 | /** 60 | * 是否要求自执行的方法使用括号括起 61 | * 62 | * 默认打开 63 | */ 64 | "immed": true, 65 | 66 | 67 | /** 68 | * 指定缩进大小为 4 个空格 69 | */ 70 | "indent": 4, 71 | 72 | 73 | /** 74 | * 要求变量在使用前声明 75 | */ 76 | "latedef": true, 77 | 78 | 79 | /** 80 | * 要求构造函数大写 81 | */ 82 | "newcap": true, 83 | 84 | 85 | /** 86 | * 不允许使用 arguments.callee 和 arguments.caller 87 | */ 88 | "noarg": true, 89 | 90 | 91 | /** 92 | * 不允许空的代码快,默认关闭 93 | */ 94 | "noempty": false, 95 | 96 | 97 | /** 98 | * 不允许使用 "non-breaking whitespace"。 99 | * 100 | * 这些字符在非 UTF8 页面会导致代码失效 101 | */ 102 | "nonbsp": true, 103 | 104 | 105 | /** 106 | * 阻止直接使用 new 调用构造函数的语句(不赋值对象) 107 | * 108 | * // OK 109 | * var a = new Animal(); 110 | * 111 | * // Warn 112 | * new Animal(); 113 | */ 114 | "nonew": true, 115 | 116 | 117 | /** 118 | * 不允许使用 ++ 和 -- 运算符 119 | * 120 | * 默认关闭 121 | */ 122 | "plusplus": false, 123 | 124 | 125 | /** 126 | * 字符串引号 127 | * 128 | * 默认要求使用单引号 129 | */ 130 | "quotmark": "single", 131 | 132 | 133 | /** 134 | * 提示未定义的变量 135 | * 136 | * 未定义的变量会容易造成全局变量,该项开启 137 | */ 138 | "undef": true, 139 | 140 | 141 | /** 142 | * 字符串不允许以空格加斜杠的形式来换行 143 | * 144 | * // OK 145 | * var str = 'Hello ' + 146 | * 'world'; 147 | * 148 | * // No Way 149 | * var str = 'Hello \ 150 | * world'; 151 | */ 152 | "trailing": true, 153 | 154 | 155 | /** 156 | * 对代码中使用的 debugger 语句默认给出警告 157 | */ 158 | "debug": false, 159 | 160 | 161 | /** 162 | * 变量只能在函数域上定义,在代码块上定义的变量给出警告 163 | * 164 | * // OK 165 | * function test() { 166 | * var x; 167 | * 168 | * if (true) { 169 | * x = 0; 170 | * } 171 | * 172 | * x += 1; 173 | * } 174 | * 175 | * // No Way 176 | * function test() { 177 | * 178 | * if (true) { 179 | * var x = 0; 180 | * } 181 | * 182 | * x += 1; 183 | * } 184 | */ 185 | "funcscope": true, 186 | 187 | 188 | /** 189 | * 写字面量时,逗号放前面给出警告,例如: 190 | * 191 | * var obj = { 192 | * name: 'Anton' 193 | * , handle: 'valueOf' 194 | * , role: 'SW Engineer' 195 | * } 196 | */ 197 | "laxcomma": false, 198 | 199 | 200 | /** 201 | * 阻止在循环语句中产生函数 202 | */ 203 | "loopfunc": false, 204 | 205 | 206 | /** 207 | * 每个函数只允许使用一个 var 定义变量 208 | * 209 | * 默认关闭 210 | */ 211 | "onevar": false, 212 | 213 | /** 214 | * 提示未使用的变量 215 | * 216 | * 默认开启 217 | */ 218 | "unused": true 219 | } 220 | -------------------------------------------------------------------------------- /security.md: -------------------------------------------------------------------------------- 1 | 前端安全规范 2 | ======== 3 | 4 | 本文档描述前端开发人员在应用开发中,需要关注的安全问题和相应的编码规范,旨在杜绝一些常见的安全隐患。 5 | 6 | **说明:**文档中使用的关键字「MUST」,「MUST NOT」,「REQUIRED」,「SHALL」,「SHALL NOT」,「SHOULD」,「SHOULD NOT」,「RECOMMENDED」,「MAY」和「OPTIONAL」在 RFC2119 中有说明。 7 | 8 | ## 背景知识 9 | 10 | 本文不会详细介绍 Web 安全的攻击和防御技术,所以请先参考如下资料了解相关知识: 11 | 12 | [XSS]() 13 | 14 | ## XSS防御 15 | 16 | 请务必阅读:[终极XSS防护备忘录](http://www.fooying.com/chinese-translationthe-ultimate-xss-protection-cheatsheet-for-developers/) 根据其中的描述做好数据转义操作。 17 | 18 | 使用fis进行smarty模板XSS转义: 19 | 20 | fis-plus默认开启xss转义功能,需要编译时加`-o`参数 21 | > fisp release -o 22 | 23 | ## 富文本数据 24 | 25 | 富文本数据「MUST」必须由 RD 依赖最小化原则进行处理,杜绝不安全的内容。 26 | 27 | ## CSRF 28 | 29 | 背景知识: 30 | - [CSRF简单介绍及利用方法](http://drops.wooyun.org/papers/155) 31 | - [Cross-Site Request Forgery (CSRF)](https://www.owasp.org/index.php/Cross-Site_Request_Forgery_%28CSRF%29) 32 | 33 | ## Cookie使用 34 | 35 | 禁止「MUST NOT」给 baidu.com 域下设置 cookie,设置 cookie 请指定为当前域名。 36 | 37 | 禁止「MUST NOT」给页面设置 document.domain = "baidu.com"。 38 | 39 | ## 防钓鱼 40 | 41 | 似乎没啥好方法。 42 | 43 | ## 隐私数据 44 | 45 | 禁止「MUST NOT」在页面中出现 ip 相关信息 ,如有提交 ip 给 server 端的需求,请修改为其它形式,比如:由 rd 在 server 端间接获取 ip 信息。 46 | 47 | 用户的个人隐私信息:姓名、邮箱、电话、QQ 等,必须「MUST」遵守最小化原则,使用不到信息 禁止「MUST NOT」 出现在页面代码中,获取这些数据的 json 接口也必 「MUST」须经过权限控制。 48 | 49 | ## 页面跳转 50 | 51 | 获取 url 中的参数,跳转至另一个站点时, 禁止「MUST NOT」跳转至任意站点,「MUST」设置白名单,只允许跳转至规定的站点。 52 | 53 | 点击页面中的链接跳转至第三方站点的时候,建议「RECOMMENDED」 以类似百度、 Google 搜索结果中 Server 端跳转的形式进行,后端可以对 URL 进行安全性扫描,检测到是一个恶意网站、或者目标资源是可执行文件时,应给予用户强烈的警告,告知其风险。 54 | 55 | ## 第三方功能和资源 56 | 57 | 页面中嵌入的第三方功能,禁止「MUST NOT」直接内嵌第三方 JS 代码,建议以「RECOMMENDED」iframe 形式引入 。 58 | 59 | 页面中如果需要使用第三方的数据,建议 「RECOMMENDED」 由 RD 获取,尽量避免以 jsonp 方式获取,如必须使用 jsonp,请限制好第三方返回的 callback 函数名,对于不允许使用的函数名称,一律禁止。 60 | 61 | 原则上禁止「MUST NOT」使用第三方提供的资源:图片、文档等,如有必须,建议「RECOMMENDED」由 RD 抓取相关内容转存至自有 Server。因为,外链会产生站外请求,可以被利用实施 CSRF 攻击,而且往往存在性能问题。 62 | 63 | ## 点击劫持 64 | 65 | 对一些重要的操作,例如删除数据、支付等,建议「RECOMMENDED」先验证是否被嵌套。如果处于第三方页面的框架里,应弹出确认框提醒用户。确认框的坐标位置最好有一定的随机偏移,从而使攻击者构造的点击区域失效。验证方式为: 66 | 67 | ## Flash 使用 68 | 69 | SWF与js交互的控制属性AllowScriptAccess,「RECOMMENDED」强烈建议为never,如果选择sameDomain或者always,自己应该清楚自己在做什么 70 | 71 | SWF的网络属性AllowNetworking,「RECOMMENDED」强烈建议为none,否则可能会引起CSRF,如果选择all或internel,自己应该清楚自己在做什么 72 | 73 | SWF与js参数传递,「SHOULD」需要进行字符过滤,防止XSS攻击 74 | 75 | 可执行文件(.swf)授信,不应该「SHOULD NOT」使用Security.allowDomain("*"),加载者或被加载者会获得和当前SWF相同的权限 76 | 77 | 非可执行文件(.txt,.xml,.json,.jpg等)授信,不应该「SHOULD NOT」在crossdomain.xml文件中使用allow-access-from domain="*" 78 | 79 | 在加载跨域SWF时,「SHOULD NOT」慎用合并安全域(crossdomain.xml授权或loadBytes()),被加载的SWF会拥有和父SWF同样的权限,能做任何事情 80 | 81 | 在使用sharedEvents时,所发送的事件「SHOULD」应该限制为只包含简单数据的事件,否则可能会把SWF中的数据和舞台上所有对象暴露出去 82 | 83 | ## 上传文件 84 | 85 | 建议「RECOMMENDED」上传接口所在的域名和主域名隔离,并且采用最小化原则限制允许上传的文件大小。 86 | 87 | ## JSON/JSONP协议 88 | 89 | JSON 数据,必须「MUST」设置 http header Content-Type : application/json 90 | 91 | 以 JSONP 形式向第三方提供数据,必须「MUST」设置 http header Content-Type : application/javascript,并且callback function 名称只允许出现:数组、字母、下划线。 92 | 93 | JSON/JSONP 方式提供数据,建议「RECOMMENDED」遵循最小化原则,只暴露需要暴露的信息,并且用白名单限制 referer 防止恶意抓取。 94 | 95 | ## Cross Domain 设置 96 | 97 | 在时 XML Httprequest 2(xhr2) 以及 font-face 如果需要支持跨域访问的需求,禁止「MUST」设置 header 为: Access-Control-Allow-Origin: * ,需要设置为允许访问的地址,比如: Access-Control-Allow-Origin: www.baidu.com 98 | 99 | 100 | ## 参考资料 101 | [JSONP 安全攻防技术](http://blog.knownsec.com/2015/03/jsonp_security_technic) 102 | [Web 前端攻防 2014](http://fex.baidu.com/blog/2014/03/web-sec-2014/) 103 | [New Tricks in XMLHttpRequest2](http://www.html5rocks.com/en/tutorials/file/xhr2/) 104 | [WooYun](http://www.wooyun.org/) 105 | [80sec](http://www.80sec.com/) 106 | [Open Web Application Security Project](https://www.owasp.org/) 107 | [知道创宇](http://blog.knownsec.com/) 108 | [上传攻击框架](http://www.owasp.org.cn/OWASP_Training/Upload_Attack_Framework.pdf) 109 | [SS编码剖析](http://www.freebuf.com/articles/web/43285.html) 110 | [XSS原理-1](http://www.freebuf.com/articles/web/40520.html) 111 | [XSS的原理-2](http://www.freebuf.com/articles/web/42727.html) 112 | -------------------------------------------------------------------------------- /normalize.css: -------------------------------------------------------------------------------- 1 | /*! normalize.css v3.0.1 | MIT License | git.io/normalize */ 2 | 3 | /** 4 | * 1. Set default font family to sans-serif. 5 | * 2. Prevent iOS text size adjust after orientation change, without disabling 6 | * user zoom. 7 | */ 8 | 9 | html { 10 | font-family: sans-serif; /* 1 */ 11 | -ms-text-size-adjust: 100%; /* 2 */ 12 | -webkit-text-size-adjust: 100%; /* 2 */ 13 | } 14 | 15 | /** 16 | * Remove default margin. 17 | */ 18 | 19 | body { 20 | margin: 0; 21 | } 22 | 23 | /* HTML5 display definitions 24 | ========================================================================== */ 25 | 26 | /** 27 | * Correct `block` display not defined for any HTML5 element in IE 8/9. 28 | * Correct `block` display not defined for `details` or `summary` in IE 10/11 and Firefox. 29 | * Correct `block` display not defined for `main` in IE 11. 30 | */ 31 | 32 | article, 33 | aside, 34 | details, 35 | figcaption, 36 | figure, 37 | footer, 38 | header, 39 | hgroup, 40 | main, 41 | nav, 42 | section, 43 | summary { 44 | display: block; 45 | } 46 | 47 | /** 48 | * 1. Correct `inline-block` display not defined in IE 8/9. 49 | * 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera. 50 | */ 51 | 52 | audio, 53 | canvas, 54 | progress, 55 | video { 56 | display: inline-block; /* 1 */ 57 | vertical-align: baseline; /* 2 */ 58 | } 59 | 60 | /** 61 | * Prevent modern browsers from displaying `audio` without controls. 62 | * Remove excess height in iOS 5 devices. 63 | */ 64 | 65 | audio:not([controls]) { 66 | display: none; 67 | height: 0; 68 | } 69 | 70 | /** 71 | * Address `[hidden]` styling not present in IE 8/9/10. 72 | * Hide the `template` element in IE 8/9/11, Safari, and Firefox < 22. 73 | */ 74 | 75 | [hidden], 76 | template { 77 | display: none; 78 | } 79 | 80 | /* Links 81 | ========================================================================== */ 82 | 83 | /** 84 | * Remove the gray background color from active links in IE 10. 85 | */ 86 | 87 | a { 88 | background: transparent; 89 | } 90 | 91 | /** 92 | * Improve readability when focused and also mouse hovered in all browsers. 93 | */ 94 | 95 | a:active, 96 | a:hover { 97 | outline: 0; 98 | } 99 | 100 | /* Text-level semantics 101 | ========================================================================== */ 102 | 103 | /** 104 | * Address styling not present in IE 8/9/10/11, Safari, and Chrome. 105 | */ 106 | 107 | abbr[title] { 108 | border-bottom: 1px dotted; 109 | } 110 | 111 | /** 112 | * Address style set to `bolder` in Firefox 4+, Safari, and Chrome. 113 | */ 114 | 115 | b, 116 | strong { 117 | font-weight: bold; 118 | } 119 | 120 | /** 121 | * Address styling not present in Safari and Chrome. 122 | */ 123 | 124 | dfn { 125 | font-style: italic; 126 | } 127 | 128 | /** 129 | * Address variable `h1` font-size and margin within `section` and `article` 130 | * contexts in Firefox 4+, Safari, and Chrome. 131 | */ 132 | 133 | h1 { 134 | font-size: 2em; 135 | margin: 0.67em 0; 136 | } 137 | 138 | /** 139 | * Address styling not present in IE 8/9. 140 | */ 141 | 142 | mark { 143 | background: #ff0; 144 | color: #000; 145 | } 146 | 147 | /** 148 | * Address inconsistent and variable font size in all browsers. 149 | */ 150 | 151 | small { 152 | font-size: 80%; 153 | } 154 | 155 | /** 156 | * Prevent `sub` and `sup` affecting `line-height` in all browsers. 157 | */ 158 | 159 | sub, 160 | sup { 161 | font-size: 75%; 162 | line-height: 0; 163 | position: relative; 164 | vertical-align: baseline; 165 | } 166 | 167 | sup { 168 | top: -0.5em; 169 | } 170 | 171 | sub { 172 | bottom: -0.25em; 173 | } 174 | 175 | /* Embedded content 176 | ========================================================================== */ 177 | 178 | /** 179 | * Remove border when inside `a` element in IE 8/9/10. 180 | */ 181 | 182 | img { 183 | border: 0; 184 | } 185 | 186 | /** 187 | * Correct overflow not hidden in IE 9/10/11. 188 | */ 189 | 190 | svg:not(:root) { 191 | overflow: hidden; 192 | } 193 | 194 | /* Grouping content 195 | ========================================================================== */ 196 | 197 | /** 198 | * Address margin not present in IE 8/9 and Safari. 199 | */ 200 | 201 | figure { 202 | margin: 1em 40px; 203 | } 204 | 205 | /** 206 | * Address differences between Firefox and other browsers. 207 | */ 208 | 209 | hr { 210 | -moz-box-sizing: content-box; 211 | box-sizing: content-box; 212 | height: 0; 213 | } 214 | 215 | /** 216 | * Contain overflow in all browsers. 217 | */ 218 | 219 | pre { 220 | overflow: auto; 221 | } 222 | 223 | /** 224 | * Address odd `em`-unit font size rendering in all browsers. 225 | */ 226 | 227 | code, 228 | kbd, 229 | pre, 230 | samp { 231 | font-family: monospace, monospace; 232 | font-size: 1em; 233 | } 234 | 235 | /* Forms 236 | ========================================================================== */ 237 | 238 | /** 239 | * Known limitation: by default, Chrome and Safari on OS X allow very limited 240 | * styling of `select`, unless a `border` property is set. 241 | */ 242 | 243 | /** 244 | * 1. Correct color not being inherited. 245 | * Known issue: affects color of disabled elements. 246 | * 2. Correct font properties not being inherited. 247 | * 3. Address margins set differently in Firefox 4+, Safari, and Chrome. 248 | */ 249 | 250 | button, 251 | input, 252 | optgroup, 253 | select, 254 | textarea { 255 | color: inherit; /* 1 */ 256 | font: inherit; /* 2 */ 257 | margin: 0; /* 3 */ 258 | } 259 | 260 | /** 261 | * Address `overflow` set to `hidden` in IE 8/9/10/11. 262 | */ 263 | 264 | button { 265 | overflow: visible; 266 | } 267 | 268 | /** 269 | * Address inconsistent `text-transform` inheritance for `button` and `select`. 270 | * All other form control elements do not inherit `text-transform` values. 271 | * Correct `button` style inheritance in Firefox, IE 8/9/10/11, and Opera. 272 | * Correct `select` style inheritance in Firefox. 273 | */ 274 | 275 | button, 276 | select { 277 | text-transform: none; 278 | } 279 | 280 | /** 281 | * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio` 282 | * and `video` controls. 283 | * 2. Correct inability to style clickable `input` types in iOS. 284 | * 3. Improve usability and consistency of cursor style between image-type 285 | * `input` and others. 286 | */ 287 | 288 | button, 289 | html input[type="button"], /* 1 */ 290 | input[type="reset"], 291 | input[type="submit"] { 292 | -webkit-appearance: button; /* 2 */ 293 | cursor: pointer; /* 3 */ 294 | } 295 | 296 | /** 297 | * Re-set default cursor for disabled elements. 298 | */ 299 | 300 | button[disabled], 301 | html input[disabled] { 302 | cursor: default; 303 | } 304 | 305 | /** 306 | * Remove inner padding and border in Firefox 4+. 307 | */ 308 | 309 | button::-moz-focus-inner, 310 | input::-moz-focus-inner { 311 | border: 0; 312 | padding: 0; 313 | } 314 | 315 | /** 316 | * Address Firefox 4+ setting `line-height` on `input` using `!important` in 317 | * the UA stylesheet. 318 | */ 319 | 320 | input { 321 | line-height: normal; 322 | } 323 | 324 | /** 325 | * It's recommended that you don't attempt to style these elements. 326 | * Firefox's implementation doesn't respect box-sizing, padding, or width. 327 | * 328 | * 1. Address box sizing set to `content-box` in IE 8/9/10. 329 | * 2. Remove excess padding in IE 8/9/10. 330 | */ 331 | 332 | input[type="checkbox"], 333 | input[type="radio"] { 334 | box-sizing: border-box; /* 1 */ 335 | padding: 0; /* 2 */ 336 | } 337 | 338 | /** 339 | * Fix the cursor style for Chrome's increment/decrement buttons. For certain 340 | * `font-size` values of the `input`, it causes the cursor style of the 341 | * decrement button to change from `default` to `text`. 342 | */ 343 | 344 | input[type="number"]::-webkit-inner-spin-button, 345 | input[type="number"]::-webkit-outer-spin-button { 346 | height: auto; 347 | } 348 | 349 | /** 350 | * 1. Address `appearance` set to `searchfield` in Safari and Chrome. 351 | * 2. Address `box-sizing` set to `border-box` in Safari and Chrome 352 | * (include `-moz` to future-proof). 353 | */ 354 | 355 | input[type="search"] { 356 | -webkit-appearance: textfield; /* 1 */ 357 | -moz-box-sizing: content-box; 358 | -webkit-box-sizing: content-box; /* 2 */ 359 | box-sizing: content-box; 360 | } 361 | 362 | /** 363 | * Remove inner padding and search cancel button in Safari and Chrome on OS X. 364 | * Safari (but not Chrome) clips the cancel button when the search input has 365 | * padding (and `textfield` appearance). 366 | */ 367 | 368 | input[type="search"]::-webkit-search-cancel-button, 369 | input[type="search"]::-webkit-search-decoration { 370 | -webkit-appearance: none; 371 | } 372 | 373 | /** 374 | * Define consistent border, margin, and padding. 375 | */ 376 | 377 | fieldset { 378 | border: 1px solid #c0c0c0; 379 | margin: 0 2px; 380 | padding: 0.35em 0.625em 0.75em; 381 | } 382 | 383 | /** 384 | * 1. Correct `color` not being inherited in IE 8/9/10/11. 385 | * 2. Remove padding so people aren't caught out if they zero out fieldsets. 386 | */ 387 | 388 | legend { 389 | border: 0; /* 1 */ 390 | padding: 0; /* 2 */ 391 | } 392 | 393 | /** 394 | * Remove default vertical scrollbar in IE 8/9/10/11. 395 | */ 396 | 397 | textarea { 398 | overflow: auto; 399 | } 400 | 401 | /** 402 | * Don't inherit the `font-weight` (applied by a rule above). 403 | * NOTE: the default cannot safely be changed in Chrome and Safari on OS X. 404 | */ 405 | 406 | optgroup { 407 | font-weight: bold; 408 | } 409 | 410 | /* Tables 411 | ========================================================================== */ 412 | 413 | /** 414 | * Remove most spacing between table cells. 415 | */ 416 | 417 | table { 418 | border-collapse: collapse; 419 | border-spacing: 0; 420 | } 421 | 422 | td, 423 | th { 424 | padding: 0; 425 | } -------------------------------------------------------------------------------- /html.md: -------------------------------------------------------------------------------- 1 | 2 | # HTML编码规范 3 | 4 | 5 | 6 | 7 | [1 前言](#1-%E5%89%8D%E8%A8%80) 8 | 9 | [2 代码风格](#2-%E4%BB%A3%E7%A0%81%E9%A3%8E%E6%A0%BC) 10 | 11 |   [2.1 缩进与换行](#21-%E7%BC%A9%E8%BF%9B%E4%B8%8E%E6%8D%A2%E8%A1%8C) 12 | 13 |   [2.2 命名](#22-%E5%91%BD%E5%90%8D) 14 | 15 |   [2.3 标签](#23-%E6%A0%87%E7%AD%BE) 16 | 17 |   [2.4 属性](#24-%E5%B1%9E%E6%80%A7) 18 | 19 | [3 通用](#3-%E9%80%9A%E7%94%A8) 20 | 21 |   [3.1 DOCTYPE](#31-doctype) 22 | 23 |   [3.2 编码](#32-%E7%BC%96%E7%A0%81) 24 | 25 |   [3.3 CSS和JavaScript引入](#33-css%E5%92%8Cjavascript%E5%BC%95%E5%85%A5) 26 | 27 | [4 head](#4-head) 28 | 29 |   [4.1 title](#41-title) 30 | 31 |   [4.2 favicon](#42-favicon) 32 | 33 |   [4.3 viewport](#43-viewport) 34 | 35 | [5 图片](#5-%E5%9B%BE%E7%89%87) 36 | 37 | [6 表单](#6-%E8%A1%A8%E5%8D%95) 38 | 39 |   [6.1 控件标题](#61-%E6%8E%A7%E4%BB%B6%E6%A0%87%E9%A2%98) 40 | 41 |   [6.2 按钮](#62-%E6%8C%89%E9%92%AE) 42 | 43 |   [6.3 可访问性 (A11Y)](#63-%E5%8F%AF%E8%AE%BF%E9%97%AE%E6%80%A7-a11y) 44 | 45 | [7 多媒体](#7-%E5%A4%9A%E5%AA%92%E4%BD%93) 46 | 47 | [8 模板中的 HTML](#8-%E6%A8%A1%E6%9D%BF%E4%B8%AD%E7%9A%84-html) 48 | 49 | 50 | 51 | 52 | 53 | ## 1 前言 54 | 55 | 56 | HTML作为描述网页结构的超文本标记语言,在百度一直有着广泛的应用。本文档的目标是使HTML代码风格保持一致,容易被理解和被维护。 57 | 58 | 59 | 60 | 61 | ## 2 代码风格 62 | 63 | 64 | ### 2.1 缩进与换行 65 | 66 | 67 | #### [强制] 使用 `4` 个空格做为一个缩进层级,不允许使用 `2` 个空格 或 `tab` 字符。 68 | 69 | 70 | 示例: 71 | 72 | ```html 73 | 77 | ``` 78 | 79 | #### [建议] 每行不得超过 `120` 个字符。 80 | 81 | 解释: 82 | 83 | 过长的代码不容易阅读与维护。但是考虑到 HTML 的特殊性,不做硬性要求。 84 | 85 | 86 | ### 2.2 命名 87 | 88 | 89 | 90 | #### [强制] `class` 必须单词全字母小写,单词间以 `-` 分隔。 91 | 92 | #### [强制] `class` 必须代表相应模块或部件的内容或功能,不得以样式信息进行命名。 93 | 94 | 示例: 95 | 96 | ```html 97 | 98 | 99 | 100 | 101 |
102 | ``` 103 | 104 | #### [强制] 元素 `id` 必须保证页面唯一。 105 | 106 | 解释: 107 | 108 | 同一个页面中,不同的元素包含相同的 id,不符合 id 的属性含义。并且使用 document.getElementById 时可能导致难以追查的问题。 109 | 110 | 111 | #### [建议] `id` 建议单词全字母小写,单词间以 `-` 分隔。同项目必须保持风格一致。 112 | 113 | 114 | #### [建议] `id`、`class` 命名,在避免冲突并描述清楚的前提下尽可能短。 115 | 116 | 示例: 117 | 118 | ```html 119 | 120 | 121 | 122 | 123 | 124 | 125 |

126 | 127 |

128 | 129 | 130 | 131 | 132 | 133 | ``` 134 | 135 | #### [强制] 禁止为了 `hook 脚本`,创建无样式信息的 `class`。 136 | 137 | 解释: 138 | 139 | 不允许 class 只用于让 JavaScript 选择某些元素,class 应该具有明确的语义和样式。否则容易导致 css class 泛滥。 140 | 141 | 使用 id、属性选择作为 hook 是更好的方式。 142 | 143 | 144 | #### [强制] 同一页面,应避免使用相同的 `name` 与 `id`。 145 | 146 | 解释: 147 | 148 | IE 浏览器会混淆元素的 id 和 name 属性, document.getElementById 可能获得不期望的元素。所以在对元素的 id 与 name 属性的命名需要非常小心。 149 | 150 | 一个比较好的实践是,为 id 和 name 使用不同的命名法。 151 | 152 | 示例: 153 | 154 | ```html 155 | 156 |
157 | 161 | ```` 162 | 163 | 164 | ### 2.3 标签 165 | 166 | 167 | #### [强制] 标签名必须使用小写字母。 168 | 169 | 示例: 170 | 171 | ```html 172 | 173 |

Hello StyleGuide!

174 | 175 | 176 |

Hello StyleGuide!

177 | ``` 178 | 179 | #### [强制] 对于无需自闭合的标签,不允许自闭合。 180 | 181 | 解释: 182 | 183 | 常见无需自闭合标签有input、br、img、hr等。 184 | 185 | 186 | 示例: 187 | 188 | ```html 189 | 190 | 191 | 192 | 193 | 194 | ``` 195 | 196 | #### [强制] 对 `HTML5` 中规定允许省略的闭合标签,不允许省略闭合标签。 197 | 198 | 解释: 199 | 200 | 对代码体积要求非常严苛的场景,可以例外。比如:第三方页面使用的投放系统。 201 | 202 | 203 | 示例: 204 | 205 | ```html 206 | 207 | 211 | 212 | 213 | 217 | ``` 218 | 219 | 220 | #### [强制] 标签使用必须符合标签嵌套规则。 221 | 222 | 解释: 223 | 224 | 比如 div 不得置于 p 中,tbody 必须置于 table 中。 225 | 226 | 详细的标签嵌套规则参见[HTML DTD](http://www.cs.tut.fi/~jkorpela/html5.dtd)中的 `Elements` 定义部分。 227 | 228 | 229 | #### [建议] `HTML` 标签的使用应该遵循标签的语义。 230 | 231 | 解释: 232 | 233 | 下面是常见标签语义 234 | 235 | - p - 段落 236 | - h1,h2,h3,h4,h5,h6 - 层级标题 237 | - strong,em - 强调 238 | - ins - 插入 239 | - del - 删除 240 | - abbr - 缩写 241 | - code - 代码标识 242 | - cite - 引述来源作品的标题 243 | - q - 引用 244 | - blockquote - 一段或长篇引用 245 | - ul - 无序列表 246 | - ol - 有序列表 247 | - dl,dt,dd - 定义列表 248 | 249 | 250 | 示例: 251 | 252 | ```html 253 | 254 |

Esprima serves as an important building block for some JavaScript language tools.

255 | 256 | 257 |
Esprima serves as an important building block for some JavaScript language tools.
258 | ``` 259 | 260 | 261 | #### [建议] 在 `CSS` 可以实现相同需求的情况下不得使用表格进行布局。 262 | 263 | 解释: 264 | 265 | 在兼容性允许的情况下应尽量保持语义正确性。对网格对齐和拉伸性有严格要求的场景允许例外,如多列复杂表单。 266 | 267 | 268 | #### [建议] 标签的使用应尽量简洁,减少不必要的标签。 269 | 270 | 示例: 271 | 272 | ```html 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | ``` 281 | 282 | 283 | 284 | ### 2.4 属性 285 | 286 | 287 | #### [强制] 属性名必须使用小写字母。 288 | 289 | 示例: 290 | 291 | ```html 292 | 293 | ...
294 | 295 | 296 | ...
297 | ``` 298 | 299 | 300 | #### [强制] 属性值必须用双引号包围。 301 | 302 | 解释: 303 | 304 | 不允许使用单引号,不允许不使用引号。 305 | 306 | 307 | 示例: 308 | 309 | ```html 310 | 311 | 312 | 313 | 314 | 315 | 316 | ``` 317 | 318 | #### [建议] 布尔类型的属性,建议不添加属性值。 319 | 320 | 示例: 321 | 322 | ```html 323 | 324 | 325 | ``` 326 | 327 | 328 | #### [建议] 自定义属性建议以 `xxx-` 为前缀,推荐使用 `data-`。 329 | 330 | 解释: 331 | 332 | 使用前缀有助于区分自定义属性和标准定义的属性。 333 | 334 | 335 | 示例: 336 | 337 | ```html 338 |
    339 | ``` 340 | 341 | 342 | 343 | 344 | ## 3 通用 345 | 346 | 347 | ### 3.1 DOCTYPE 348 | 349 | 350 | #### [强制] 使用 `HTML5` 的 `doctype` 来启用标准模式,建议使用大写的 `DOCTYPE`。 351 | 352 | 示例: 353 | 354 | ```html 355 | 356 | ``` 357 | 358 | #### [建议] 启用 IE Edge 模式。 359 | 360 | 示例: 361 | 362 | ```html 363 | 364 | ``` 365 | 366 | #### [建议] 在 `html` 标签上设置正确的 lang 属性。 367 | 368 | 解释: 369 | 370 | 有助于提高页面的可访问性,如:让语音合成工具确定其所应该采用的发音,令翻译工具确定其翻译语言等。 371 | 372 | 373 | 示例: 374 | 375 | ```html 376 | 377 | ``` 378 | 379 | 380 | ### 3.2 编码 381 | 382 | 383 | #### [强制] 页面必须使用精简形式,明确指定字符编码。指定字符编码的 `meta` 必须是 `head` 的第一个直接子元素。 384 | 385 | 解释: 386 | 387 | 见 [HTML5 Charset能用吗](http://www.qianduan.net/html5-charset-can-it.html) 一文。 388 | 389 | 示例: 390 | 391 | ```html 392 | 393 | 394 | 395 | ...... 396 | 397 | 398 | ...... 399 | 400 | 401 | ``` 402 | 403 | #### [建议] `HTML` 文件使用无 `BOM` 的 `UTF-8` 编码。 404 | 405 | 解释: 406 | 407 | UTF-8 编码具有更广泛的适应性。BOM 在使用程序或工具处理文件时可能造成不必要的干扰。 408 | 409 | 410 | 411 | ### 3.3 CSS和JavaScript引入 412 | 413 | 414 | #### [强制] 引入 `CSS` 时必须指明 `rel="stylesheet"`。 415 | 416 | 示例: 417 | 418 | ```html 419 | 420 | ``` 421 | 422 | 423 | #### [建议] 引入 `CSS` 和 `JavaScript` 时无须指明 `type` 属性。 424 | 425 | 解释: 426 | 427 | `text/css` 和 `text/javascript` 是 type 的默认值。 428 | 429 | 430 | #### [建议] 展现定义放置于外部 `CSS` 中,行为定义放置于外部 `JavaScript` 中。 431 | 432 | 解释: 433 | 434 | 结构-样式-行为的代码分离,对于提高代码的可阅读性和维护性都有好处。 435 | 436 | 437 | #### [建议] 在 `head` 中引入页面需要的所有 `CSS` 资源。 438 | 439 | 解释: 440 | 441 | 在页面渲染的过程中,新的CSS可能导致元素的样式重新计算和绘制,页面闪烁。 442 | 443 | 444 | #### [建议] `JavaScript` 应当放在页面末尾,或采用异步加载。 445 | 446 | 解释: 447 | 448 | 将 script 放在页面中间将阻断页面的渲染。出于性能方面的考虑,如非必要,请遵守此条建议。 449 | 450 | 451 | 示例: 452 | 453 | ```html 454 | 455 | 456 | 457 | 458 | ``` 459 | 460 | 461 | #### [建议] 移动环境或只针对现代浏览器设计的 Web 应用,如果引用外部资源的 `URL` 协议部分与页面相同,建议省略协议前缀。 462 | 463 | 解释: 464 | 465 | 使用 `protocol-relative URL` 引入 CSS,在 `IE7/8` 下,会发两次请求。是否使用 `protocol-relative URL` 应充分考虑页面针对的环境。 466 | 467 | 468 | 示例: 469 | 470 | ```html 471 | 472 | ``` 473 | 474 | 475 | 476 | 477 | 478 | 479 | ## 4 head 480 | 481 | 482 | ### 4.1 title 483 | 484 | 485 | #### [强制] 页面必须包含 `title` 标签声明标题。 486 | 487 | #### [强制] `title` 必须作为 `head` 的直接子元素,并紧随 `charset` 声明之后。 488 | 489 | 解释: 490 | 491 | title 中如果包含 ascii 之外的字符,浏览器需要知道字符编码类型才能进行解码,否则可能导致乱码。 492 | 493 | 494 | 示例: 495 | 496 | ```html 497 | 498 | 499 | 页面标题 500 | 501 | ``` 502 | 503 | ### 4.2 favicon 504 | 505 | 506 | #### [强制] 保证 `favicon` 可访问。 507 | 508 | 解释: 509 | 510 | 在未指定 favicon 时,大多数浏览器会请求 Web Server 根目录下的 favicon.ico 。为了保证favicon可访问,避免404,必须遵循以下两种方法之一: 511 | 512 | 1. 在 Web Server 根目录放置 favicon.ico 文件。 513 | 2. 使用 link 指定 favicon。 514 | 515 | 516 | 示例: 517 | 518 | ```html 519 | 520 | ``` 521 | 522 | ### 4.3 viewport 523 | 524 | 525 | #### [建议] 若页面欲对移动设备友好,需指定页面的 `viewport`。 526 | 527 | 解释: 528 | 529 | viewport meta tag可以设置可视区域的宽度和初始缩放大小,避免在移动设备上出现页面展示不正常。 530 | 531 | 比如,在页面宽度小于 980px 时,若需 iOS 设备友好,应当设置 viewport 的 width 值来适应你的页面宽度。同时因为不同移动设备分辨率不同,在设置时,应当使用 device-width 和 device-height 变量。 532 | 533 | 另外,为了使 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) 534 | 535 | 536 | 537 | 538 | ## 5 图片 539 | 540 | 541 | 542 | #### [强制] 禁止 `img` 的 `src` 取值为空。延迟加载的图片也要增加默认的 `src`。 543 | 544 | 解释: 545 | 546 | src 取值为空,会导致部分浏览器重新加载一次当前页面,参考: 547 | 548 | 549 | #### [建议] 避免为 `img` 添加不必要的 `title` 属性。 550 | 551 | 解释: 552 | 553 | 多余的 title 影响看图体验,并且增加了页面尺寸。 554 | 555 | #### [建议] 为重要图片添加 `alt` 属性。 556 | 557 | 解释: 558 | 559 | 可以提高图片加载失败时的用户体验。 560 | 561 | #### [建议] 添加 `width` 和 `height` 属性,以避免页面抖动。 562 | 563 | #### [建议] 有下载需求的图片采用 `img` 标签实现,无下载需求的图片采用 `CSS` 背景图实现。 564 | 565 | 解释: 566 | 567 | 1. 产品 logo、用户头像、用户产生的图片等有潜在下载需求的图片,以 img 形式实现,能方便用户下载。 568 | 2. 无下载需求的图片,比如:icon、背景、代码使用的图片等,尽可能采用 css 背景图实现。 569 | 570 | 571 | 572 | ## 6 表单 573 | 574 | 575 | ### 6.1 控件标题 576 | 577 | 578 | #### [强制] 有文本标题的控件必须使用 `label` 标签将其与其标题相关联。 579 | 580 | 解释: 581 | 582 | 有两种方式: 583 | 584 | 1. 将控件置于 label 内。 585 | 2. label 的 for 属性指向控件的 id。 586 | 587 | 推荐使用第一种,减少不必要的 id。如果 DOM 结构不允许直接嵌套,则应使用第二种。 588 | 589 | 590 | 示例: 591 | 592 | ```html 593 | 594 | 595 | 596 | ``` 597 | 598 | 599 | ### 6.2 按钮 600 | 601 | 602 | #### [强制] 使用 `button` 元素时必须指明 `type` 属性值。 603 | 604 | 解释: 605 | 606 | button 元素的默认 type 为 submit,如果被置于 form 元素中,点击后将导致表单提交。为显示区分其作用方便理解,必须给出 type 属性。 607 | 608 | 609 | 示例: 610 | 611 | ```html 612 | 613 | 614 | ``` 615 | 616 | #### [建议] 尽量不要使用按钮类元素的 `name` 属性。 617 | 618 | 解释: 619 | 620 | 由于浏览器兼容性问题,使用按钮的 name 属性会带来许多难以发现的问题。具体情况可参考[此文](http://w3help.org/zh-cn/causes/CM2001)。 621 | 622 | 623 | ### 6.3 可访问性 (A11Y) 624 | 625 | 626 | #### [建议] 负责主要功能的按钮在 `DOM` 中的顺序应靠前。 627 | 628 | 解释: 629 | 630 | 负责主要功能的按钮应相对靠前,以提高可访问性。如果在 CSS 中指定了 `float: right` 则可能导致视觉上主按钮在前,而 DOM 中主按钮靠后的情况。 631 | 632 | 633 | 示例: 634 | 635 | ```html 636 | 637 | 642 | 643 |
    644 |
    645 | 646 | 647 |
    648 |
    649 | 650 | 651 | 656 | 657 |
    658 | 659 | 660 |
    661 | ``` 662 | 663 | #### [建议] 当使用 `JavaScript` 进行表单提交时,如果条件允许,应使原生提交功能正常工作。 664 | 665 | 解释: 666 | 667 | 当浏览器 JS 运行错误或关闭 JS 时,提交功能将无法工作。如果正确指定了 form 元素的 action 属性和表单控件的 name 属性时,提交仍可继续进行。 668 | 669 | 670 | 示例: 671 | 672 | ```html 673 |
    674 |

    675 |

    676 |
    677 | ``` 678 | 679 | #### [建议] 在针对移动设备开发的页面时,根据内容类型指定输入框的 `type` 属性。 680 | 681 | 解释: 682 | 683 | 根据内容类型指定输入框类型,能获得能友好的输入体验。 684 | 685 | 686 | 示例: 687 | 688 | ```html 689 | 690 | ``` 691 | 692 | 693 | 694 | 695 | 696 | ## 7 多媒体 697 | 698 | 699 | 700 | #### [建议] 当在现代浏览器中使用 `audio` 以及 `video` 标签来播放音频、视频时,应当注意格式。 701 | 702 | 解释: 703 | 704 | 音频应尽可能覆盖到如下格式: 705 | 706 | * MP3 707 | * WAV 708 | * Ogg 709 | 710 | 视频应尽可能覆盖到如下格式: 711 | 712 | * MP4 713 | * WebM 714 | * Ogg 715 | 716 | #### [建议] 在支持 `HTML5` 的浏览器中优先使用 `audio` 和 `video` 标签来定义音视频元素。 717 | 718 | #### [建议] 使用退化到插件的方式来对多浏览器进行支持。 719 | 720 | 示例: 721 | 722 | ```html 723 | 730 | 731 | 738 | ``` 739 | 740 | #### [建议] 只在必要的时候开启音视频的自动播放。 741 | 742 | 743 | #### [建议] 在 `object` 标签内部提供指示浏览器不支持该标签的说明。 744 | 745 | 示例: 746 | 747 | ```html 748 | DO NOT SUPPORT THIS TAG 749 | ``` 750 | 751 | 752 | 753 | 754 | ## 8 模板中的 HTML 755 | 756 | 757 | #### [建议] 模板代码的缩进优先保证 `HTML` 代码的缩进规则。 758 | 759 | 示例: 760 | 761 | ```html 762 | 763 | {if $display == true} 764 |
    765 | 770 |
    771 | {/if} 772 | 773 | 774 | {if $display == true} 775 |
    776 | 781 |
    782 | {/if} 783 | ``` 784 | 785 | #### [建议] 模板代码应以保证 `HTML` 单个标签语法的正确性为基本原则。 786 | 787 | 示例: 788 | 789 | ```html 790 | 791 |
  1. { $item.type_name }
  2. 792 | 793 | 794 |
  3. { $item.type_name }
  4. 795 | ``` 796 | 797 | #### [建议] 在循环处理模板数据构造表格时,若要求每行输出固定的个数,建议先将数据分组,之后再循环输出。 798 | 799 | 示例: 800 | 801 | ```html 802 | 803 | 804 | {foreach $item_list as $item_group} 805 | 806 | {foreach $item_group as $item} 807 | 808 | {/foreach} 809 | 810 | {/foreach} 811 |
    { $item.name }
    812 | 813 | 814 | 815 | 816 | {foreach $item_list as $item} 817 | 818 | {if $item@iteration is div by 5} 819 | 820 | 821 | {/if} 822 | {/foreach} 823 | 824 |
    { $item.name }
    825 | ``` 826 | 827 | 828 | 829 | 830 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Attribution 4.0 International 2 | 3 | ======================================================================= 4 | 5 | Creative Commons Corporation ("Creative Commons") is not a law firm and 6 | does not provide legal services or legal advice. Distribution of 7 | Creative Commons public licenses does not create a lawyer-client or 8 | other relationship. Creative Commons makes its licenses and related 9 | information available on an "as-is" basis. Creative Commons gives no 10 | warranties regarding its licenses, any material licensed under their 11 | terms and conditions, or any related information. Creative Commons 12 | disclaims all liability for damages resulting from their use to the 13 | fullest extent possible. 14 | 15 | Using Creative Commons Public Licenses 16 | 17 | Creative Commons public licenses provide a standard set of terms and 18 | conditions that creators and other rights holders may use to share 19 | original works of authorship and other material subject to copyright 20 | and certain other rights specified in the public license below. The 21 | following considerations are for informational purposes only, are not 22 | exhaustive, and do not form part of our licenses. 23 | 24 | Considerations for licensors: Our public licenses are 25 | intended for use by those authorized to give the public 26 | permission to use material in ways otherwise restricted by 27 | copyright and certain other rights. Our licenses are 28 | irrevocable. Licensors should read and understand the terms 29 | and conditions of the license they choose before applying it. 30 | Licensors should also secure all rights necessary before 31 | applying our licenses so that the public can reuse the 32 | material as expected. Licensors should clearly mark any 33 | material not subject to the license. This includes other CC- 34 | licensed material, or material used under an exception or 35 | limitation to copyright. More considerations for licensors: 36 | wiki.creativecommons.org/Considerations_for_licensors 37 | 38 | Considerations for the public: By using one of our public 39 | licenses, a licensor grants the public permission to use the 40 | licensed material under specified terms and conditions. If 41 | the licensor's permission is not necessary for any reason--for 42 | example, because of any applicable exception or limitation to 43 | copyright--then that use is not regulated by the license. Our 44 | licenses grant only permissions under copyright and certain 45 | other rights that a licensor has authority to grant. Use of 46 | the licensed material may still be restricted for other 47 | reasons, including because others have copyright or other 48 | rights in the material. A licensor may make special requests, 49 | such as asking that all changes be marked or described. 50 | Although not required by our licenses, you are encouraged to 51 | respect those requests where reasonable. More_considerations 52 | for the public: 53 | wiki.creativecommons.org/Considerations_for_licensees 54 | 55 | ======================================================================= 56 | 57 | Creative Commons Attribution 4.0 International Public License 58 | 59 | By exercising the Licensed Rights (defined below), You accept and agree 60 | to be bound by the terms and conditions of this Creative Commons 61 | Attribution 4.0 International Public License ("Public License"). To the 62 | extent this Public License may be interpreted as a contract, You are 63 | granted the Licensed Rights in consideration of Your acceptance of 64 | these terms and conditions, and the Licensor grants You such rights in 65 | consideration of benefits the Licensor receives from making the 66 | Licensed Material available under these terms and conditions. 67 | 68 | 69 | Section 1 -- Definitions. 70 | 71 | a. Adapted Material means material subject to Copyright and Similar 72 | Rights that is derived from or based upon the Licensed Material 73 | and in which the Licensed Material is translated, altered, 74 | arranged, transformed, or otherwise modified in a manner requiring 75 | permission under the Copyright and Similar Rights held by the 76 | Licensor. For purposes of this Public License, where the Licensed 77 | Material is a musical work, performance, or sound recording, 78 | Adapted Material is always produced where the Licensed Material is 79 | synched in timed relation with a moving image. 80 | 81 | b. Adapter's License means the license You apply to Your Copyright 82 | and Similar Rights in Your contributions to Adapted Material in 83 | accordance with the terms and conditions of this Public License. 84 | 85 | c. Copyright and Similar Rights means copyright and/or similar rights 86 | closely related to copyright including, without limitation, 87 | performance, broadcast, sound recording, and Sui Generis Database 88 | Rights, without regard to how the rights are labeled or 89 | categorized. For purposes of this Public License, the rights 90 | specified in Section 2(b)(1)-(2) are not Copyright and Similar 91 | Rights. 92 | 93 | d. Effective Technological Measures means those measures that, in the 94 | absence of proper authority, may not be circumvented under laws 95 | fulfilling obligations under Article 11 of the WIPO Copyright 96 | Treaty adopted on December 20, 1996, and/or similar international 97 | agreements. 98 | 99 | e. Exceptions and Limitations means fair use, fair dealing, and/or 100 | any other exception or limitation to Copyright and Similar Rights 101 | that applies to Your use of the Licensed Material. 102 | 103 | f. Licensed Material means the artistic or literary work, database, 104 | or other material to which the Licensor applied this Public 105 | License. 106 | 107 | g. Licensed Rights means the rights granted to You subject to the 108 | terms and conditions of this Public License, which are limited to 109 | all Copyright and Similar Rights that apply to Your use of the 110 | Licensed Material and that the Licensor has authority to license. 111 | 112 | h. Licensor means the individual(s) or entity(ies) granting rights 113 | under this Public License. 114 | 115 | i. Share means to provide material to the public by any means or 116 | process that requires permission under the Licensed Rights, such 117 | as reproduction, public display, public performance, distribution, 118 | dissemination, communication, or importation, and to make material 119 | available to the public including in ways that members of the 120 | public may access the material from a place and at a time 121 | individually chosen by them. 122 | 123 | j. Sui Generis Database Rights means rights other than copyright 124 | resulting from Directive 96/9/EC of the European Parliament and of 125 | the Council of 11 March 1996 on the legal protection of databases, 126 | as amended and/or succeeded, as well as other essentially 127 | equivalent rights anywhere in the world. 128 | 129 | k. You means the individual or entity exercising the Licensed Rights 130 | under this Public License. Your has a corresponding meaning. 131 | 132 | 133 | Section 2 -- Scope. 134 | 135 | a. License grant. 136 | 137 | 1. Subject to the terms and conditions of this Public License, 138 | the Licensor hereby grants You a worldwide, royalty-free, 139 | non-sublicensable, non-exclusive, irrevocable license to 140 | exercise the Licensed Rights in the Licensed Material to: 141 | 142 | a. reproduce and Share the Licensed Material, in whole or 143 | in part; and 144 | 145 | b. produce, reproduce, and Share Adapted Material. 146 | 147 | 2. Exceptions and Limitations. For the avoidance of doubt, where 148 | Exceptions and Limitations apply to Your use, this Public 149 | License does not apply, and You do not need to comply with 150 | its terms and conditions. 151 | 152 | 3. Term. The term of this Public License is specified in Section 153 | 6(a). 154 | 155 | 4. Media and formats; technical modifications allowed. The 156 | Licensor authorizes You to exercise the Licensed Rights in 157 | all media and formats whether now known or hereafter created, 158 | and to make technical modifications necessary to do so. The 159 | Licensor waives and/or agrees not to assert any right or 160 | authority to forbid You from making technical modifications 161 | necessary to exercise the Licensed Rights, including 162 | technical modifications necessary to circumvent Effective 163 | Technological Measures. For purposes of this Public License, 164 | simply making modifications authorized by this Section 2(a) 165 | (4) never produces Adapted Material. 166 | 167 | 5. Downstream recipients. 168 | 169 | a. Offer from the Licensor -- Licensed Material. Every 170 | recipient of the Licensed Material automatically 171 | receives an offer from the Licensor to exercise the 172 | Licensed Rights under the terms and conditions of this 173 | Public License. 174 | 175 | b. No downstream restrictions. You may not offer or impose 176 | any additional or different terms or conditions on, or 177 | apply any Effective Technological Measures to, the 178 | Licensed Material if doing so restricts exercise of the 179 | Licensed Rights by any recipient of the Licensed 180 | Material. 181 | 182 | 6. No endorsement. Nothing in this Public License constitutes or 183 | may be construed as permission to assert or imply that You 184 | are, or that Your use of the Licensed Material is, connected 185 | with, or sponsored, endorsed, or granted official status by, 186 | the Licensor or others designated to receive attribution as 187 | provided in Section 3(a)(1)(A)(i). 188 | 189 | b. Other rights. 190 | 191 | 1. Moral rights, such as the right of integrity, are not 192 | licensed under this Public License, nor are publicity, 193 | privacy, and/or other similar personality rights; however, to 194 | the extent possible, the Licensor waives and/or agrees not to 195 | assert any such rights held by the Licensor to the limited 196 | extent necessary to allow You to exercise the Licensed 197 | Rights, but not otherwise. 198 | 199 | 2. Patent and trademark rights are not licensed under this 200 | Public License. 201 | 202 | 3. To the extent possible, the Licensor waives any right to 203 | collect royalties from You for the exercise of the Licensed 204 | Rights, whether directly or through a collecting society 205 | under any voluntary or waivable statutory or compulsory 206 | licensing scheme. In all other cases the Licensor expressly 207 | reserves any right to collect such royalties. 208 | 209 | 210 | Section 3 -- License Conditions. 211 | 212 | Your exercise of the Licensed Rights is expressly made subject to the 213 | following conditions. 214 | 215 | a. Attribution. 216 | 217 | 1. If You Share the Licensed Material (including in modified 218 | form), You must: 219 | 220 | a. retain the following if it is supplied by the Licensor 221 | with the Licensed Material: 222 | 223 | i. identification of the creator(s) of the Licensed 224 | Material and any others designated to receive 225 | attribution, in any reasonable manner requested by 226 | the Licensor (including by pseudonym if 227 | designated); 228 | 229 | ii. a copyright notice; 230 | 231 | iii. a notice that refers to this Public License; 232 | 233 | iv. a notice that refers to the disclaimer of 234 | warranties; 235 | 236 | v. a URI or hyperlink to the Licensed Material to the 237 | extent reasonably practicable; 238 | 239 | b. indicate if You modified the Licensed Material and 240 | retain an indication of any previous modifications; and 241 | 242 | c. indicate the Licensed Material is licensed under this 243 | Public License, and include the text of, or the URI or 244 | hyperlink to, this Public License. 245 | 246 | 2. You may satisfy the conditions in Section 3(a)(1) in any 247 | reasonable manner based on the medium, means, and context in 248 | which You Share the Licensed Material. For example, it may be 249 | reasonable to satisfy the conditions by providing a URI or 250 | hyperlink to a resource that includes the required 251 | information. 252 | 253 | 3. If requested by the Licensor, You must remove any of the 254 | information required by Section 3(a)(1)(A) to the extent 255 | reasonably practicable. 256 | 257 | 4. If You Share Adapted Material You produce, the Adapter's 258 | License You apply must not prevent recipients of the Adapted 259 | Material from complying with this Public License. 260 | 261 | 262 | Section 4 -- Sui Generis Database Rights. 263 | 264 | Where the Licensed Rights include Sui Generis Database Rights that 265 | apply to Your use of the Licensed Material: 266 | 267 | a. for the avoidance of doubt, Section 2(a)(1) grants You the right 268 | to extract, reuse, reproduce, and Share all or a substantial 269 | portion of the contents of the database; 270 | 271 | b. if You include all or a substantial portion of the database 272 | contents in a database in which You have Sui Generis Database 273 | Rights, then the database in which You have Sui Generis Database 274 | Rights (but not its individual contents) is Adapted Material; and 275 | 276 | c. You must comply with the conditions in Section 3(a) if You Share 277 | all or a substantial portion of the contents of the database. 278 | 279 | For the avoidance of doubt, this Section 4 supplements and does not 280 | replace Your obligations under this Public License where the Licensed 281 | Rights include other Copyright and Similar Rights. 282 | 283 | 284 | Section 5 -- Disclaimer of Warranties and Limitation of Liability. 285 | 286 | a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE 287 | EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS 288 | AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF 289 | ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS, 290 | IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION, 291 | WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR 292 | PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS, 293 | ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT 294 | KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT 295 | ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU. 296 | 297 | b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE 298 | TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, 299 | NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT, 300 | INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES, 301 | COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR 302 | USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN 303 | ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR 304 | DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR 305 | IN PART, THIS LIMITATION MAY NOT APPLY TO YOU. 306 | 307 | c. The disclaimer of warranties and limitation of liability provided 308 | above shall be interpreted in a manner that, to the extent 309 | possible, most closely approximates an absolute disclaimer and 310 | waiver of all liability. 311 | 312 | 313 | Section 6 -- Term and Termination. 314 | 315 | a. This Public License applies for the term of the Copyright and 316 | Similar Rights licensed here. However, if You fail to comply with 317 | this Public License, then Your rights under this Public License 318 | terminate automatically. 319 | 320 | b. Where Your right to use the Licensed Material has terminated under 321 | Section 6(a), it reinstates: 322 | 323 | 1. automatically as of the date the violation is cured, provided 324 | it is cured within 30 days of Your discovery of the 325 | violation; or 326 | 327 | 2. upon express reinstatement by the Licensor. 328 | 329 | For the avoidance of doubt, this Section 6(b) does not affect any 330 | right the Licensor may have to seek remedies for Your violations 331 | of this Public License. 332 | 333 | c. For the avoidance of doubt, the Licensor may also offer the 334 | Licensed Material under separate terms or conditions or stop 335 | distributing the Licensed Material at any time; however, doing so 336 | will not terminate this Public License. 337 | 338 | d. Sections 1, 5, 6, 7, and 8 survive termination of this Public 339 | License. 340 | 341 | 342 | Section 7 -- Other Terms and Conditions. 343 | 344 | a. The Licensor shall not be bound by any additional or different 345 | terms or conditions communicated by You unless expressly agreed. 346 | 347 | b. Any arrangements, understandings, or agreements regarding the 348 | Licensed Material not stated herein are separate from and 349 | independent of the terms and conditions of this Public License. 350 | 351 | 352 | Section 8 -- Interpretation. 353 | 354 | a. For the avoidance of doubt, this Public License does not, and 355 | shall not be interpreted to, reduce, limit, restrict, or impose 356 | conditions on any use of the Licensed Material that could lawfully 357 | be made without permission under this Public License. 358 | 359 | b. To the extent possible, if any provision of this Public License is 360 | deemed unenforceable, it shall be automatically reformed to the 361 | minimum extent necessary to make it enforceable. If the provision 362 | cannot be reformed, it shall be severed from this Public License 363 | without affecting the enforceability of the remaining terms and 364 | conditions. 365 | 366 | c. No term or condition of this Public License will be waived and no 367 | failure to comply consented to unless expressly agreed to by the 368 | Licensor. 369 | 370 | d. Nothing in this Public License constitutes or may be interpreted 371 | as a limitation upon, or waiver of, any privileges and immunities 372 | that apply to the Licensor or You, including from the legal 373 | processes of any jurisdiction or authority. 374 | 375 | 376 | ======================================================================= 377 | 378 | Creative Commons is not a party to its public licenses. 379 | Notwithstanding, Creative Commons may elect to apply one of its public 380 | licenses to material it publishes and in those instances will be 381 | considered the "Licensor." Except for the limited purpose of indicating 382 | that material is shared under a Creative Commons public license or as 383 | otherwise permitted by the Creative Commons policies published at 384 | creativecommons.org/policies, Creative Commons does not authorize the 385 | use of the trademark "Creative Commons" or any other trademark or logo 386 | of Creative Commons without its prior written consent including, 387 | without limitation, in connection with any unauthorized modifications 388 | to any of its public licenses or any other arrangements, 389 | understandings, or agreements concerning use of licensed material. For 390 | the avoidance of doubt, this paragraph does not form part of the public 391 | licenses. 392 | 393 | Creative Commons may be contacted at creativecommons.org. 394 | -------------------------------------------------------------------------------- /css.md: -------------------------------------------------------------------------------- 1 | 2 | # CSS编码规范 3 | 4 | 5 | 6 | 7 | [1 前言](#1-%E5%89%8D%E8%A8%80) 8 | 9 | [2 代码风格](#2-%E4%BB%A3%E7%A0%81%E9%A3%8E%E6%A0%BC) 10 | 11 |   [2.1 文件](#21-%E6%96%87%E4%BB%B6) 12 | 13 |   [2.2 缩进](#22-%E7%BC%A9%E8%BF%9B) 14 | 15 |   [2.3 空格](#23-%E7%A9%BA%E6%A0%BC) 16 | 17 |   [2.4 行长度](#24-%E8%A1%8C%E9%95%BF%E5%BA%A6) 18 | 19 |   [2.5 选择器](#25-%E9%80%89%E6%8B%A9%E5%99%A8) 20 | 21 |   [2.6 属性](#26-%E5%B1%9E%E6%80%A7) 22 | 23 | [3 通用](#3-%E9%80%9A%E7%94%A8) 24 | 25 |   [3.1 选择器](#31-%E9%80%89%E6%8B%A9%E5%99%A8) 26 | 27 |   [3.2 属性缩写](#32-%E5%B1%9E%E6%80%A7%E7%BC%A9%E5%86%99) 28 | 29 |   [3.3 属性书写顺序](#33-%E5%B1%9E%E6%80%A7%E4%B9%A6%E5%86%99%E9%A1%BA%E5%BA%8F) 30 | 31 |   [3.4 清除浮动](#34-%E6%B8%85%E9%99%A4%E6%B5%AE%E5%8A%A8) 32 | 33 |   [3.5 !important](#35-important) 34 | 35 |   [3.6 z-index](#36-z-index) 36 | 37 | [4 值与单位](#4-%E5%80%BC%E4%B8%8E%E5%8D%95%E4%BD%8D) 38 | 39 |   [4.1 文本](#41-%E6%96%87%E6%9C%AC) 40 | 41 |   [4.2 数值](#42-%E6%95%B0%E5%80%BC) 42 | 43 |   [4.3 url()](#43-url) 44 | 45 |   [4.4 长度](#44-%E9%95%BF%E5%BA%A6) 46 | 47 |   [4.5 颜色](#45-%E9%A2%9C%E8%89%B2) 48 | 49 |   [4.6 2D 位置](#46-2d-%E4%BD%8D%E7%BD%AE) 50 | 51 | [5 文本编排](#5-%E6%96%87%E6%9C%AC%E7%BC%96%E6%8E%92) 52 | 53 |   [5.1 字体族](#51-%E5%AD%97%E4%BD%93%E6%97%8F) 54 | 55 |   [5.2 字号](#52-%E5%AD%97%E5%8F%B7) 56 | 57 |   [5.3 字体风格](#53-%E5%AD%97%E4%BD%93%E9%A3%8E%E6%A0%BC) 58 | 59 |   [5.4 字重](#54-%E5%AD%97%E9%87%8D) 60 | 61 |   [5.5 行高](#55-%E8%A1%8C%E9%AB%98) 62 | 63 | [6 变换与动画](#6-%E5%8F%98%E6%8D%A2%E4%B8%8E%E5%8A%A8%E7%94%BB) 64 | 65 | [7 响应式](#7-%E5%93%8D%E5%BA%94%E5%BC%8F) 66 | 67 | [8 兼容性](#8-%E5%85%BC%E5%AE%B9%E6%80%A7) 68 | 69 |   [8.1 属性前缀](#81-%E5%B1%9E%E6%80%A7%E5%89%8D%E7%BC%80) 70 | 71 |   [8.2 Hack](#82-hack) 72 | 73 |   [8.3 Expression](#83-expression) 74 | 75 | 76 | 77 | 78 | 79 | ## 1 前言 80 | 81 | 82 | CSS作为网页样式的描述语言,在百度一直有着广泛的应用。本文档的目标是使CSS代码风格保持一致,容易被理解和被维护。 83 | 84 | 虽然本文档是针对CSS设计的,但是在使用各种CSS的预编译器(如less、sass、stylus等)时,适用的部分也应尽量遵循本文档的约定。 85 | 86 | 87 | ## 2 代码风格 88 | 89 | 90 | ### 2.1 文件 91 | 92 | 93 | #### [建议] `CSS` 文件使用无 `BOM` 的 `UTF-8` 编码。 94 | 95 | 解释: 96 | 97 | UTF-8 编码具有更广泛的适应性。BOM 在使用程序或工具处理文件时可能造成不必要的干扰。 98 | 99 | ### 2.2 缩进 100 | 101 | 102 | #### [强制] 使用 `4` 个空格做为一个缩进层级,不允许使用 `2` 个空格 或 `tab` 字符。 103 | 104 | 105 | 示例: 106 | 107 | ```css 108 | .selector { 109 | margin: 0; 110 | padding: 0; 111 | } 112 | ``` 113 | 114 | ### 2.3 空格 115 | 116 | 117 | #### [强制] `选择器` 与 `{` 之间必须包含空格。 118 | 119 | 示例: 120 | 121 | ```css 122 | .selector { 123 | } 124 | ``` 125 | 126 | #### [强制] `属性名` 与之后的 `:` 之间不允许包含空格, `:` 与 `属性值` 之间必须包含空格。 127 | 128 | 示例: 129 | 130 | ```css 131 | margin: 0; 132 | ``` 133 | 134 | #### [强制] `列表型属性值` 书写在单行时,`,` 后必须跟一个空格。 135 | 136 | 示例: 137 | 138 | ```css 139 | font-family: Arial, sans-serif; 140 | ``` 141 | 142 | ### 2.4 行长度 143 | 144 | 145 | #### [强制] 每行不得超过 `120` 个字符,除非单行不可分割。 146 | 147 | 解释: 148 | 149 | 常见不可分割的场景为URL超长。 150 | 151 | 152 | #### [建议] 对于超长的样式,在样式值的 `空格` 处或 `,` 后换行,建议按逻辑分组。 153 | 154 | 示例: 155 | 156 | ```css 157 | /* 不同属性值按逻辑分组 */ 158 | background: 159 | transparent url(aVeryVeryVeryLongUrlIsPlacedHere) 160 | no-repeat 0 0; 161 | 162 | /* 可重复多次的属性,每次重复一行 */ 163 | background-image: 164 | url(aVeryVeryVeryLongUrlIsPlacedHere) 165 | url(anotherVeryVeryVeryLongUrlIsPlacedHere); 166 | 167 | /* 类似函数的属性值可以根据函数调用的缩进进行 */ 168 | background-image: -webkit-gradient( 169 | linear, 170 | left bottom, 171 | left top, 172 | color-stop(0.04, rgb(88,94,124)), 173 | color-stop(0.52, rgb(115,123,162)) 174 | ); 175 | ``` 176 | 177 | ### 2.5 选择器 178 | 179 | 180 | #### [强制] 当一个 rule 包含多个 selector 时,每个选择器声明必须独占一行。 181 | 182 | 示例: 183 | 184 | ```css 185 | /* good */ 186 | .post, 187 | .page, 188 | .comment { 189 | line-height: 1.5; 190 | } 191 | 192 | /* bad */ 193 | .post, .page, .comment { 194 | line-height: 1.5; 195 | } 196 | ``` 197 | 198 | #### [强制] `>`、`+`、`~` 选择器的两边各保留一个空格。 199 | 200 | 示例: 201 | 202 | ```css 203 | /* good */ 204 | main > nav { 205 | padding: 10px; 206 | } 207 | 208 | label + input { 209 | margin-left: 5px; 210 | } 211 | 212 | input:checked ~ button { 213 | background-color: #69C; 214 | } 215 | 216 | /* bad */ 217 | main>nav { 218 | padding: 10px; 219 | } 220 | 221 | label+input { 222 | margin-left: 5px; 223 | } 224 | 225 | input:checked~button { 226 | background-color: #69C; 227 | } 228 | ``` 229 | 230 | #### [强制] 属性选择器中的值必须用双引号包围。 231 | 232 | 解释: 233 | 234 | 不允许使用单引号,不允许不使用引号。 235 | 236 | 237 | 示例: 238 | 239 | ```css 240 | /* good */ 241 | article[character="juliet"] { 242 | voice-family: "Vivien Leigh", victoria, female 243 | } 244 | 245 | /* bad */ 246 | article[character='juliet'] { 247 | voice-family: "Vivien Leigh", victoria, female 248 | } 249 | ``` 250 | 251 | ### 2.6 属性 252 | 253 | 254 | #### [强制] 属性定义必须另起一行。 255 | 256 | 示例: 257 | 258 | ```css 259 | /* good */ 260 | .selector { 261 | margin: 0; 262 | padding: 0; 263 | } 264 | 265 | /* bad */ 266 | .selector { margin: 0; padding: 0; } 267 | ``` 268 | 269 | #### [强制] 属性定义后必须以分号结尾。 270 | 271 | 示例: 272 | 273 | ```css 274 | /* good */ 275 | .selector { 276 | margin: 0; 277 | } 278 | 279 | /* bad */ 280 | .selector { 281 | margin: 0 282 | } 283 | ``` 284 | 285 | 286 | 287 | 288 | 289 | 290 | ## 3 通用 291 | 292 | 293 | 294 | 295 | ### 3.1 选择器 296 | 297 | 298 | #### [强制] 如无必要,不得为 `id`、`class` 选择器添加类型选择器进行限定。 299 | 300 | 解释: 301 | 302 | 在性能和维护性上,都有一定的影响。 303 | 304 | 305 | 示例: 306 | 307 | 308 | ```css 309 | /* good */ 310 | #error, 311 | .danger-message { 312 | font-color: #c00; 313 | } 314 | 315 | /* bad */ 316 | dialog#error, 317 | p.danger-message { 318 | font-color: #c00; 319 | } 320 | ``` 321 | 322 | #### [建议] 选择器的嵌套层级应不大于 3 级,位置靠后的限定条件应尽可能精确。 323 | 324 | 示例: 325 | 326 | ```css 327 | /* good */ 328 | #username input {} 329 | .comment .avatar {} 330 | 331 | /* bad */ 332 | .page .header .login #username input {} 333 | .comment div * {} 334 | ``` 335 | 336 | 337 | 338 | ### 3.2 属性缩写 339 | 340 | 341 | 342 | #### [建议] 在可以使用缩写的情况下,尽量使用属性缩写。 343 | 344 | 示例: 345 | 346 | ```css 347 | /* good */ 348 | .post { 349 | font: 12px/1.5 arial, sans-serif; 350 | } 351 | 352 | /* bad */ 353 | .post { 354 | font-family: arial, sans-serif; 355 | font-size: 12px; 356 | line-height: 1.5; 357 | } 358 | ``` 359 | 360 | #### [建议] 使用 `border` / `margin` / `padding` 等缩写时,应注意隐含值对实际数值的影响,确实需要设置多个方向的值时才使用缩写。 361 | 362 | 解释: 363 | 364 | border / margin / padding 等缩写会同时设置多个属性的值,容易覆盖不需要覆盖的设定。如某些方向需要继承其他声明的值,则应该分开设置。 365 | 366 | 367 | 示例: 368 | 369 | ```css 370 | /* centering
    horizontally and highlight featured ones */ 371 | article { 372 | margin: 5px; 373 | border: 1px solid #999; 374 | } 375 | 376 | /* good */ 377 | .page { 378 | margin-right: auto; 379 | margin-left: auto; 380 | } 381 | 382 | .featured { 383 | border-color: #69c; 384 | } 385 | 386 | /* bad */ 387 | .page { 388 | margin: 5px auto; /* introducing redundancy */ 389 | } 390 | 391 | .featured { 392 | border: 1px solid #69c; /* introducing redundancy */ 393 | } 394 | ``` 395 | 396 | 397 | ### 3.3 属性书写顺序 398 | 399 | 400 | #### [建议] 同一 rule set 下的属性在书写时,应按功能进行分组,并以 **Formatting Model(布局方式、位置) > Box Model(尺寸) > Typographic(文本相关) > Visual(视觉效果)** 的顺序书写,以提高代码的可读性。 401 | 402 | 解释: 403 | 404 | - Formatting Model 相关属性包括:`position` / `top` / `right` / `bottom` / `left` / `float` / `display` / `overflow` 等 405 | - Box Model 相关属性包括:`border` / `margin` / `padding` / `width` / `height` 等 406 | - Typographic 相关属性包括:`font` / `line-height` / `text-align` / `word-wrap` 等 407 | - Visual 相关属性包括:`background` / `color` / `transition` / `list-style` 等 408 | 409 | 另外,如果包含 `content` 属性,应放在最前面。 410 | 411 | 412 | 示例: 413 | 414 | ```css 415 | .sidebar { 416 | /* formatting model: positioning schemes / offsets / z-indexes / display / ... */ 417 | position: absolute; 418 | top: 50px; 419 | left: 0; 420 | overflow-x: hidden; 421 | 422 | /* box model: sizes / margins / paddings / borders / ... */ 423 | width: 200px; 424 | padding: 5px; 425 | border: 1px solid #ddd; 426 | 427 | /* typographic: font / aligns / text styles / ... */ 428 | font-size: 14px; 429 | line-height: 20px; 430 | 431 | /* visual: colors / shadows / gradients / ... */ 432 | background: #f5f5f5; 433 | color: #333; 434 | -webkit-transition: color 1s; 435 | -moz-transition: color 1s; 436 | transition: color 1s; 437 | } 438 | ``` 439 | 440 | 441 | ### 3.4 清除浮动 442 | 443 | 444 | 445 | #### [建议] 当元素需要撑起高度以包含内部的浮动元素时,通过对伪类设置 `clear` 或触发 `BFC` 的方式进行 `clearfix`。尽量不使用增加空标签的方式。 446 | 447 | 解释: 448 | 449 | 触发 BFC 的方式很多,常见的有: 450 | 451 | * float 非 none 452 | * position 非 static 453 | * overflow 非 visible 454 | 455 | 如希望使用更小副作用的清除浮动方法,参见 [A new micro clearfix hack](http://nicolasgallagher.com/micro-clearfix-hack/) 一文。 456 | 457 | 另需注意,对已经触发 BFC 的元素不需要再进行 clearfix。 458 | 459 | 460 | ### 3.5 !important 461 | 462 | 463 | #### [建议] 尽量不使用 `!important` 声明。 464 | 465 | 466 | #### [建议] 当需要强制指定样式且不允许任何场景覆盖时,通过标签内联和 `!important` 定义样式。 467 | 468 | 解释: 469 | 470 | 必须注意的是,仅在设计上 `确实不允许任何其它场景覆盖样式` 时,才使用内联的 `!important` 样式。通常在第三方环境的应用中使用这种方案。下面的 z-index 章节是其中一个特殊场景的典型样例。 471 | 472 | 473 | 474 | ### 3.6 z-index 475 | 476 | 477 | 478 | #### [建议] 将 `z-index` 进行分层,对文档流外绝对定位元素的视觉层级关系进行管理。 479 | 480 | 解释: 481 | 482 | 同层的多个元素,如多个由用户输入触发的 Dialog,在该层级内使用相同的 `z-index` 或递增 `z-index`。 483 | 484 | 建议每层包含100个 `z-index` 来容纳足够的元素,如果每层元素较多,可以调整这个数值。 485 | 486 | 487 | #### [建议] 在可控环境下,期望显示在最上层的元素,`z-index` 指定为 `999999`。 488 | 489 | 解释: 490 | 491 | 可控环境分成两种,一种是自身产品线环境;还有一种是可能会被其他产品线引用,但是不会被外部第三方的产品引用。 492 | 493 | 不建议取值为 `2147483647`。以便于自身产品线被其他产品线引用时,当遇到层级覆盖冲突的情况,留出向上调整的空间。 494 | 495 | 496 | #### [建议] 在第三方环境下,期望显示在最上层的元素,通过标签内联和 `!important`,将 `z-index` 指定为 `2147483647`。 497 | 498 | 解释: 499 | 500 | 第三方环境对于开发者来说完全不可控。在第三方环境下的元素,为了保证元素不被其页面其他样式定义覆盖,需要采用此做法。 501 | 502 | 503 | 504 | 505 | ## 4 值与单位 506 | 507 | 508 | ### 4.1 文本 509 | 510 | 511 | #### [强制] 文本内容必须用双引号包围。 512 | 513 | 解释: 514 | 515 | 文本类型的内容可能在选择器、属性值等内容中。 516 | 517 | 518 | 示例: 519 | 520 | ```css 521 | /* good */ 522 | html[lang|="zh"] q:before { 523 | font-family: "Microsoft YaHei", sans-serif; 524 | content: "“"; 525 | } 526 | 527 | html[lang|="zh"] q:after { 528 | font-family: "Microsoft YaHei", sans-serif; 529 | content: "”"; 530 | } 531 | 532 | /* bad */ 533 | html[lang|=zh] q:before { 534 | font-family: 'Microsoft YaHei', sans-serif; 535 | content: '“'; 536 | } 537 | 538 | html[lang|=zh] q:after { 539 | font-family: "Microsoft YaHei", sans-serif; 540 | content: "”"; 541 | } 542 | ``` 543 | 544 | ### 4.2 数值 545 | 546 | 547 | #### [强制] 当数值为 0 - 1 之间的小数时,省略整数部分的 `0`。 548 | 549 | 示例: 550 | 551 | ```css 552 | /* good */ 553 | panel { 554 | opacity: .8 555 | } 556 | 557 | /* bad */ 558 | panel { 559 | opacity: 0.8 560 | } 561 | ``` 562 | 563 | ### 4.3 url() 564 | 565 | 566 | #### [强制] `url()` 函数中的路径不加引号。 567 | 568 | 示例: 569 | 570 | ```css 571 | body { 572 | background: url(bg.png); 573 | } 574 | ``` 575 | 576 | 577 | #### [建议] `url()` 函数中的绝对路径可省去协议名。 578 | 579 | 580 | 示例: 581 | 582 | ```css 583 | body { 584 | background: url(//baidu.com/img/bg.png) no-repeat 0 0; 585 | } 586 | ``` 587 | 588 | 589 | ### 4.4 长度 590 | 591 | 592 | #### [强制] 长度为 `0` 时须省略单位。 (也只有长度单位可省) 593 | 594 | 示例: 595 | 596 | ```css 597 | /* good */ 598 | body { 599 | padding: 0 5px; 600 | } 601 | 602 | /* bad */ 603 | body { 604 | padding: 0px 5px; 605 | } 606 | ``` 607 | 608 | 609 | ### 4.5 颜色 610 | 611 | 612 | #### [强制] RGB颜色值必须使用十六进制记号形式 `#rrggbb`。不允许使用 `rgb()`。 613 | 614 | 解释: 615 | 616 | 带有alpha的颜色信息可以使用 `rgba()`。使用 `rgba()` 时每个逗号后必须保留一个空格。 617 | 618 | 619 | 示例: 620 | 621 | ```css 622 | /* good */ 623 | .success { 624 | box-shadow: 0 0 2px rgba(0, 128, 0, .3); 625 | border-color: #008000; 626 | } 627 | 628 | /* bad */ 629 | .success { 630 | box-shadow: 0 0 2px rgba(0,128,0,.3); 631 | border-color: rgb(0, 128, 0); 632 | } 633 | ``` 634 | 635 | #### [强制] 颜色值可以缩写时,必须使用缩写形式。 636 | 637 | 示例: 638 | 639 | ```css 640 | /* good */ 641 | .success { 642 | background-color: #aca; 643 | } 644 | 645 | /* bad */ 646 | .success { 647 | background-color: #aaccaa; 648 | } 649 | ``` 650 | 651 | #### [强制] 颜色值不允许使用命名色值。 652 | 653 | 示例: 654 | 655 | ```css 656 | /* good */ 657 | .success { 658 | color: #90ee90; 659 | } 660 | 661 | /* bad */ 662 | .success { 663 | color: lightgreen; 664 | } 665 | ``` 666 | 667 | #### [建议] 颜色值中的英文字符采用小写。如不用小写也需要保证同一项目内保持大小写一致。 668 | 669 | 670 | 示例: 671 | 672 | ```css 673 | /* good */ 674 | .success { 675 | background-color: #aca; 676 | color: #90ee90; 677 | } 678 | 679 | /* good */ 680 | .success { 681 | background-color: #ACA; 682 | color: #90EE90; 683 | } 684 | 685 | /* bad */ 686 | .success { 687 | background-color: #ACA; 688 | color: #90ee90; 689 | } 690 | ``` 691 | 692 | 693 | ### 4.6 2D 位置 694 | 695 | 696 | #### [强制] 必须同时给出水平和垂直方向的位置。 697 | 698 | 解释: 699 | 700 | 2D 位置初始值为 `0% 0%`,但在只有一个方向的值时,另一个方向的值会被解析为 center。为避免理解上的困扰,应同时给出两个方向的值。[background-position属性值的定义](http://www.w3.org/TR/CSS21/colors.html#propdef-background-position) 701 | 702 | 703 | 示例: 704 | 705 | ```css 706 | /* good */ 707 | body { 708 | background-position: center top; /* 50% 0% */ 709 | } 710 | 711 | /* bad */ 712 | body { 713 | background-position: top; /* 50% 0% */ 714 | } 715 | ``` 716 | 717 | 718 | 719 | 720 | 721 | ## 5 文本编排 722 | 723 | 724 | ### 5.1 字体族 725 | 726 | 727 | #### [强制] `font-family` 属性中的字体族名称应使用字体的英文 `Family Name`,其中如有空格,须放置在引号中。 728 | 729 | 解释: 730 | 731 | 所谓英文 Family Name,为字体文件的一个元数据,常见名称如下: 732 | 733 | 字体 | 操作系统 | Family Name 734 | -----|----------|------------ 735 | 宋体 (中易宋体) | Windows | SimSun 736 | 黑体 (中易黑体) | Windows | SimHei 737 | 微软雅黑 | Windows | Microsoft YaHei 738 | 微软正黑 | Windows | Microsoft JhengHei 739 | 华文黑体 | Mac/iOS | STHeiti 740 | 冬青黑体 | Mac/iOS | Hiragino Sans GB 741 | 文泉驿正黑 | Linux | WenQuanYi Zen Hei 742 | 文泉驿微米黑 | Linux | WenQuanYi Micro Hei 743 | 744 | 745 | 示例: 746 | 747 | ```css 748 | h1 { 749 | font-family: "Microsoft YaHei"; 750 | } 751 | ``` 752 | 753 | 754 | #### [强制] `font-family` 按「西文字体在前、中文字体在后」、「效果佳 (质量高/更能满足需求) 的字体在前、效果一般的字体在后」的顺序编写,最后必须指定一个通用字体族( `serif` / `sans-serif` )。 755 | 756 | 解释: 757 | 758 | 更详细说明可参考[本文](http://www.zhihu.com/question/19911793/answer/13329819)。 759 | 760 | 示例: 761 | 762 | ```css 763 | /* Display according to platform */ 764 | .article { 765 | font-family: Arial, sans-serif; 766 | } 767 | 768 | /* Specific for most platforms */ 769 | h1 { 770 | font-family: "Helvetica Neue", Arial, "Hiragino Sans GB", "WenQuanYi Micro Hei", "Microsoft YaHei", sans-serif; 771 | } 772 | ``` 773 | 774 | #### [强制] `font-family` 不区分大小写,但在同一个项目中,同样的 `Family Name` 大小写必须统一。 775 | 776 | 示例: 777 | 778 | ```css 779 | /* good */ 780 | body { 781 | font-family: Arial, sans-serif; 782 | } 783 | 784 | h1 { 785 | font-family: Arial, "Microsoft YaHei", sans-serif; 786 | } 787 | 788 | /* bad */ 789 | body { 790 | font-family: arial, sans-serif; 791 | } 792 | 793 | h1 { 794 | font-family: Arial, "Microsoft YaHei", sans-serif; 795 | } 796 | ``` 797 | 798 | ### 5.2 字号 799 | 800 | 801 | #### [强制] 需要在 Windows 平台显示的中文内容,其字号应不小于 `12px`。 802 | 803 | 解释: 804 | 805 | 由于 Windows 的字体渲染机制,小于 12px 的文字显示效果极差、难以辨认。 806 | 807 | 808 | ### 5.3 字体风格 809 | 810 | 811 | #### [建议] 需要在 Windows 平台显示的中文内容,不要使用除 `normal` 外的 `font-style`。其他平台也应慎用。 812 | 813 | 解释: 814 | 815 | 由于中文字体没有 italic 风格的实现,所有浏览器下都会 fallback 到 obilique 实现 (自动拟合为斜体),小字号下 (特别是 Windows 下会在小字号下使用点阵字体的情况下) 显示效果差,造成阅读困难。 816 | 817 | 818 | ### 5.4 字重 819 | 820 | 821 | #### [强制] `font-weight` 属性必须使用数值方式描述。 822 | 823 | 解释: 824 | 825 | CSS 的字重分 100 – 900 共九档,但目前受字体本身质量和浏览器的限制,实际上支持 400 和 700 两档,分别等价于关键词 normal 和 bold。 826 | 827 | 浏览器本身使用一系列[启发式规则](http://www.w3.org/TR/CSS21/fonts.html#propdef-font-weight)来进行匹配,在 <700 时一般匹配字体的 Regular 字重,>=700 时匹配 Bold 字重。 828 | 829 | 但已有浏览器开始支持 =600 时匹配 Semibold 字重 (见[此表](http://justineo.github.io/slideshows/font/#/3/15)),故使用数值描述增加了灵活性,也更简短。 830 | 831 | 示例: 832 | 833 | ```css 834 | /* good */ 835 | h1 { 836 | font-weight: 700; 837 | } 838 | 839 | /* bad */ 840 | h1 { 841 | font-weight: bold; 842 | } 843 | ``` 844 | 845 | ### 5.5 行高 846 | 847 | 848 | #### [建议] `line-height` 在定义文本段落时,应使用数值。 849 | 850 | 解释: 851 | 852 | 将 line-height 设置为数值,浏览器会基于当前元素设置的 font-size 进行再次计算。在不同字号的文本段落组合中,能达到较为舒适的行间间隔效果,避免在每个设置了 font-size 都需要设置 line-height。 853 | 854 | 当 line-height 用于控制垂直居中时,还是应该设置成与容器高度一致。 855 | 856 | 857 | 示例: 858 | 859 | ```css 860 | .container { 861 | line-height: 1.5; 862 | } 863 | ``` 864 | 865 | 866 | 867 | ## 6 变换与动画 868 | 869 | 870 | 871 | #### [强制] 使用 `transition` 时应指定 `transition-property`。 872 | 873 | 示例: 874 | 875 | ```css 876 | /* good */ 877 | .box { 878 | transition: color 1s, border-color 1s; 879 | } 880 | 881 | /* bad */ 882 | .box { 883 | transition: all 1s; 884 | } 885 | ``` 886 | 887 | #### [建议] 尽可能在浏览器能高效实现的属性上添加过渡和动画。 888 | 889 | 解释: 890 | 891 | 见[本文](http://www.html5rocks.com/en/tutorials/speed/high-performance-animations/),在可能的情况下应选择这样四种变换: 892 | 893 | * `transform: translate(npx, npx);` 894 | * `transform: scale(n);` 895 | * `transform: rotate(ndeg);` 896 | * `opacity: 0..1;` 897 | 898 | 典型的,可以使用 translate 来代替 left 作为动画属性。 899 | 900 | 示例: 901 | 902 | ```css 903 | /* good */ 904 | .box { 905 | transition: transform 1s; 906 | } 907 | .box:hover { 908 | transform: translate(20px); /* move right for 20px */ 909 | } 910 | 911 | /* bad */ 912 | .box { 913 | left: 0; 914 | transition: left 1s; 915 | } 916 | .box:hover { 917 | left: 20px; /* move right for 20px */ 918 | } 919 | ``` 920 | 921 | 922 | 923 | 924 | ## 7 响应式 925 | 926 | 927 | 928 | #### [强制] `Media Query` 不得单独编排,必须与相关的规则一起定义。 929 | 930 | 示例: 931 | 932 | ```css 933 | /* Good */ 934 | /* header styles */ 935 | @media (...) { 936 | /* header styles */ 937 | } 938 | 939 | /* main styles */ 940 | @media (...) { 941 | /* main styles */ 942 | } 943 | 944 | /* footer styles */ 945 | @media (...) { 946 | /* footer styles */ 947 | } 948 | 949 | 950 | /* Bad */ 951 | /* header styles */ 952 | /* main styles */ 953 | /* footer styles */ 954 | 955 | @media (...) { 956 | /* header styles */ 957 | /* main styles */ 958 | /* footer styles */ 959 | } 960 | ``` 961 | 962 | #### [强制] `Media Query` 如果有多个逗号分隔的条件时,应将每个条件放在单独一行中。 963 | 964 | 示例: 965 | 966 | ```css 967 | @media 968 | (-webkit-min-device-pixel-ratio: 2), /* Webkit-based browsers */ 969 | (min--moz-device-pixel-ratio: 2), /* Older Firefox browsers (prior to Firefox 16) */ 970 | (min-resolution: 2dppx), /* The standard way */ 971 | (min-resolution: 192dpi) { /* dppx fallback */ 972 | /* Retina-specific stuff here */ 973 | } 974 | ``` 975 | 976 | #### [建议] 尽可能给出在高分辨率设备 (Retina) 下效果更佳的样式。 977 | 978 | 979 | 980 | ## 8 兼容性 981 | 982 | 983 | ### 8.1 属性前缀 984 | 985 | 986 | #### [强制] 带私有前缀的属性由长到短排列,按冒号位置对齐。 987 | 988 | 解释: 989 | 990 | 标准属性放在最后,按冒号对齐方便阅读,也便于在编辑器内进行多行编辑。 991 | 992 | 993 | 示例: 994 | 995 | ```css 996 | .box { 997 | -webkit-box-sizing: border-box; 998 | -moz-box-sizing: border-box; 999 | box-sizing: border-box; 1000 | } 1001 | ``` 1002 | 1003 | 1004 | ### 8.2 Hack 1005 | 1006 | 1007 | #### [建议] 需要添加 `hack` 时应尽可能考虑是否可以采用其他方式解决。 1008 | 1009 | 解释: 1010 | 1011 | 如果能通过合理的 HTML 结构或使用其他的 CSS 定义达到理想的样式,则不应该使用 hack 手段解决问题。通常 hack 会导致维护成本的增加。 1012 | 1013 | #### [建议] 尽量使用 `选择器 hack` 处理兼容性,而非 `属性 hack`。 1014 | 1015 | 解释: 1016 | 1017 | 尽量使用符合 CSS 语法的 selector hack,可以避免一些第三方库无法识别 hack 语法的问题。 1018 | 1019 | 1020 | 示例: 1021 | 1022 | ```css 1023 | /* IE 7 */ 1024 | *:first-child + html #header { 1025 | margin-top: 3px; 1026 | padding: 5px; 1027 | } 1028 | 1029 | /* IE 6 */ 1030 | * html #header { 1031 | margin-top: 5px; 1032 | padding: 4px; 1033 | } 1034 | ``` 1035 | 1036 | 1037 | #### [建议] 尽量使用简单的 `属性 hack`。 1038 | 1039 | 示例: 1040 | 1041 | ```css 1042 | .box { 1043 | _display: inline; /* fix double margin */ 1044 | float: left; 1045 | margin-left: 20px; 1046 | } 1047 | 1048 | .container { 1049 | overflow: hidden; 1050 | *zoom: 1; /* triggering hasLayout */ 1051 | } 1052 | ``` 1053 | 1054 | ### 8.3 Expression 1055 | 1056 | 1057 | #### [强制] 禁止使用 `Expression`。 1058 | 1059 | 1060 | 1061 | 1062 | 1063 | -------------------------------------------------------------------------------- /javascript.md: -------------------------------------------------------------------------------- 1 | 2 | # JavaScript编码规范 3 | 4 | 5 | 6 | 7 | [1 前言](#1-%E5%89%8D%E8%A8%80) 8 | 9 | [2 代码风格](#2-%E4%BB%A3%E7%A0%81%E9%A3%8E%E6%A0%BC) 10 | 11 |   [2.1 文件](#21-%E6%96%87%E4%BB%B6) 12 | 13 |   [2.2 结构](#22-%E7%BB%93%E6%9E%84) 14 | 15 |     [2.2.1 缩进](#221-%E7%BC%A9%E8%BF%9B) 16 | 17 |     [2.2.2 空格](#222-%E7%A9%BA%E6%A0%BC) 18 | 19 |     [2.2.3 换行](#223-%E6%8D%A2%E8%A1%8C) 20 | 21 |     [2.2.4 语句](#224-%E8%AF%AD%E5%8F%A5) 22 | 23 |   [2.3 命名](#23-%E5%91%BD%E5%90%8D) 24 | 25 |   [2.4 注释](#24-%E6%B3%A8%E9%87%8A) 26 | 27 |     [2.4.1 单行注释](#241-%E5%8D%95%E8%A1%8C%E6%B3%A8%E9%87%8A) 28 | 29 |     [2.4.2 多行注释](#242-%E5%A4%9A%E8%A1%8C%E6%B3%A8%E9%87%8A) 30 | 31 |     [2.4.3 文档化注释](#243-%E6%96%87%E6%A1%A3%E5%8C%96%E6%B3%A8%E9%87%8A) 32 | 33 |     [2.4.4 类型定义](#244-%E7%B1%BB%E5%9E%8B%E5%AE%9A%E4%B9%89) 34 | 35 |     [2.4.5 文件注释](#245-%E6%96%87%E4%BB%B6%E6%B3%A8%E9%87%8A) 36 | 37 |     [2.4.6 命名空间注释](#246-%E5%91%BD%E5%90%8D%E7%A9%BA%E9%97%B4%E6%B3%A8%E9%87%8A) 38 | 39 |     [2.4.7 类注释](#247-%E7%B1%BB%E6%B3%A8%E9%87%8A) 40 | 41 |     [2.4.8 函数/方法注释](#248-%E5%87%BD%E6%95%B0/%E6%96%B9%E6%B3%95%E6%B3%A8%E9%87%8A) 42 | 43 |     [2.4.9 事件注释](#249-%E4%BA%8B%E4%BB%B6%E6%B3%A8%E9%87%8A) 44 | 45 |     [2.4.10 常量注释](#2410-%E5%B8%B8%E9%87%8F%E6%B3%A8%E9%87%8A) 46 | 47 |     [2.4.11 复杂类型注释](#2411-%E5%A4%8D%E6%9D%82%E7%B1%BB%E5%9E%8B%E6%B3%A8%E9%87%8A) 48 | 49 |     [2.4.12 AMD 模块注释](#2412-amd-%E6%A8%A1%E5%9D%97%E6%B3%A8%E9%87%8A) 50 | 51 |     [2.4.13 细节注释](#2413-%E7%BB%86%E8%8A%82%E6%B3%A8%E9%87%8A) 52 | 53 | [3 语言特性](#3-%E8%AF%AD%E8%A8%80%E7%89%B9%E6%80%A7) 54 | 55 |   [3.1 变量](#31-%E5%8F%98%E9%87%8F) 56 | 57 |   [3.2 条件](#32-%E6%9D%A1%E4%BB%B6) 58 | 59 |   [3.3 循环](#33-%E5%BE%AA%E7%8E%AF) 60 | 61 |   [3.4 类型](#34-%E7%B1%BB%E5%9E%8B) 62 | 63 |     [3.4.1 类型检测](#341-%E7%B1%BB%E5%9E%8B%E6%A3%80%E6%B5%8B) 64 | 65 |     [3.4.2 类型转换](#342-%E7%B1%BB%E5%9E%8B%E8%BD%AC%E6%8D%A2) 66 | 67 |   [3.5 字符串](#35-%E5%AD%97%E7%AC%A6%E4%B8%B2) 68 | 69 |   [3.6 对象](#36-%E5%AF%B9%E8%B1%A1) 70 | 71 |   [3.7 数组](#37-%E6%95%B0%E7%BB%84) 72 | 73 |   [3.8 函数](#38-%E5%87%BD%E6%95%B0) 74 | 75 |     [3.8.1 函数长度](#381-%E5%87%BD%E6%95%B0%E9%95%BF%E5%BA%A6) 76 | 77 |     [3.8.2 参数设计](#382-%E5%8F%82%E6%95%B0%E8%AE%BE%E8%AE%A1) 78 | 79 |     [3.8.3 闭包](#383-%E9%97%AD%E5%8C%85) 80 | 81 |     [3.8.4 空函数](#384-%E7%A9%BA%E5%87%BD%E6%95%B0) 82 | 83 |   [3.9 面向对象](#39-%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1) 84 | 85 |   [3.10 动态特性](#310-%E5%8A%A8%E6%80%81%E7%89%B9%E6%80%A7) 86 | 87 |     [3.10.1 eval](#3101-eval) 88 | 89 |     [3.10.2 动态执行代码](#3102-%E5%8A%A8%E6%80%81%E6%89%A7%E8%A1%8C%E4%BB%A3%E7%A0%81) 90 | 91 |     [3.10.3 with](#3103-with) 92 | 93 |     [3.10.4 delete](#3104-delete) 94 | 95 |     [3.10.5 对象属性](#3105-%E5%AF%B9%E8%B1%A1%E5%B1%9E%E6%80%A7) 96 | 97 | [4 浏览器环境](#4-%E6%B5%8F%E8%A7%88%E5%99%A8%E7%8E%AF%E5%A2%83) 98 | 99 |   [4.1 模块化](#41-%E6%A8%A1%E5%9D%97%E5%8C%96) 100 | 101 |     [4.1.1 AMD](#411-amd) 102 | 103 |     [4.1.2 define](#412-define) 104 | 105 |     [4.1.3 require](#413-require) 106 | 107 |   [4.2 DOM](#42-dom) 108 | 109 |     [4.2.1 元素获取](#421-%E5%85%83%E7%B4%A0%E8%8E%B7%E5%8F%96) 110 | 111 |     [4.2.2 样式获取](#422-%E6%A0%B7%E5%BC%8F%E8%8E%B7%E5%8F%96) 112 | 113 |     [4.2.3 样式设置](#423-%E6%A0%B7%E5%BC%8F%E8%AE%BE%E7%BD%AE) 114 | 115 |     [4.2.4 DOM 操作](#424-dom-%E6%93%8D%E4%BD%9C) 116 | 117 |     [4.2.5 DOM 事件](#425-dom-%E4%BA%8B%E4%BB%B6) 118 | 119 | 120 | 121 | 122 | 123 | ## 1 前言 124 | 125 | 126 | JavaScript在百度一直有着广泛的应用,特别是在浏览器端的行为管理。本文档的目标是使JavaScript代码风格保持一致,容易被理解和被维护。 127 | 128 | 虽然本文档是针对JavaScript设计的,但是在使用各种JavaScript的预编译语言时(如TypeScript等)时,适用的部分也应尽量遵循本文档的约定。 129 | 130 | 131 | 132 | ## 2 代码风格 133 | 134 | 135 | 136 | 137 | 138 | 139 | ### 2.1 文件 140 | 141 | 142 | ##### [建议] `JavaScript` 文件使用无 `BOM` 的 `UTF-8` 编码。 143 | 144 | 解释: 145 | 146 | UTF-8 编码具有更广泛的适应性。BOM 在使用程序或工具处理文件时可能造成不必要的干扰。 147 | 148 | ##### [建议] 在文件结尾处,保留一个空行。 149 | 150 | 151 | 152 | 153 | ### 2.2 结构 154 | 155 | 156 | 157 | #### 2.2.1 缩进 158 | 159 | 160 | ##### [强制] 使用 `4` 个空格做为一个缩进层级,不允许使用 `2` 个空格 或 `tab` 字符。 161 | 162 | 163 | 164 | ##### [强制] `switch` 下的 `case` 和 `default` 必须增加一个缩进层级。 165 | 166 | 示例: 167 | 168 | ```javascript 169 | // good 170 | switch (variable) { 171 | 172 | case '1': 173 | // do... 174 | break; 175 | 176 | case '2': 177 | // do... 178 | break; 179 | 180 | default: 181 | // do... 182 | 183 | } 184 | 185 | // bad 186 | switch (variable) { 187 | 188 | case '1': 189 | // do... 190 | break; 191 | 192 | case '2': 193 | // do... 194 | break; 195 | 196 | default: 197 | // do... 198 | 199 | } 200 | ``` 201 | 202 | #### 2.2.2 空格 203 | 204 | 205 | 206 | ##### [强制] 二元运算符两侧必须有一个空格,一元运算符与操作对象之间不允许有空格。 207 | 208 | 示例: 209 | 210 | ```javascript 211 | var a = !arr.length; 212 | a++; 213 | a = b + c; 214 | ``` 215 | 216 | ##### [强制] 用作代码块起始的左花括号 `{` 前必须有一个空格。 217 | 218 | 示例: 219 | 220 | ```javascript 221 | // good 222 | if (condition) { 223 | } 224 | 225 | while (condition) { 226 | } 227 | 228 | function funcName() { 229 | } 230 | 231 | // bad 232 | if (condition){ 233 | } 234 | 235 | while (condition){ 236 | } 237 | 238 | function funcName(){ 239 | } 240 | ``` 241 | 242 | ##### [强制] `if / else / for / while / function / switch / do / try / catch / finally` 关键字后,必须有一个空格。 243 | 244 | 示例: 245 | 246 | ```javascript 247 | // good 248 | if (condition) { 249 | } 250 | 251 | while (condition) { 252 | } 253 | 254 | (function () { 255 | })(); 256 | 257 | // bad 258 | if(condition) { 259 | } 260 | 261 | while(condition) { 262 | } 263 | 264 | (function() { 265 | })(); 266 | ``` 267 | 268 | ##### [强制] 在对象创建时,属性中的 `:` 之后必须有空格,`:` 之前不允许有空格。 269 | 270 | 示例: 271 | 272 | ```javascript 273 | // good 274 | var obj = { 275 | a: 1, 276 | b: 2, 277 | c: 3 278 | }; 279 | 280 | // bad 281 | var obj = { 282 | a : 1, 283 | b:2, 284 | c :3 285 | }; 286 | ``` 287 | 288 | ##### [强制] 函数声明、具名函数表达式、函数调用中,函数名和 `(` 之间不允许有空格。 289 | 290 | 示例: 291 | 292 | ```javascript 293 | // good 294 | function funcName() { 295 | } 296 | 297 | var funcName = function funcName() { 298 | }; 299 | 300 | funcName(); 301 | 302 | // bad 303 | function funcName () { 304 | } 305 | 306 | var funcName = function funcName () { 307 | }; 308 | 309 | funcName (); 310 | ``` 311 | 312 | ##### [强制] `,` 和 `;` 前不允许有空格。 313 | 314 | 示例: 315 | 316 | ```javascript 317 | // good 318 | callFunc(a, b); 319 | 320 | // bad 321 | callFunc(a , b) ; 322 | ``` 323 | 324 | ##### [强制] 在函数调用、函数声明、括号表达式、属性访问、`if / for / while / switch / catch` 等语句中,`()` 和 `[]` 内紧贴括号部分不允许有空格。 325 | 326 | 示例: 327 | 328 | ```javascript 329 | // good 330 | 331 | callFunc(param1, param2, param3); 332 | 333 | save(this.list[this.indexes[i]]); 334 | 335 | needIncream && (variable += increament); 336 | 337 | if (num > list.length) { 338 | } 339 | 340 | while (len--) { 341 | } 342 | 343 | 344 | // bad 345 | 346 | callFunc( param1, param2, param3 ); 347 | 348 | save( this.list[ this.indexes[ i ] ] ); 349 | 350 | needIncreament && ( variable += increament ); 351 | 352 | if ( num > list.length ) { 353 | } 354 | 355 | while ( len-- ) { 356 | } 357 | ``` 358 | 359 | ##### [强制] 单行声明的数组与对象,如果包含元素,`{}` 和 `[]` 内紧贴括号部分不允许包含空格。 360 | 361 | 解释: 362 | 363 | 声明包含元素的数组与对象,只有当内部元素的形式较为简单时,才允许写在一行。元素复杂的情况,还是应该换行书写。 364 | 365 | 366 | 示例: 367 | 368 | ```javascript 369 | // good 370 | var arr1 = []; 371 | var arr2 = [1, 2, 3]; 372 | var obj1 = {}; 373 | var obj2 = {name: 'obj'}; 374 | var obj3 = { 375 | name: 'obj', 376 | age: 20, 377 | sex: 1 378 | }; 379 | 380 | // bad 381 | var arr1 = [ ]; 382 | var arr2 = [ 1, 2, 3 ]; 383 | var obj1 = { }; 384 | var obj2 = { name: 'obj' }; 385 | var obj3 = {name: 'obj', age: 20, sex: 1}; 386 | ``` 387 | 388 | ##### [强制] 行尾不得有多余的空格。 389 | 390 | 391 | #### 2.2.3 换行 392 | 393 | 394 | ##### [强制] 每个独立语句结束后必须换行。 395 | 396 | ##### [强制] 每行不得超过 `120` 个字符。 397 | 398 | 解释: 399 | 400 | 超长的不可分割的代码允许例外,比如复杂的正则表达式。长字符串不在例外之列。 401 | 402 | 403 | ##### [强制] 运算符处换行时,运算符必须在新行的行首。 404 | 405 | 示例: 406 | 407 | ```javascript 408 | // good 409 | if (user.isAuthenticated() 410 | && user.isInRole('admin') 411 | && user.hasAuthority('add-admin') 412 | || user.hasAuthority('delete-admin') 413 | ) { 414 | // Code 415 | } 416 | 417 | var result = number1 + number2 + number3 418 | + number4 + number5; 419 | 420 | 421 | // bad 422 | if (user.isAuthenticated() && 423 | user.isInRole('admin') && 424 | user.hasAuthority('add-admin') || 425 | user.hasAuthority('delete-admin')) { 426 | // Code 427 | } 428 | 429 | var result = number1 + number2 + number3 + 430 | number4 + number5; 431 | ``` 432 | 433 | ##### [强制] 在函数声明、函数表达式、函数调用、对象创建、数组创建、for语句等场景中,不允许在 `,` 或 `;` 前换行。 434 | 435 | 示例: 436 | 437 | ```javascript 438 | // good 439 | var obj = { 440 | a: 1, 441 | b: 2, 442 | c: 3 443 | }; 444 | 445 | foo( 446 | aVeryVeryLongArgument, 447 | anotherVeryLongArgument, 448 | callback 449 | ); 450 | 451 | 452 | // bad 453 | var obj = { 454 | a: 1 455 | , b: 2 456 | , c: 3 457 | }; 458 | 459 | foo( 460 | aVeryVeryLongArgument 461 | , anotherVeryLongArgument 462 | , callback 463 | ); 464 | ``` 465 | 466 | ##### [建议] 不同行为或逻辑的语句集,使用空行隔开,更易阅读。 467 | 468 | 示例: 469 | 470 | ```javascript 471 | // 仅为按逻辑换行的示例,不代表setStyle的最优实现 472 | function setStyle(element, property, value) { 473 | if (element == null) { 474 | return; 475 | } 476 | 477 | element.style[property] = value; 478 | } 479 | ``` 480 | 481 | ##### [建议] 在语句的行长度超过 `120` 时,根据逻辑条件合理缩进。 482 | 483 | 示例: 484 | 485 | ```javascript 486 | // 较复杂的逻辑条件组合,将每个条件独立一行,逻辑运算符放置在行首进行分隔,或将部分逻辑按逻辑组合进行分隔。 487 | // 建议最终将右括号 ) 与左大括号 { 放在独立一行,保证与 if 内语句块能容易视觉辨识。 488 | if (user.isAuthenticated() 489 | && user.isInRole('admin') 490 | && user.hasAuthority('add-admin') 491 | || user.hasAuthority('delete-admin') 492 | ) { 493 | // Code 494 | } 495 | 496 | // 按一定长度截断字符串,并使用 + 运算符进行连接。 497 | // 分隔字符串尽量按语义进行,如不要在一个完整的名词中间断开。 498 | // 特别的,对于HTML片段的拼接,通过缩进,保持和HTML相同的结构。 499 | var html = '' // 此处用一个空字符串,以便整个HTML片段都在新行严格对齐 500 | + '
    ' 501 | + '

    Title here

    ' 502 | + '

    This is a paragraph

    ' 503 | + '
    Complete
    ' 504 | + '
    '; 505 | 506 | // 也可使用数组来进行拼接,相对 + 更容易调整缩进。 507 | var html = [ 508 | '
    ', 509 | '

    Title here

    ', 510 | '

    This is a paragraph

    ', 511 | '
    Complete
    ', 512 | '
    ' 513 | ]; 514 | html = html.join(''); 515 | 516 | // 当参数过多时,将每个参数独立写在一行上,并将结束的右括号 ) 独立一行。 517 | // 所有参数必须增加一个缩进。 518 | foo( 519 | aVeryVeryLongArgument, 520 | anotherVeryLongArgument, 521 | callback 522 | ); 523 | 524 | // 也可以按逻辑对参数进行组合。 525 | // 最经典的是baidu.format函数,调用时将参数分为“模板”和“数据”两块 526 | baidu.format( 527 | dateFormatTemplate, 528 | year, month, date, hour, minute, second 529 | ); 530 | 531 | // 当函数调用时,如果有一个或以上参数跨越多行,应当每一个参数独立一行。 532 | // 这通常出现在匿名函数或者对象初始化等作为参数时,如setTimeout函数等。 533 | setTimeout( 534 | function () { 535 | alert('hello'); 536 | }, 537 | 200 538 | ); 539 | 540 | order.data.read( 541 | 'id=' + me.model.id, 542 | function (data) { 543 | me.attchToModel(data.result); 544 | callback(); 545 | }, 546 | 300 547 | ); 548 | 549 | // 链式调用较长时采用缩进进行调整。 550 | $('#items') 551 | .find('.selected') 552 | .highlight() 553 | .end(); 554 | 555 | // 三元运算符由3部分组成,因此其换行应当根据每个部分的长度不同,形成不同的情况。 556 | var result = thisIsAVeryVeryLongCondition 557 | ? resultA : resultB; 558 | 559 | var result = condition 560 | ? thisIsAVeryVeryLongResult 561 | : resultB; 562 | 563 | // 数组和对象初始化的混用,严格按照每个对象的 { 和结束 } 在独立一行的风格书写。 564 | var array = [ 565 | { 566 | // ... 567 | }, 568 | { 569 | // ... 570 | } 571 | ]; 572 | ``` 573 | 574 | ##### [建议] 对于 `if...else...`、`try...catch...finally` 等语句,推荐使用在 `}` 号后添加一个换行 的风格,使代码层次结构更清晰,阅读性更好。 575 | 576 | 示例: 577 | 578 | ```javascript 579 | if (condition) { 580 | // some statements; 581 | } 582 | else { 583 | // some statements; 584 | } 585 | 586 | try { 587 | // some statements; 588 | } 589 | catch (ex) { 590 | // some statements; 591 | } 592 | ``` 593 | 594 | 595 | 596 | #### 2.2.4 语句 597 | 598 | 599 | ##### [强制] 不得省略语句结束的分号。 600 | 601 | ##### [强制] 在 `if / else / for / do / while` 语句中,即使只有一行,也不得省略块 `{...}`。 602 | 603 | 示例: 604 | 605 | ```javascript 606 | // good 607 | if (condition) { 608 | callFunc(); 609 | } 610 | 611 | // bad 612 | if (condition) callFunc(); 613 | if (condition) 614 | callFunc(); 615 | ``` 616 | 617 | ##### [强制] 函数定义结束不允许添加分号。 618 | 619 | 示例: 620 | 621 | ```javascript 622 | // good 623 | function funcName() { 624 | } 625 | 626 | // bad 627 | function funcName() { 628 | }; 629 | 630 | // 如果是函数表达式,分号是不允许省略的。 631 | var funcName = function () { 632 | }; 633 | ``` 634 | 635 | ##### [强制] `IIFE` 必须在函数表达式外添加 `(`,非 `IIFE` 不得在函数表达式外添加 `(`。 636 | 637 | 解释: 638 | 639 | IIFE = Immediately-Invoked Function Expression. 640 | 641 | 额外的 ( 能够让代码在阅读的一开始就能判断函数是否立即被调用,进而明白接下来代码的用途。而不是一直拖到底部才恍然大悟。 642 | 643 | 644 | 示例: 645 | 646 | ```javascript 647 | // good 648 | var task = (function () { 649 | // Code 650 | return result; 651 | })(); 652 | 653 | var func = function () { 654 | }; 655 | 656 | 657 | // bad 658 | var task = function () { 659 | // Code 660 | return result; 661 | }(); 662 | 663 | var func = (function () { 664 | }); 665 | ``` 666 | 667 | 668 | 669 | 670 | 671 | ### 2.3 命名 672 | 673 | 674 | ##### [强制] `变量` 使用 `Camel命名法`。 675 | 676 | 示例: 677 | 678 | ```javascript 679 | var loadingModules = {}; 680 | ``` 681 | 682 | ##### [强制] `常量` 使用 `全部字母大写,单词间下划线分隔` 的命名方式。 683 | 684 | 示例: 685 | 686 | ```javascript 687 | var HTML_ENTITY = {}; 688 | ``` 689 | 690 | ##### [强制] `函数` 使用 `Camel命名法`。 691 | 692 | 示例: 693 | 694 | ```javascript 695 | function stringFormat(source) { 696 | } 697 | ``` 698 | 699 | ##### [强制] 函数的 `参数` 使用 `Camel命名法`。 700 | 701 | 示例: 702 | 703 | ```javascript 704 | function hear(theBells) { 705 | } 706 | ``` 707 | 708 | 709 | ##### [强制] `类` 使用 `Pascal命名法`。 710 | 711 | 示例: 712 | 713 | ```javascript 714 | function TextNode(options) { 715 | } 716 | ``` 717 | 718 | ##### [强制] 类的 `方法 / 属性` 使用 `Camel命名法`。 719 | 720 | 示例: 721 | 722 | ```javascript 723 | function TextNode(value, engine) { 724 | this.value = value; 725 | this.engine = engine; 726 | } 727 | 728 | TextNode.prototype.clone = function () { 729 | return this; 730 | }; 731 | ``` 732 | 733 | ##### [强制] `枚举变量` 使用 `Pascal命名法`,`枚举的属性` 使用 `全部字母大写,单词间下划线分隔` 的命名方式。 734 | 735 | 示例: 736 | 737 | ```javascript 738 | var TargetState = { 739 | READING: 1, 740 | READED: 2, 741 | APPLIED: 3, 742 | READY: 4 743 | }; 744 | ``` 745 | 746 | ##### [强制] `命名空间` 使用 `Camel命名法`。 747 | 748 | 示例: 749 | 750 | ```javascript 751 | equipments.heavyWeapons = {}; 752 | ``` 753 | 754 | ##### [强制] 由多个单词组成的缩写词,在命名中,根据当前命名法和出现的位置,所有字母的大小写与首字母的大小写保持一致。 755 | 756 | 示例: 757 | 758 | ```javascript 759 | function XMLParser() { 760 | } 761 | 762 | function insertHTML(element, html) { 763 | } 764 | 765 | var httpRequest = new HTTPRequest(); 766 | ``` 767 | 768 | ##### [强制] `类名` 使用 `名词`。 769 | 770 | 示例: 771 | 772 | ```javascript 773 | function Engine(options) { 774 | } 775 | ``` 776 | 777 | ##### [建议] `函数名` 使用 `动宾短语`。 778 | 779 | 示例: 780 | 781 | ```javascript 782 | function getStyle(element) { 783 | } 784 | ``` 785 | 786 | ##### [建议] `boolean` 类型的变量使用 `is` 或 `has` 开头。 787 | 788 | 示例: 789 | 790 | ```javascript 791 | var isReady = false; 792 | var hasMoreCommands = false; 793 | ``` 794 | 795 | ##### [建议] `Promise对象` 用 `动宾短语的进行时` 表达。 796 | 797 | 示例: 798 | 799 | ```javascript 800 | var loadingData = ajax.get('url'); 801 | loadingData.then(callback); 802 | ``` 803 | 804 | 805 | 806 | 807 | ### 2.4 注释 808 | 809 | 810 | #### 2.4.1 单行注释 811 | 812 | 813 | ##### [强制] 必须独占一行。`//` 后跟一个空格,缩进与下一行被注释说明的代码一致。 814 | 815 | #### 2.4.2 多行注释 816 | 817 | 818 | ##### [建议] 避免使用 `/*...*/` 这样的多行注释。有多行注释内容时,使用多个单行注释。 819 | 820 | 821 | #### 2.4.3 文档化注释 822 | 823 | 824 | ##### [强制] 为了便于代码阅读和自文档化,以下内容必须包含以 `/**...*/` 形式的块注释中。 825 | 826 | 解释: 827 | 828 | 1. 文件 829 | 2. namespace 830 | 3. 类 831 | 4. 函数或方法 832 | 5. 类属性 833 | 6. 事件 834 | 7. 全局变量 835 | 8. 常量 836 | 9. AMD 模块 837 | 838 | 839 | ##### [强制] 文档注释前必须空一行。 840 | 841 | 842 | ##### [建议] 自文档化的文档说明 what,而不是 how。 843 | 844 | 845 | 846 | #### 2.4.4 类型定义 847 | 848 | 849 | ##### [强制] 类型定义都是以`{`开始, 以`}`结束。 850 | 851 | 解释: 852 | 853 | 常用类型如:{string}, {number}, {boolean}, {Object}, {Function}, {RegExp}, {Array}, {Date}。 854 | 855 | 类型不仅局限于内置的类型,也可以是自定义的类型。比如定义了一个类 Developer,就可以使用它来定义一个参数和返回值的类型。 856 | 857 | 858 | ##### [强制] 对于基本类型 {string}, {number}, {boolean},首字母必须小写。 859 | 860 | | 类型定义 | 语法示例 | 解释 | 861 | | ------- | ------- | --- | 862 | |String|{string}|--| 863 | |Number|{number}|--| 864 | |Boolean|{boolean}|--| 865 | |Object|{Object}|--| 866 | |Function|{Function}|--| 867 | |RegExp|{RegExp}|--| 868 | |Array|{Array}|--| 869 | |Date|{Date}|--| 870 | |单一类型集合|{Array.<string>}|string 类型的数组| 871 | |多类型|{(number|boolean)}|可能是 number 类型, 也可能是 boolean 类型| 872 | |允许为null|{?number}|可能是 number, 也可能是 null| 873 | |不允许为null|{!Object}|Object 类型, 但不是 null| 874 | |Function类型|{function(number, boolean)}|函数, 形参类型| 875 | |Function带返回值|{function(number, boolean):string}|函数, 形参, 返回值类型| 876 | |参数可选|@param {string=} name|可选参数, =为类型后缀| 877 | |可变参数|@param {...number} args|变长参数, ...为类型前缀| 878 | |任意类型|{*}|任意类型| 879 | |可选任意类型|@param {*=} name|可选参数,类型不限| 880 | |可变任意类型|@param {...*} args|变长参数,类型不限| 881 | 882 | 883 | #### 2.4.5 文件注释 884 | 885 | 886 | ##### [强制] 文件顶部必须包含文件注释,用 `@file` 标识文件说明。 887 | 888 | 示例: 889 | 890 | ```javascript 891 | /** 892 | * @file Describe the file 893 | */ 894 | ``` 895 | 896 | ##### [建议] 文件注释中可以用 `@author` 标识开发者信息。 897 | 898 | 解释: 899 | 900 | 开发者信息能够体现开发人员对文件的贡献,并且能够让遇到问题或希望了解相关信息的人找到维护人。通常情况文件在被创建时标识的是创建者。随着项目的进展,越来越多的人加入,参与这个文件的开发,新的作者应该被加入 `@author` 标识。 901 | 902 | `@author` 标识具有多人时,原则是按照 `责任` 进行排序。通常的说就是如果有问题,就是找第一个人应该比找第二个人有效。比如文件的创建者由于各种原因,模块移交给了其他人或其他团队,后来因为新增需求,其他人在新增代码时,添加 `@author` 标识应该把自己的名字添加在创建人的前面。 903 | 904 | `@author` 中的名字不允许被删除。任何劳动成果都应该被尊重。 905 | 906 | 业务项目中,一个文件可能被多人频繁修改,并且每个人的维护时间都可能不会很长,不建议为文件增加 `@author` 标识。通过版本控制系统追踪变更,按业务逻辑单元确定模块的维护责任人,通过文档与wiki跟踪和查询,是更好的责任管理方式。 907 | 908 | 对于业务逻辑无关的技术型基础项目,特别是开源的公共项目,应使用 `@author` 标识。 909 | 910 | 911 | 示例: 912 | 913 | ```javascript 914 | /** 915 | * @file Describe the file 916 | * @author author-name(mail-name@domain.com) 917 | * author-name2(mail-name2@domain.com) 918 | */ 919 | ``` 920 | 921 | #### 2.4.6 命名空间注释 922 | 923 | 924 | ##### [建议] 命名空间使用 `@namespace` 标识。 925 | 926 | 示例: 927 | 928 | ```javascript 929 | /** 930 | * @namespace 931 | */ 932 | var util = {}; 933 | ``` 934 | 935 | #### 2.4.7 类注释 936 | 937 | 938 | ##### [建议] 使用 `@class` 标记类或构造函数。 939 | 940 | 解释: 941 | 942 | 对于使用对象 `constructor` 属性来定义的构造函数,可以使用 `@constructor` 来标记。 943 | 944 | 945 | 示例: 946 | 947 | ```javascript 948 | /** 949 | * 描述 950 | * 951 | * @class 952 | */ 953 | function Developer() { 954 | // constructor body 955 | } 956 | ``` 957 | 958 | ##### [建议] 使用 `@extends` 标记类的继承信息。 959 | 960 | 示例: 961 | 962 | ```javascript 963 | /** 964 | * 描述 965 | * 966 | * @class 967 | * @extends Developer 968 | */ 969 | function Fronteer() { 970 | Developer.call(this); 971 | // constructor body 972 | } 973 | util.inherits(Fronteer, Developer); 974 | ``` 975 | 976 | ##### [强制] 使用包装方式扩展类成员时, 必须通过 `@lends` 进行重新指向。 977 | 978 | 解释: 979 | 980 | 没有 `@lends` 标记将无法为该类生成包含扩展类成员的文档。 981 | 982 | 983 | 示例: 984 | 985 | ```javascript 986 | /** 987 | * 类描述 988 | * 989 | * @class 990 | * @extends Developer 991 | */ 992 | function Fronteer() { 993 | Developer.call(this); 994 | // constructor body 995 | } 996 | 997 | util.extend( 998 | Fronteer.prototype, 999 | /** @lends Fronteer.prototype */{ 1000 | _getLevel: function () { 1001 | // TODO 1002 | } 1003 | } 1004 | ); 1005 | ``` 1006 | 1007 | ##### [强制] 类的属性或方法等成员信息使用 `@public` / `@protected` / `@private` 中的任意一个,指明可访问性。 1008 | 1009 | 解释: 1010 | 1011 | 生成的文档中将有可访问性的标记,避免用户直接使用非 `public` 的属性或方法。 1012 | 1013 | 示例: 1014 | 1015 | ```javascript 1016 | /** 1017 | * 类描述 1018 | * 1019 | * @class 1020 | * @extends Developer 1021 | */ 1022 | var Fronteer = function () { 1023 | Developer.call(this); 1024 | 1025 | /** 1026 | * 属性描述 1027 | * 1028 | * @type {string} 1029 | * @private 1030 | */ 1031 | this._level = 'T12'; 1032 | 1033 | // constructor body 1034 | }; 1035 | util.inherits(Fronteer, Developer); 1036 | 1037 | /** 1038 | * 方法描述 1039 | * 1040 | * @private 1041 | * @return {string} 返回值描述 1042 | */ 1043 | Fronteer.prototype._getLevel = function () { 1044 | }; 1045 | ``` 1046 | 1047 | 1048 | #### 2.4.8 函数/方法注释 1049 | 1050 | 1051 | ##### [强制] 函数/方法注释必须包含函数说明,有参数和返回值时必须使用注释标识。 1052 | 1053 | ##### [强制] 参数和返回值注释必须包含类型信息和说明。 1054 | 1055 | ##### [建议] 当函数是内部函数,外部不可访问时,可以使用 `@inner` 标识。 1056 | 1057 | 示例: 1058 | 1059 | ```javascript 1060 | /** 1061 | * 函数描述 1062 | * 1063 | * @param {string} p1 参数1的说明 1064 | * @param {string} p2 参数2的说明,比较长 1065 | * 那就换行了. 1066 | * @param {number=} p3 参数3的说明(可选) 1067 | * @return {Object} 返回值描述 1068 | */ 1069 | function foo(p1, p2, p3) { 1070 | var p3 = p3 || 10; 1071 | return { 1072 | p1: p1, 1073 | p2: p2, 1074 | p3: p3 1075 | }; 1076 | } 1077 | ``` 1078 | 1079 | ##### [强制] 对 Object 中各项的描述, 必须使用 `@param` 标识。 1080 | 1081 | 示例: 1082 | 1083 | ```javascript 1084 | /** 1085 | * 函数描述 1086 | * 1087 | * @param {Object} option 参数描述 1088 | * @param {string} option.url option项描述 1089 | * @param {string=} option.method option项描述,可选参数 1090 | */ 1091 | function foo(option) { 1092 | // TODO 1093 | } 1094 | ``` 1095 | 1096 | ##### [建议] 重写父类方法时, 应当添加 `@override` 标识。如果重写的形参个数、类型、顺序和返回值类型均未发生变化,可省略 `@param`、`@return`,仅用 `@override` 标识,否则仍应作完整注释。 1097 | 1098 | 解释: 1099 | 1100 | 简而言之,当子类重写的方法能直接套用父类的方法注释时可省略对参数与返回值的注释。 1101 | 1102 | #### 2.4.9 事件注释 1103 | 1104 | 1105 | ##### [强制] 必须使用 `@event` 标识事件,事件参数的标识与方法描述的参数标识相同。 1106 | 1107 | 示例: 1108 | 1109 | ```javascript 1110 | /** 1111 | * 值变更时触发 1112 | * 1113 | * @event 1114 | * @param {Object} e e描述 1115 | * @param {string} e.before before描述 1116 | * @param {string} e.after after描述 1117 | */ 1118 | onchange: function (e) { 1119 | } 1120 | ``` 1121 | 1122 | ##### [强制] 在会广播事件的函数前使用 `@fires` 标识广播的事件,在广播事件代码前使用 `@event` 标识事件。 1123 | 1124 | ##### [建议] 对于事件对象的注释,使用 `@param` 标识,生成文档时可读性更好。 1125 | 1126 | 示例: 1127 | 1128 | ```javascript 1129 | /** 1130 | * 点击处理 1131 | * 1132 | * @fires Select#change 1133 | * @private 1134 | */ 1135 | Select.prototype.clickHandler = function () { 1136 | /** 1137 | * 值变更时触发 1138 | * 1139 | * @event Select#change 1140 | * @param {Object} e e描述 1141 | * @param {string} e.before before描述 1142 | * @param {string} e.after after描述 1143 | */ 1144 | this.fire( 1145 | 'change', 1146 | { 1147 | before: 'foo', 1148 | after: 'bar' 1149 | } 1150 | ); 1151 | }; 1152 | ``` 1153 | 1154 | #### 2.4.10 常量注释 1155 | 1156 | 1157 | ##### [强制] 常量必须使用 `@const` 标记,并包含说明和类型信息。 1158 | 1159 | 示例: 1160 | 1161 | ```javascript 1162 | /** 1163 | * 常量说明 1164 | * 1165 | * @const 1166 | * @type {string} 1167 | */ 1168 | var REQUEST_URL = 'myurl.do'; 1169 | ``` 1170 | 1171 | #### 2.4.11 复杂类型注释 1172 | 1173 | 1174 | ##### [建议] 对于类型未定义的复杂结构的注释,可以使用 `@typedef` 标识来定义。 1175 | 1176 | 示例: 1177 | 1178 | ```javascript 1179 | // `namespaceA~` 可以换成其它 namepaths 前缀,目的是为了生成文档中能显示 `@typedef` 定义的类型和链接。 1180 | /** 1181 | * 服务器 1182 | * 1183 | * @typedef {Object} namespaceA~Server 1184 | * @property {string} host 主机 1185 | * @property {number} port 端口 1186 | */ 1187 | 1188 | /** 1189 | * 服务器列表 1190 | * 1191 | * @type {Array.} 1192 | */ 1193 | var servers = [ 1194 | { 1195 | host: '1.2.3.4', 1196 | port: 8080 1197 | }, 1198 | { 1199 | host: '1.2.3.5', 1200 | port: 8081 1201 | } 1202 | ]; 1203 | ``` 1204 | 1205 | 1206 | #### 2.4.12 AMD 模块注释 1207 | 1208 | 1209 | ##### [强制] AMD 模块使用 `@module` 或 `@exports` 标识。 1210 | 1211 | 解释: 1212 | 1213 | @exports 与 @module 都可以用来标识模块,区别在于 @module 可以省略模块名称。而只使用 @exports 时在 namepaths 中可以省略 module: 前缀。 1214 | 1215 | 1216 | 示例: 1217 | 1218 | ```javascript 1219 | define( 1220 | function (require) { 1221 | 1222 | /** 1223 | * foo description 1224 | * 1225 | * @exports Foo 1226 | */ 1227 | var foo = { 1228 | // TODO 1229 | }; 1230 | 1231 | /** 1232 | * baz description 1233 | * 1234 | * @return {boolean} return description 1235 | */ 1236 | foo.baz = function () { 1237 | // TODO 1238 | }; 1239 | 1240 | return foo; 1241 | 1242 | } 1243 | ); 1244 | ``` 1245 | 1246 | 也可以在 exports 变量前使用 @module 标识: 1247 | 1248 | ```javascript 1249 | define( 1250 | function (require) { 1251 | 1252 | /** 1253 | * module description. 1254 | * 1255 | * @module foo 1256 | */ 1257 | var exports = {}; 1258 | 1259 | 1260 | /** 1261 | * bar description 1262 | * 1263 | */ 1264 | exports.bar = function () { 1265 | // TODO 1266 | }; 1267 | 1268 | return exports; 1269 | } 1270 | ); 1271 | ``` 1272 | 1273 | 如果直接使用 factory 的 exports 参数,还可以: 1274 | 1275 | ```javascript 1276 | /** 1277 | * module description. 1278 | * 1279 | * @module 1280 | */ 1281 | define( 1282 | function (require, exports) { 1283 | 1284 | /** 1285 | * bar description 1286 | * 1287 | */ 1288 | exports.bar = function () { 1289 | // TODO 1290 | }; 1291 | return exports; 1292 | } 1293 | ); 1294 | ``` 1295 | 1296 | ##### [强制] 对于已使用 `@module` 标识为 AMD模块 的引用,在 `namepaths` 中必须增加 `module:` 作前缀。 1297 | 1298 | 解释: 1299 | 1300 | namepaths 没有 module: 前缀时,生成的文档中将无法正确生成链接。 1301 | 1302 | 示例: 1303 | 1304 | ```javascript 1305 | /** 1306 | * 点击处理 1307 | * 1308 | * @fires module:Select#change 1309 | * @private 1310 | */ 1311 | Select.prototype.clickHandler = function () { 1312 | /** 1313 | * 值变更时触发 1314 | * 1315 | * @event module:Select#change 1316 | * @param {Object} e e描述 1317 | * @param {string} e.before before描述 1318 | * @param {string} e.after after描述 1319 | */ 1320 | this.fire( 1321 | 'change', 1322 | { 1323 | before: 'foo', 1324 | after: 'bar' 1325 | } 1326 | ); 1327 | }; 1328 | ``` 1329 | 1330 | ##### [建议] 对于类定义的模块,可以使用 `@alias` 标识构建函数。 1331 | 1332 | 示例: 1333 | 1334 | ```javascript 1335 | /** 1336 | * A module representing a jacket. 1337 | * @module jacket 1338 | */ 1339 | define( 1340 | function () { 1341 | 1342 | /** 1343 | * @class 1344 | * @alias module:jacket 1345 | */ 1346 | var Jacket = function () { 1347 | }; 1348 | 1349 | return Jacket; 1350 | } 1351 | ); 1352 | ``` 1353 | 1354 | 1355 | ##### [建议] 多模块定义时,可以使用 `@exports` 标识各个模块。 1356 | 1357 | 示例: 1358 | 1359 | ```javascript 1360 | // one module 1361 | define('html/utils', 1362 | /** 1363 | * Utility functions to ease working with DOM elements. 1364 | * @exports html/utils 1365 | */ 1366 | function () { 1367 | var exports = { 1368 | }; 1369 | 1370 | return exports; 1371 | } 1372 | ); 1373 | 1374 | // another module 1375 | define('tag', 1376 | /** @exports tag */ 1377 | function () { 1378 | var exports = { 1379 | }; 1380 | 1381 | return exports; 1382 | } 1383 | ); 1384 | ``` 1385 | 1386 | ##### [建议] 对于 exports 为 Object 的模块,可以使用`@namespace`标识。 1387 | 1388 | 解释: 1389 | 1390 | 使用 @namespace 而不是 @module 或 @exports 时,对模块的引用可以省略 module: 前缀。 1391 | 1392 | ##### [建议] 对于 exports 为类名的模块,使用 `@class` 和 `@exports` 标识。 1393 | 1394 | 1395 | 示例: 1396 | 1397 | ```javascript 1398 | 1399 | // 只使用 @class Bar 时,类方法和属性都必须增加 @name Bar#methodName 来标识,与 @exports 配合可以免除这一麻烦,并且在引用时可以省去 module: 前缀。 1400 | // 另外需要注意类名需要使用 var 定义的方式。 1401 | 1402 | /** 1403 | * Bar description 1404 | * 1405 | * @see foo 1406 | * @exports Bar 1407 | * @class 1408 | */ 1409 | var Bar = function () { 1410 | // TODO 1411 | }; 1412 | 1413 | /** 1414 | * baz description 1415 | * 1416 | * @return {(string|Array)} return description 1417 | */ 1418 | Bar.prototype.baz = function () { 1419 | // TODO 1420 | }; 1421 | ``` 1422 | 1423 | 1424 | #### 2.4.13 细节注释 1425 | 1426 | 1427 | 对于内部实现、不容易理解的逻辑说明、摘要信息等,我们可能需要编写细节注释。 1428 | 1429 | #### [建议] 细节注释遵循单行注释的格式。说明必须换行时,每行是一个单行注释的起始。 1430 | 1431 | 示例: 1432 | 1433 | ```javascript 1434 | function foo(p1, p2, opt_p3) { 1435 | // 这里对具体内部逻辑进行说明 1436 | // 说明太长需要换行 1437 | for (...) { 1438 | .... 1439 | } 1440 | } 1441 | ``` 1442 | 1443 | ##### [强制] 有时我们会使用一些特殊标记进行说明。特殊标记必须使用单行注释的形式。下面列举了一些常用标记: 1444 | 1445 | 解释: 1446 | 1447 | 1. TODO: 有功能待实现。此时需要对将要实现的功能进行简单说明。 1448 | 2. FIXME: 该处代码运行没问题,但可能由于时间赶或者其他原因,需要修正。此时需要对如何修正进行简单说明。 1449 | 3. HACK: 为修正某些问题而写的不太好或者使用了某些诡异手段的代码。此时需要对思路或诡异手段进行描述。 1450 | 4. XXX: 该处存在陷阱。此时需要对陷阱进行描述。 1451 | 1452 | 1453 | 1454 | 1455 | ## 3 语言特性 1456 | 1457 | 1458 | 1459 | 1460 | 1461 | 1462 | ### 3.1 变量 1463 | 1464 | 1465 | ##### [强制] 变量在使用前必须通过 `var` 定义。 1466 | 1467 | 解释: 1468 | 1469 | 不通过 var 定义变量将导致变量污染全局环境。 1470 | 1471 | 1472 | 示例: 1473 | 1474 | ```javascript 1475 | // good 1476 | var name = 'MyName'; 1477 | 1478 | // bad 1479 | name = 'MyName'; 1480 | ``` 1481 | 1482 | ##### [强制] 每个 `var` 只能声明一个变量。 1483 | 1484 | 解释: 1485 | 1486 | 一个 var 声明多个变量,容易导致较长的行长度,并且在修改时容易造成逗号和分号的混淆。 1487 | 1488 | 1489 | 示例: 1490 | 1491 | ```javascript 1492 | // good 1493 | var hangModules = []; 1494 | var missModules = []; 1495 | var visited = {}; 1496 | 1497 | // bad 1498 | var hangModules = [], 1499 | missModules = [], 1500 | visited = {}; 1501 | ``` 1502 | 1503 | 1504 | ##### [强制] 变量必须 `即用即声明`,不得在函数或其它形式的代码块起始位置统一声明所有变量。 1505 | 1506 | 解释: 1507 | 1508 | 变量声明与使用的距离越远,出现的跨度越大,代码的阅读与维护成本越高。虽然JavaScript的变量是函数作用域,还是应该根据编程中的意图,缩小变量出现的距离空间。 1509 | 1510 | 1511 | 示例: 1512 | 1513 | ```javascript 1514 | // good 1515 | function kv2List(source) { 1516 | var list = []; 1517 | 1518 | for (var key in source) { 1519 | if (source.hasOwnProperty(key)) { 1520 | var item = { 1521 | k: key, 1522 | v: source[key] 1523 | }; 1524 | list.push(item); 1525 | } 1526 | } 1527 | 1528 | return list; 1529 | } 1530 | 1531 | // bad 1532 | function kv2List(source) { 1533 | var list = []; 1534 | var key; 1535 | var item; 1536 | 1537 | for (key in source) { 1538 | if (source.hasOwnProperty(key)) { 1539 | item = { 1540 | k: key, 1541 | v: source[key] 1542 | }; 1543 | list.push(item); 1544 | } 1545 | } 1546 | 1547 | return list; 1548 | } 1549 | ``` 1550 | 1551 | 1552 | 1553 | 1554 | 1555 | 1556 | ### 3.2 条件 1557 | 1558 | 1559 | ##### [强制] 在 Equality Expression 中使用类型严格的 `===`。仅当判断 null 或 undefined 时,允许使用 `== null`。 1560 | 1561 | 解释: 1562 | 1563 | 使用 === 可以避免等于判断中隐式的类型转换。 1564 | 1565 | 1566 | 示例: 1567 | 1568 | ```javascript 1569 | // good 1570 | if (age === 30) { 1571 | // ...... 1572 | } 1573 | 1574 | // bad 1575 | if (age == 30) { 1576 | // ...... 1577 | } 1578 | ``` 1579 | 1580 | ##### [建议] 尽可能使用简洁的表达式。 1581 | 1582 | 1583 | 示例: 1584 | 1585 | ```javascript 1586 | // 字符串为空 1587 | 1588 | // good 1589 | if (!name) { 1590 | // ...... 1591 | } 1592 | 1593 | // bad 1594 | if (name === '') { 1595 | // ...... 1596 | } 1597 | ``` 1598 | 1599 | ```javascript 1600 | // 字符串非空 1601 | 1602 | // good 1603 | if (name) { 1604 | // ...... 1605 | } 1606 | 1607 | // bad 1608 | if (name !== '') { 1609 | // ...... 1610 | } 1611 | ``` 1612 | 1613 | ```javascript 1614 | // 数组非空 1615 | 1616 | // good 1617 | if (collection.length) { 1618 | // ...... 1619 | } 1620 | 1621 | // bad 1622 | if (collection.length > 0) { 1623 | // ...... 1624 | } 1625 | ``` 1626 | 1627 | ```javascript 1628 | // 布尔不成立 1629 | 1630 | // good 1631 | if (!notTrue) { 1632 | // ...... 1633 | } 1634 | 1635 | // bad 1636 | if (notTrue === false) { 1637 | // ...... 1638 | } 1639 | ``` 1640 | 1641 | ```javascript 1642 | // null 或 undefined 1643 | 1644 | // good 1645 | if (noValue == null) { 1646 | // ...... 1647 | } 1648 | 1649 | // bad 1650 | if (noValue === null || typeof noValue === 'undefined') { 1651 | // ...... 1652 | } 1653 | ``` 1654 | 1655 | 1656 | ##### [建议] 按执行频率排列分支的顺序。 1657 | 1658 | 解释: 1659 | 1660 | 按执行频率排列分支的顺序好处是: 1661 | 1662 | 1. 阅读的人容易找到最常见的情况,增加可读性。 1663 | 2. 提高执行效率。 1664 | 1665 | 1666 | ##### [建议] 对于相同变量或表达式的多值条件,用 `switch` 代替 `if`。 1667 | 1668 | 示例: 1669 | 1670 | ```javascript 1671 | // good 1672 | switch (typeof variable) { 1673 | case 'object': 1674 | // ...... 1675 | break; 1676 | case 'number': 1677 | case 'boolean': 1678 | case 'string': 1679 | // ...... 1680 | break; 1681 | } 1682 | 1683 | // bad 1684 | var type = typeof variable; 1685 | if (type === 'object') { 1686 | // ...... 1687 | } 1688 | else if (type === 'number' || type === 'boolean' || type === 'string') { 1689 | // ...... 1690 | } 1691 | ``` 1692 | 1693 | ##### [建议] 如果函数或全局中的 `else` 块后没有任何语句,可以删除 `else`。 1694 | 1695 | 示例: 1696 | 1697 | ```javascript 1698 | // good 1699 | function getName() { 1700 | if (name) { 1701 | return name; 1702 | } 1703 | 1704 | return 'unnamed'; 1705 | } 1706 | 1707 | // bad 1708 | function getName() { 1709 | if (name) { 1710 | return name; 1711 | } 1712 | else { 1713 | return 'unnamed'; 1714 | } 1715 | } 1716 | ``` 1717 | 1718 | 1719 | 1720 | 1721 | 1722 | ### 3.3 循环 1723 | 1724 | 1725 | ##### [建议] 不要在循环体中包含函数表达式,事先将函数提取到循环体外。 1726 | 1727 | 解释: 1728 | 1729 | 循环体中的函数表达式,运行过程中会生成循环次数个函数对象。 1730 | 1731 | 1732 | 示例: 1733 | 1734 | ```javascript 1735 | // good 1736 | function clicker() { 1737 | // ...... 1738 | } 1739 | 1740 | for (var i = 0, len = elements.length; i < len; i++) { 1741 | var element = elements[i]; 1742 | addListener(element, 'click', clicker); 1743 | } 1744 | 1745 | 1746 | // bad 1747 | for (var i = 0, len = elements.length; i < len; i++) { 1748 | var element = elements[i]; 1749 | addListener(element, 'click', function () {}); 1750 | } 1751 | ``` 1752 | 1753 | ##### [建议] 对循环内多次使用的不变值,在循环外用变量缓存。 1754 | 1755 | 示例: 1756 | 1757 | ```javascript 1758 | // good 1759 | var width = wrap.offsetWidth + 'px'; 1760 | for (var i = 0, len = elements.length; i < len; i++) { 1761 | var element = elements[i]; 1762 | element.style.width = width; 1763 | // ...... 1764 | } 1765 | 1766 | 1767 | // bad 1768 | for (var i = 0, len = elements.length; i < len; i++) { 1769 | var element = elements[i]; 1770 | element.style.width = wrap.offsetWidth + 'px'; 1771 | // ...... 1772 | } 1773 | ``` 1774 | 1775 | 1776 | ##### [建议] 对有序集合进行遍历时,缓存 `length`。 1777 | 1778 | 解释: 1779 | 1780 | 虽然现代浏览器都对数组长度进行了缓存,但对于一些宿主对象和老旧浏览器的数组对象,在每次 length 访问时会动态计算元素个数,此时缓存 length 能有效提高程序性能。 1781 | 1782 | 1783 | 示例: 1784 | 1785 | ```javascript 1786 | for (var i = 0, len = elements.length; i < len; i++) { 1787 | var element = elements[i]; 1788 | // ...... 1789 | } 1790 | ``` 1791 | 1792 | ##### [建议] 对有序集合进行顺序无关的遍历时,使用逆序遍历。 1793 | 1794 | 解释: 1795 | 1796 | 逆序遍历可以节省变量,代码比较优化。 1797 | 1798 | 示例: 1799 | 1800 | ```javascript 1801 | var len = elements.length; 1802 | while (len--) { 1803 | var element = elements[len]; 1804 | // ...... 1805 | } 1806 | ``` 1807 | 1808 | 1809 | 1810 | 1811 | 1812 | ### 3.4 类型 1813 | 1814 | 1815 | #### 3.4.1 类型检测 1816 | 1817 | 1818 | ##### [建议] 类型检测优先使用 `typeof`。对象类型检测使用 `instanceof`。`null` 或 `undefined` 的检测使用 `== null`。 1819 | 1820 | 示例: 1821 | 1822 | ```javascript 1823 | // string 1824 | typeof variable === 'string' 1825 | 1826 | // number 1827 | typeof variable === 'number' 1828 | 1829 | // boolean 1830 | typeof variable === 'boolean' 1831 | 1832 | // Function 1833 | typeof variable === 'function' 1834 | 1835 | // Object 1836 | typeof variable === 'object' 1837 | 1838 | // RegExp 1839 | variable instanceof RegExp 1840 | 1841 | // Array 1842 | variable instanceof Array 1843 | 1844 | // null 1845 | variable === null 1846 | 1847 | // null or undefined 1848 | variable == null 1849 | 1850 | // undefined 1851 | typeof variable === 'undefined' 1852 | ``` 1853 | 1854 | 1855 | #### 3.4.2 类型转换 1856 | 1857 | 1858 | ##### [建议] 转换成 `string` 时,使用 `+ ''`。 1859 | 1860 | 示例: 1861 | 1862 | ```javascript 1863 | // good 1864 | num + ''; 1865 | 1866 | // bad 1867 | new String(num); 1868 | num.toString(); 1869 | String(num); 1870 | ``` 1871 | 1872 | ##### [建议] 转换成 `number` 时,通常使用 `+`。 1873 | 1874 | 示例: 1875 | 1876 | ```javascript 1877 | // good 1878 | +str; 1879 | 1880 | // bad 1881 | Number(str); 1882 | ``` 1883 | 1884 | ##### [建议] `string` 转换成 `number`,要转换的字符串结尾包含非数字并期望忽略时,使用 `parseInt`。 1885 | 1886 | 示例: 1887 | 1888 | ```javascript 1889 | var width = '200px'; 1890 | parseInt(width, 10); 1891 | ``` 1892 | 1893 | ##### [强制] 使用 `parseInt` 时,必须指定进制。 1894 | 1895 | 示例: 1896 | 1897 | ```javascript 1898 | // good 1899 | parseInt(str, 10); 1900 | 1901 | // bad 1902 | parseInt(str); 1903 | ``` 1904 | 1905 | ##### [建议] 转换成 `boolean` 时,使用 `!!`。 1906 | 1907 | 示例: 1908 | 1909 | ```javascript 1910 | var num = 3.14; 1911 | !!num; 1912 | ``` 1913 | 1914 | ##### [建议] `number` 去除小数点,使用 `Math.floor / Math.round / Math.ceil`,不使用 `parseInt`。 1915 | 1916 | 示例: 1917 | 1918 | ```javascript 1919 | // good 1920 | var num = 3.14; 1921 | Math.ceil(num); 1922 | 1923 | // bad 1924 | var num = 3.14; 1925 | parseInt(num, 10); 1926 | ``` 1927 | 1928 | 1929 | 1930 | 1931 | ### 3.5 字符串 1932 | 1933 | 1934 | ##### [强制] 字符串开头和结束使用单引号 `'`。 1935 | 1936 | 解释: 1937 | 1938 | 1. 输入单引号不需要按住 shift,方便输入。 1939 | 2. 实际使用中,字符串经常用来拼接 HTML。为方便 HTML 中包含双引号而不需要转义写法。 1940 | 1941 | 示例: 1942 | 1943 | ```javascript 1944 | var str = '我是一个字符串'; 1945 | var html = '
    拼接HTML可以省去双引号转义
    '; 1946 | ``` 1947 | 1948 | ##### [建议] 使用 `数组` 或 `+` 拼接字符串。 1949 | 1950 | 解释: 1951 | 1952 | 1. 使用 + 拼接字符串,如果拼接的全部是 StringLiteral,压缩工具可以对其进行自动合并的优化。所以,静态字符串建议使用 + 拼接。 1953 | 2. 在现代浏览器下,使用 + 拼接字符串,性能较数组的方式要高。 1954 | 3. 如需要兼顾老旧浏览器,应尽量使用数组拼接字符串。 1955 | 1956 | 示例: 1957 | 1958 | ```javascript 1959 | // 使用数组拼接字符串 1960 | var str = [ 1961 | // 推荐换行开始并缩进开始第一个字符串, 对齐代码, 方便阅读. 1962 | '
      ', 1963 | '
    • 第一项
    • ', 1964 | '
    • 第二项
    • ', 1965 | '
    ' 1966 | ].join(''); 1967 | 1968 | // 使用 + 拼接字符串 1969 | var str2 = '' // 建议第一个为空字符串, 第二个换行开始并缩进开始, 对齐代码, 方便阅读 1970 | + '
      ', 1971 | + '
    • 第一项
    • ', 1972 | + '
    • 第二项
    • ', 1973 | + '
    '; 1974 | ``` 1975 | 1976 | ##### [建议] 复杂的数据到视图字符串的转换过程,选用一种模板引擎。 1977 | 1978 | 解释: 1979 | 1980 | 使用模板引擎有如下好处: 1981 | 1982 | 1. 在开发过程中专注于数据,将视图生成的过程由另外一个层级维护,使程序逻辑结构更清晰。 1983 | 2. 优秀的模板引擎,通过模板编译技术和高质量的编译产物,能获得比手工拼接字符串更高的性能。 1984 | 1985 | - artTemplate: 体积较小,在所有环境下性能高,语法灵活。 1986 | - dot.js: 体积小,在现代浏览器下性能高,语法灵活。 1987 | - etpl: 体积较小,在所有环境下性能高,模板复用性高,语法灵活。 1988 | - handlebars: 体积大,在所有环境下性能高,扩展性高。 1989 | - hogon: 体积小,在现代浏览器下性能高。 1990 | - nunjucks: 体积较大,性能一般,模板复用性高。 1991 | 1992 | 1993 | 1994 | 1995 | ### 3.6 对象 1996 | 1997 | 1998 | ##### [强制] 使用对象字面量 `{}` 创建新 `Object`。 1999 | 2000 | 示例: 2001 | 2002 | ```javascript 2003 | // good 2004 | var obj = {}; 2005 | 2006 | // bad 2007 | var obj = new Object(); 2008 | ``` 2009 | 2010 | ##### [强制] 对象创建时,如果一个对象的所有 `属性` 均可以不添加引号,则所有 `属性` 不得添加引号。 2011 | 2012 | 示例: 2013 | 2014 | ```javascript 2015 | var info = { 2016 | name: 'someone', 2017 | age: 28 2018 | }; 2019 | ``` 2020 | 2021 | ##### [强制] 对象创建时,如果任何一个 `属性` 需要添加引号,则所有 `属性` 必须添加 `'`。 2022 | 2023 | 解释: 2024 | 2025 | 如果属性不符合 Identifier 和 NumberLiteral 的形式,就需要以 StringLiteral 的形式提供。 2026 | 2027 | 2028 | 示例: 2029 | 2030 | ```javascript 2031 | // good 2032 | var info = { 2033 | 'name': 'someone', 2034 | 'age': 28, 2035 | 'more-info': '...' 2036 | }; 2037 | 2038 | // bad 2039 | var info = { 2040 | name: 'someone', 2041 | age: 28, 2042 | 'more-info': '...' 2043 | }; 2044 | ``` 2045 | 2046 | ##### [强制] 不允许修改和扩展任何原生对象和宿主对象的原型。 2047 | 2048 | 示例: 2049 | 2050 | ```javascript 2051 | // 以下行为绝对禁止 2052 | String.prototype.trim = function () { 2053 | }; 2054 | ``` 2055 | 2056 | ##### [建议] 属性访问时,尽量使用 `.`。 2057 | 2058 | 解释: 2059 | 2060 | 属性名符合 Identifier 的要求,就可以通过 `.` 来访问,否则就只能通过 `[expr]` 方式访问。 2061 | 2062 | 通常在 JavaScript 中声明的对象,属性命名是使用 Camel 命名法,用 `.` 来访问更清晰简洁。部分特殊的属性(比如来自后端的JSON),可能采用不寻常的命名方式,可以通过 `[expr]` 方式访问。 2063 | 2064 | 2065 | 示例: 2066 | 2067 | ```javascript 2068 | info.age; 2069 | info['more-info']; 2070 | ``` 2071 | 2072 | ##### [建议] `for in` 遍历对象时, 使用 `hasOwnProperty` 过滤掉原型中的属性。 2073 | 2074 | 示例: 2075 | 2076 | ```javascript 2077 | var newInfo = {}; 2078 | for (var key in info) { 2079 | if (info.hasOwnProperty(key)) { 2080 | newInfo[key] = info[key]; 2081 | } 2082 | } 2083 | ``` 2084 | 2085 | 2086 | 2087 | 2088 | ### 3.7 数组 2089 | 2090 | 2091 | ##### [强制] 使用数组字面量 `[]` 创建新数组,除非想要创建的是指定长度的数组。 2092 | 2093 | 示例: 2094 | 2095 | ```javascript 2096 | // good 2097 | var arr = []; 2098 | 2099 | // bad 2100 | var arr = new Array(); 2101 | ``` 2102 | 2103 | ##### [强制] 遍历数组不使用 `for in`。 2104 | 2105 | 解释: 2106 | 2107 | 数组对象可能存在数字以外的属性, 这种情况下 for in 不会得到正确结果. 2108 | 2109 | 示例: 2110 | 2111 | ```javascript 2112 | var arr = ['a', 'b', 'c']; 2113 | arr.other = 'other things'; // 这里仅作演示, 实际中应使用Object类型 2114 | 2115 | // 正确的遍历方式 2116 | for (var i = 0, len = arr.length; i < len; i++) { 2117 | console.log(i); 2118 | } 2119 | 2120 | // 错误的遍历方式 2121 | for (i in arr) { 2122 | console.log(i); 2123 | } 2124 | ``` 2125 | 2126 | ##### [建议] 不因为性能的原因自己实现数组排序功能,尽量使用数组的 `sort` 方法。 2127 | 2128 | 解释: 2129 | 2130 | 自己实现的常规排序算法,在性能上并不优于数组默认的 sort 方法。以下两种场景可以自己实现排序: 2131 | 2132 | 1. 需要稳定的排序算法,达到严格一致的排序结果。 2133 | 2. 数据特点鲜明,适合使用桶排。 2134 | 2135 | ##### [建议] 清空数组使用 `.length = 0`。 2136 | 2137 | 2138 | 2139 | 2140 | ### 3.8 函数 2141 | 2142 | 2143 | 2144 | #### 3.8.1 函数长度 2145 | 2146 | 2147 | ##### [建议] 一个函数的长度控制在 `50` 行以内。 2148 | 2149 | 解释: 2150 | 2151 | 将过多的逻辑单元混在一个大函数中,易导致难以维护。一个清晰易懂的函数应该完成单一的逻辑单元。复杂的操作应进一步抽取,通过函数的调用来体现流程。 2152 | 2153 | 特定算法等不可分割的逻辑允许例外。 2154 | 2155 | 2156 | 示例: 2157 | 2158 | ```javascript 2159 | function syncViewStateOnUserAction() { 2160 | if (x.checked) { 2161 | y.checked = true; 2162 | z.value = ''; 2163 | } 2164 | else { 2165 | y.checked = false; 2166 | } 2167 | 2168 | if (!a.value) { 2169 | warning.innerText = 'Please enter it'; 2170 | submitButton.disabled = true; 2171 | } 2172 | else { 2173 | warning.innerText = ''; 2174 | submitButton.disabled = false; 2175 | } 2176 | } 2177 | 2178 | // 直接阅读该函数会难以明确其主线逻辑,因此下方是一种更合理的表达方式: 2179 | 2180 | function syncViewStateOnUserAction() { 2181 | syncXStateToView(); 2182 | checkAAvailability(); 2183 | } 2184 | 2185 | function syncXStateToView() { 2186 | if (x.checked) { 2187 | y.checked = true; 2188 | z.value = ''; 2189 | } 2190 | else { 2191 | y.checked = false; 2192 | } 2193 | } 2194 | 2195 | function checkAAvailability() { 2196 | if (!a.value) { 2197 | displayWarningForAMissing(); 2198 | } 2199 | else { 2200 | clearWarnignForA(); 2201 | } 2202 | } 2203 | ``` 2204 | 2205 | 2206 | #### 3.8.2 参数设计 2207 | 2208 | 2209 | ##### [建议] 一个函数的参数控制在 `6` 个以内。 2210 | 2211 | 解释: 2212 | 2213 | 除去不定长参数以外,函数具备不同逻辑意义的参数建议控制在 6 个以内,过多参数会导致维护难度增大。 2214 | 2215 | 某些情况下,如使用 AMD Loader 的 require 加载多个模块时,其 callback 可能会存在较多参数,因此对函数参数的个数不做强制限制。 2216 | 2217 | 2218 | ##### [建议] 通过 `options` 参数传递非数据输入型参数。 2219 | 2220 | 解释: 2221 | 2222 | 有些函数的参数并不是作为算法的输入,而是对算法的某些分支条件判断之用,此类参数建议通过一个 options 参数传递。 2223 | 2224 | 如下函数: 2225 | 2226 | ```javascript 2227 | /** 2228 | * 移除某个元素 2229 | * 2230 | * @param {Node} element 需要移除的元素 2231 | * @param {boolean} removeEventListeners 是否同时将所有注册在元素上的事件移除 2232 | */ 2233 | function removeElement(element, removeEventListeners) { 2234 | element.parent.removeChild(element); 2235 | if (removeEventListeners) { 2236 | element.clearEventListeners(); 2237 | } 2238 | } 2239 | ``` 2240 | 2241 | 可以转换为下面的签名: 2242 | 2243 | ```javascript 2244 | /** 2245 | * 移除某个元素 2246 | * 2247 | * @param {Node} element 需要移除的元素 2248 | * @param {Object} options 相关的逻辑配置 2249 | * @param {boolean} options.removeEventListeners 是否同时将所有注册在元素上的事件移除 2250 | */ 2251 | function removeElement(element, options) { 2252 | element.parent.removeChild(element); 2253 | if (options.removeEventListeners) { 2254 | element.clearEventListeners(); 2255 | } 2256 | } 2257 | ``` 2258 | 2259 | 这种模式有几个显著的优势: 2260 | 2261 | - boolean 型的配置项具备名称,从调用的代码上更易理解其表达的逻辑意义。 2262 | - 当配置项有增长时,无需无休止地增加参数个数,不会出现 removeElement(element, true, false, false, 3) 这样难以理解的调用代码。 2263 | - 当部分配置参数可选时,多个参数的形式非常难处理重载逻辑,而使用一个 options 对象只需判断属性是否存在,实现得以简化。 2264 | 2265 | 2266 | 2267 | #### 3.8.3 闭包 2268 | 2269 | 2270 | ##### [建议] 在适当的时候将闭包内大对象置为 `null`。 2271 | 2272 | 解释: 2273 | 2274 | 在 JavaScript 中,无需特别的关键词就可以使用闭包,一个函数可以任意访问在其定义的作用域外的变量。需要注意的是,函数的作用域是静态的,即在定义时决定,与调用的时机和方式没有任何关系。 2275 | 2276 | 闭包会阻止一些变量的垃圾回收,对于较老旧的JavaScript引擎,可能导致外部所有变量均无法回收。 2277 | 2278 | 首先一个较为明确的结论是,以下内容会影响到闭包内变量的回收: 2279 | 2280 | - 嵌套的函数中是否有使用该变量。 2281 | - 嵌套的函数中是否有 **直接调用eval**。 2282 | - 是否使用了 with 表达式。 2283 | 2284 | Chakra、V8 和 SpiderMonkey 将受以上因素的影响,表现出不尽相同又较为相似的回收策略,而JScript.dll和Carakan则完全没有这方面的优化,会完整保留整个 LexicalEnvironment 中的所有变量绑定,造成一定的内存消耗。 2285 | 2286 | 由于对闭包内变量有回收优化策略的 Chakra、V8 和 SpiderMonkey 引擎的行为较为相似,因此可以总结如下,当返回一个函数 fn 时: 2287 | 2288 | 1. 如果 fn 的 [[Scope]] 是ObjectEnvironment(with 表达式生成 ObjectEnvironment,函数和 catch 表达式生成 DeclarativeEnvironment),则: 2289 | 1. 如果是 V8 引擎,则退出全过程。 2290 | 2. 如果是 SpiderMonkey,则处理该 ObjectEnvironment 的外层 LexicalEnvironment。 2291 | 2. 获取当前 LexicalEnvironment 下的所有类型为 Function 的对象,对于每一个 Function 对象,分析其 FunctionBody: 2292 | 1. 如果 FunctionBody 中含有 **直接调用eval**,则退出全过程。 2293 | 2. 否则得到所有的 Identifier。 2294 | 3. 对于每一个 Identifier,设其为 name,根据查找变量引用的规则,从 LexicalEnvironment 中找出名称为 name 的绑定 binding。 2295 | 4. 对 binding 添加 notSwap 属性,其值为 true。 2296 | 3. 检查当前 LexicalEnvironment 中的每一个变量绑定,如果该绑定有 notSwap 属性且值为 true,则: 2297 | 1. 如果是V8引擎,删除该绑定。 2298 | 2. 如果是SpiderMonkey,将该绑定的值设为 undefined,将删除 notSwap 属性。 2299 | 2300 | 对于Chakra引擎,暂无法得知是按 V8 的模式还是按 SpiderMonkey 的模式进行。 2301 | 2302 | 如果有 **非常庞大** 的对象,且预计会在 **老旧的引擎** 中执行,则使用闭包时,注意将闭包不需要的对象置为空引用。 2303 | 2304 | ##### [建议] 使用 `IIFE` 避免 `Lift 效应`。 2305 | 2306 | 解释: 2307 | 2308 | 在引用函数外部变量时,函数执行时外部变量的值由运行时决定而非定义时,最典型的场景如下: 2309 | 2310 | ```javascript 2311 | var tasks = []; 2312 | for (var i = 0; i < 5; i++) { 2313 | tasks[tasks.length] = function () { 2314 | console.log('Current cursor is at ' + i); 2315 | }; 2316 | } 2317 | 2318 | var len = tasks.length; 2319 | while (len--) { 2320 | tasks[len](); 2321 | } 2322 | ``` 2323 | 2324 | 以上代码对 tasks 中的函数的执行均会输出 `Current cursor is at 5`,往往不符合预期。 2325 | 2326 | 此现象称为 **Lift 效应** 。解决的方式是通过额外加上一层闭包函数,将需要的外部变量作为参数传递来解除变量的绑定关系: 2327 | 2328 | ```javascript 2329 | var tasks = []; 2330 | for (var i = 0; i < 5; i++) { 2331 | // 注意有一层额外的闭包 2332 | tasks[tasks.length] = (function (i) { 2333 | return function () { 2334 | console.log('Current cursor is at ' + i); 2335 | }; 2336 | })(i); 2337 | } 2338 | 2339 | var len = tasks.length; 2340 | while (len--) { 2341 | tasks[len](); 2342 | } 2343 | ``` 2344 | 2345 | #### 3.8.4 空函数 2346 | 2347 | 2348 | ##### [建议] 空函数不使用 `new Function()` 的形式。 2349 | 2350 | 示例: 2351 | 2352 | ```javascript 2353 | var emptyFunction = function () {}; 2354 | ``` 2355 | 2356 | ##### [建议] 对于性能有高要求的场合,建议存在一个空函数的常量,供多处使用共享。 2357 | 2358 | 示例: 2359 | 2360 | ```javascript 2361 | var EMPTY_FUNCTION = function () {}; 2362 | 2363 | function MyClass() { 2364 | } 2365 | 2366 | MyClass.prototype.abstractMethod = EMPTY_FUNCTION; 2367 | MyClass.prototype.hooks.before = EMPTY_FUNCTION; 2368 | MyClass.prototype.hooks.after = EMPTY_FUNCTION; 2369 | ``` 2370 | 2371 | 2372 | 2373 | 2374 | 2375 | 2376 | 2377 | ### 3.9 面向对象 2378 | 2379 | 2380 | ##### [强制] 类的继承方案,实现时需要修正 `constructor`。 2381 | 2382 | 解释: 2383 | 2384 | 通常使用其他 library 的类继承方案都会进行 constructor 修正。如果是自己实现的类继承方案,需要进行 constructor 修正。 2385 | 2386 | 2387 | 示例: 2388 | 2389 | ```javascript 2390 | /** 2391 | * 构建类之间的继承关系 2392 | * 2393 | * @param {Function} subClass 子类函数 2394 | * @param {Function} superClass 父类函数 2395 | */ 2396 | function inherits(subClass, superClass) { 2397 | var F = new Function(); 2398 | F.prototype = superClass.prototype; 2399 | subClass.prototype = new F(); 2400 | subClass.prototype.constructor = subClass; 2401 | } 2402 | ``` 2403 | 2404 | ##### [建议] 声明类时,保证 `constructor` 的正确性。 2405 | 2406 | 示例: 2407 | 2408 | ```javascript 2409 | function Animal(name) { 2410 | this.name = name; 2411 | } 2412 | 2413 | // 直接prototype等于对象时,需要修正constructor 2414 | Animal.prototype = { 2415 | constructor: Animal, 2416 | 2417 | jump: function () { 2418 | alert('animal ' + this.name + ' jump'); 2419 | } 2420 | }; 2421 | 2422 | // 这种方式扩展prototype则无需理会constructor 2423 | Animal.prototype.jump = function () { 2424 | alert('animal ' + this.name + ' jump'); 2425 | }; 2426 | ``` 2427 | 2428 | 2429 | ##### [建议] 属性在构造函数中声明,方法在原型中声明。 2430 | 2431 | 解释: 2432 | 2433 | 原型对象的成员被所有实例共享,能节约内存占用。所以编码时我们应该遵守这样的原则:原型对象包含程序不会修改的成员,如方法函数或配置项。 2434 | 2435 | ```javascript 2436 | function TextNode(value, engine) { 2437 | this.value = value; 2438 | this.engine = engine; 2439 | } 2440 | 2441 | TextNode.prototype.clone = function () { 2442 | return this; 2443 | }; 2444 | ``` 2445 | 2446 | ##### [强制] 自定义事件的 `事件名` 必须全小写。 2447 | 2448 | 解释: 2449 | 2450 | 在 JavaScript 广泛应用的浏览器环境,绝大多数 DOM 事件名称都是全小写的。为了遵循大多数 JavaScript 开发者的习惯,在设计自定义事件时,事件名也应该全小写。 2451 | 2452 | ##### [强制] 自定义事件只能有一个 `event` 参数。如果事件需要传递较多信息,应仔细设计事件对象。 2453 | 2454 | 解释: 2455 | 2456 | 一个事件对象的好处有: 2457 | 2458 | 1. 顺序无关,避免事件监听者需要记忆参数顺序。 2459 | 2. 每个事件信息都可以根据需要提供或者不提供,更自由。 2460 | 3. 扩展方便,未来添加事件信息时,无需考虑会破坏监听器参数形式而无法向后兼容。 2461 | 2462 | 2463 | ##### [建议] 设计自定义事件时,应考虑禁止默认行为。 2464 | 2465 | 解释: 2466 | 2467 | 常见禁止默认行为的方式有两种: 2468 | 2469 | 1. 事件监听函数中 return false。 2470 | 2. 事件对象中包含禁止默认行为的方法,如 preventDefault。 2471 | 2472 | 2473 | 2474 | 2475 | ### 3.10 动态特性 2476 | 2477 | 2478 | #### 3.10.1 eval 2479 | 2480 | 2481 | ##### [强制] 避免使用直接 `eval` 函数。 2482 | 2483 | 解释: 2484 | 2485 | 直接 eval,指的是以函数方式调用 eval 的调用方法。直接 eval 调用执行代码的作用域为本地作用域,应当避免。 2486 | 2487 | 如果有特殊情况需要使用直接 eval,需在代码中用详细的注释说明为何必须使用直接 eval,不能使用其它动态执行代码的方式,同时需要其他资深工程师进行 Code Review。 2488 | 2489 | ##### [建议] 尽量避免使用 `eval` 函数。 2490 | 2491 | 2492 | #### 3.10.2 动态执行代码 2493 | 2494 | 2495 | ##### [建议] 使用 `new Function` 执行动态代码。 2496 | 2497 | 解释: 2498 | 2499 | 通过 new Function 生成的函数作用域是全局使用域,不会影响当当前的本地作用域。如果有动态代码执行的需求,建议使用 new Function。 2500 | 2501 | 2502 | 示例: 2503 | 2504 | ```javascript 2505 | var handler = new Function('x', 'y', 'return x + y;'); 2506 | var result = handler($('#x').val(), $('#y').val()); 2507 | ``` 2508 | 2509 | 2510 | 2511 | #### 3.10.3 with 2512 | 2513 | 2514 | ##### [建议] 尽量不要使用 `with`。 2515 | 2516 | 解释: 2517 | 2518 | 使用 with 可能会增加代码的复杂度,不利于阅读和管理;也会对性能有影响。大多数使用 with 的场景都能使用其他方式较好的替代。所以,尽量不要使用 with。 2519 | 2520 | 2521 | 2522 | 2523 | #### 3.10.4 delete 2524 | 2525 | 2526 | ##### [建议] 减少 `delete` 的使用。 2527 | 2528 | 解释: 2529 | 2530 | 如果没有特别的需求,减少或避免使用`delete`。`delete`的使用会破坏部分 JavaScript 引擎的性能优化。 2531 | 2532 | 2533 | ##### [建议] 处理 `delete` 可能产生的异常。 2534 | 2535 | 解释: 2536 | 2537 | 对于有被遍历需求,且值 null 被认为具有业务逻辑意义的值的对象,移除某个属性必须使用 delete 操作。 2538 | 2539 | 在严格模式或IE下使用 delete 时,不能被删除的属性会抛出异常,因此在不确定属性是否可以删除的情况下,建议添加 try-catch 块。 2540 | 2541 | 示例: 2542 | 2543 | ```javascript 2544 | try { 2545 | delete o.x; 2546 | } 2547 | catch (deleteError) { 2548 | o.x = null; 2549 | } 2550 | ``` 2551 | 2552 | 2553 | 2554 | #### 3.10.5 对象属性 2555 | 2556 | 2557 | 2558 | ##### [建议] 避免修改外部传入的对象。 2559 | 2560 | 解释: 2561 | 2562 | JavaScript 因其脚本语言的动态特性,当一个对象未被 seal 或 freeze 时,可以任意添加、删除、修改属性值。 2563 | 2564 | 但是随意地对 非自身控制的对象 进行修改,很容易造成代码在不可预知的情况下出现问题。因此,设计良好的组件、函数应该避免对外部传入的对象的修改。 2565 | 2566 | 下面代码的 selectNode 方法修改了由外部传入的 datasource 对象。如果 datasource 用在其它场合(如另一个 Tree 实例)下,会造成状态的混乱。 2567 | 2568 | ```javascript 2569 | function Tree(datasource) { 2570 | this.datasource = datasource; 2571 | } 2572 | 2573 | Tree.prototype.selectNode = function (id) { 2574 | // 从datasource中找出节点对象 2575 | var node = this.findNode(id); 2576 | if (node) { 2577 | node.selected = true; 2578 | this.flushView(); 2579 | } 2580 | }; 2581 | ``` 2582 | 2583 | 对于此类场景,需要使用额外的对象来维护,使用由自身控制,不与外部产生任何交互的 selectedNodeIndex 对象来维护节点的选中状态,不对 datasource 作任何修改。 2584 | 2585 | ```javascript 2586 | function Tree(datasource) { 2587 | this.datasource = datasource; 2588 | this.selectedNodeIndex = {}; 2589 | } 2590 | 2591 | Tree.prototype.selectNode = function (id) { 2592 | // 从datasource中找出节点对象 2593 | var node = this.findNode(id); 2594 | if (node) { 2595 | this.selectedNodeIndex[id] = true; 2596 | this.flushView(); 2597 | } 2598 | }; 2599 | ``` 2600 | 2601 | 除此之外,也可以通过 deepClone 等手段将自身维护的对象与外部传入的分离,保证不会相互影响。 2602 | 2603 | 2604 | ##### [建议] 具备强类型的设计。 2605 | 2606 | 解释: 2607 | 2608 | - 如果一个属性被设计为 boolean 类型,则不要使用 1 / 0 作为其值。对于标识性的属性,如对代码体积有严格要求,可以从一开始就设计为 number 类型且将 0 作为否定值。 2609 | - 从 DOM 中取出的值通常为 string 类型,如果有对象或函数的接收类型为 number 类型,提前作好转换,而不是期望对象、函数可以处理多类型的值。 2610 | 2611 | 2612 | 2613 | 2614 | 2615 | 2616 | 2617 | 2618 | 2619 | ## 4 浏览器环境 2620 | 2621 | 2622 | 2623 | 2624 | ### 4.1 模块化 2625 | 2626 | 2627 | #### 4.1.1 AMD 2628 | 2629 | 2630 | ##### [强制] 使用 `AMD` 作为模块定义。 2631 | 2632 | 解释: 2633 | 2634 | AMD 作为由社区认可的模块定义形式,提供多种重载提供灵活的使用方式,并且绝大多数优秀的 Library 都支持 AMD,适合作为规范。 2635 | 2636 | 目前,比较成熟的 AMD Loader 有: 2637 | 2638 | - 官方实现的 [requirejs](http://requirejs.org/) 2639 | - 百度自己实现的 [esl](https://github.com/ecomfe/esl) 2640 | 2641 | 2642 | ##### [强制] 模块 `id` 必须符合标准。 2643 | 2644 | 解释: 2645 | 2646 | 模块 id 必须符合以下约束条件: 2647 | 2648 | 1. 类型为 string,并且是由 `/` 分割的一系列 terms 来组成。例如:`this/is/a/module`。 2649 | 2. term 应该符合 [a-zA-Z0-9_-]+ 规则。 2650 | 3. 不应该有 .js 后缀。 2651 | 4. 跟文件的路径保持一致。 2652 | 2653 | 2654 | 2655 | #### 4.1.2 define 2656 | 2657 | 2658 | ##### [建议] 定义模块时不要指明 `id` 和 `dependencies`。 2659 | 2660 | 解释: 2661 | 2662 | 在 AMD 的设计思想里,模块名称是和所在路径相关的,匿名的模块更利于封包和迁移。模块依赖应在模块定义内部通过 local require 引用。 2663 | 2664 | 所以,推荐使用 define(factory) 的形式进行模块定义。 2665 | 2666 | 2667 | 示例: 2668 | 2669 | ```javascript 2670 | define( 2671 | function (require) { 2672 | } 2673 | ); 2674 | ``` 2675 | 2676 | 2677 | ##### [建议] 使用 `return` 来返回模块定义。 2678 | 2679 | 解释: 2680 | 2681 | 使用 return 可以减少 factory 接收的参数(不需要接收 exports 和 module),在没有 AMD Loader 的场景下也更容易进行简单的处理来伪造一个 Loader。 2682 | 2683 | 示例: 2684 | 2685 | ```javascript 2686 | define( 2687 | function (require) { 2688 | var exports = {}; 2689 | 2690 | // ... 2691 | 2692 | return exports; 2693 | } 2694 | ); 2695 | ``` 2696 | 2697 | 2698 | 2699 | 2700 | #### 4.1.3 require 2701 | 2702 | 2703 | ##### [强制] 全局运行环境中,`require` 必须以 `async require` 形式调用。 2704 | 2705 | 解释: 2706 | 2707 | 模块的加载过程是异步的,同步调用并无法保证得到正确的结果。 2708 | 2709 | 示例: 2710 | 2711 | ```javascript 2712 | // good 2713 | require(['foo'], function (foo) { 2714 | }); 2715 | 2716 | // bad 2717 | var foo = require('foo'); 2718 | ``` 2719 | 2720 | ##### [强制] 模块定义中只允许使用 `local require`,不允许使用 `global require`。 2721 | 2722 | 解释: 2723 | 2724 | 1. 在模块定义中使用 global require,对封装性是一种破坏。 2725 | 2. 在 AMD 里,global require 是可以被重命名的。并且 Loader 甚至没有全局的 require 变量,而是用 Loader 名称做为 global require。模块定义不应该依赖使用的 Loader。 2726 | 2727 | 2728 | ##### [强制] Package在实现时,内部模块的 `require` 必须使用 `relative id`。 2729 | 2730 | 解释: 2731 | 2732 | 对于任何可能通过 发布-引入 的形式复用的第三方库、框架、包,开发者所定义的名称不代表使用者使用的名称。因此不要基于任何名称的假设。在实现源码中,require 自身的其它模块时使用 relative id。 2733 | 2734 | 示例: 2735 | 2736 | ```javascript 2737 | define( 2738 | function (require) { 2739 | var util = require('./util'); 2740 | } 2741 | ); 2742 | ``` 2743 | 2744 | 2745 | ##### [建议] 不会被调用的依赖模块,在 `factory` 开始处统一 `require`。 2746 | 2747 | 解释: 2748 | 2749 | 有些模块是依赖的模块,但不会在模块实现中被直接调用,最为典型的是 css / js / tpl 等 Plugin 所引入的外部内容。此类内容建议放在模块定义最开始处统一引用。 2750 | 2751 | 示例: 2752 | 2753 | ```javascript 2754 | define( 2755 | function (require) { 2756 | require('css!foo.css'); 2757 | require('tpl!bar.tpl.html'); 2758 | 2759 | // ... 2760 | } 2761 | ); 2762 | ``` 2763 | 2764 | 2765 | 2766 | ### 4.2 DOM 2767 | 2768 | 2769 | #### 4.2.1 元素获取 2770 | 2771 | 2772 | ##### [建议] 对于单个元素,尽可能使用 `document.getElementById` 获取,避免使用`document.all`。 2773 | 2774 | 2775 | ##### [建议] 对于多个元素的集合,尽可能使用 `context.getElementsByTagName` 获取。其中 `context` 可以为 `document` 或其他元素。指定 `tagName` 参数为 `*` 可以获得所有子元素。 2776 | 2777 | ##### [建议] 遍历元素集合时,尽量缓存集合长度。如需多次操作同一集合,则应将集合转为数组。 2778 | 2779 | 解释: 2780 | 2781 | 原生获取元素集合的结果并不直接引用 DOM 元素,而是对索引进行读取,所以 DOM 结构的改变会实时反映到结果中。 2782 | 2783 | 2784 | 示例: 2785 | 2786 | ```html 2787 |
    2788 | 2789 | 2790 | 2803 | ``` 2804 | 2805 | 2806 | ##### [建议] 获取元素的直接子元素时使用 `children`。避免使用`childNodes`,除非预期是需要包含文本、注释和属性类型的节点。 2807 | 2808 | 2809 | 2810 | 2811 | #### 4.2.2 样式获取 2812 | 2813 | 2814 | ##### [建议] 获取元素实际样式信息时,应使用 `getComputedStyle` 或 `currentStyle`。 2815 | 2816 | 解释: 2817 | 2818 | 通过 style 只能获得内联定义或通过 JavaScript 直接设置的样式。通过 CSS class 设置的元素样式无法直接通过 style 获取。 2819 | 2820 | 2821 | 2822 | 2823 | #### 4.2.3 样式设置 2824 | 2825 | 2826 | ##### [建议] 尽可能通过为元素添加预定义的 className 来改变元素样式,避免直接操作 style 设置。 2827 | 2828 | ##### [强制] 通过 style 对象设置元素样式时,对于带单位非 0 值的属性,不允许省略单位。 2829 | 2830 | 解释: 2831 | 2832 | 除了 IE,标准浏览器会忽略不规范的属性值,导致兼容性问题。 2833 | 2834 | 2835 | 2836 | 2837 | #### 4.2.4 DOM 操作 2838 | 2839 | 2840 | ##### [建议] 操作 `DOM` 时,尽量减少页面 `reflow`。 2841 | 2842 | 解释: 2843 | 2844 | 页面 reflow 是非常耗时的行为,非常容易导致性能瓶颈。下面一些场景会触发浏览器的reflow: 2845 | 2846 | - DOM元素的添加、修改(内容)、删除。 2847 | - 应用新的样式或者修改任何影响元素布局的属性。 2848 | - Resize浏览器窗口、滚动页面。 2849 | - 读取元素的某些属性(offsetLeft、offsetTop、offsetHeight、offsetWidth、scrollTop/Left/Width/Height、clientTop/Left/Width/Height、getComputedStyle()、currentStyle(in IE)) 。 2850 | 2851 | 2852 | ##### [建议] 尽量减少 `DOM` 操作。 2853 | 2854 | 解释: 2855 | 2856 | DOM 操作也是非常耗时的一种操作,减少 DOM 操作有助于提高性能。举一个简单的例子,构建一个列表。我们可以用两种方式: 2857 | 2858 | 1. 在循环体中 createElement 并 append 到父元素中。 2859 | 2. 在循环体中拼接 HTML 字符串,循环结束后写父元素的 innerHTML。 2860 | 2861 | 第一种方法看起来比较标准,但是每次循环都会对 DOM 进行操作,性能极低。在这里推荐使用第二种方法。 2862 | 2863 | 2864 | 2865 | 2866 | #### 4.2.5 DOM 事件 2867 | 2868 | 2869 | ##### [建议] 优先使用 `addEventListener / attachEvent` 绑定事件,避免直接在 HTML 属性中或 DOM 的 `expando` 属性绑定事件处理。 2870 | 2871 | 解释: 2872 | 2873 | expando 属性绑定事件容易导致互相覆盖。 2874 | 2875 | 2876 | ##### [建议] 使用 `addEventListener` 时第三个参数使用 `false`。 2877 | 2878 | 解释: 2879 | 2880 | 标准浏览器中的 addEventListener 可以通过第三个参数指定两种时间触发模型:冒泡和捕获。而 IE 的 attachEvent 仅支持冒泡的事件触发。所以为了保持一致性,通常 addEventListener 的第三个参数都为 false。 2881 | 2882 | 2883 | ##### [建议] 在没有事件自动管理的框架支持下,应持有监听器函数的引用,在适当时候(元素释放、页面卸载等)移除添加的监听器。 2884 | 2885 | 2886 | 2887 | --------------------------------------------------------------------------------