浏览器兼容性规范
前端工程师自测标准
PC端: IE7,IE8,IE9,IE10,IE11及以上,Chrome,Safari,FIREFOX
移动端: ios默认,android默认,QQ Browser,UC
测试工程师测试标准
Pc端: IE7,IE8,IE9,IE10,IE11及以上,Chrome,Safari,FIREFOX, LB BROWSER (猎豹),UC WEB ,奇虎360安全浏览器
移动端: ios默认,android默认,QQ Browser,UC
以上都兼容最新版本,及用户数大的版本,ie,uc系列保证基础功能,平稳退化。
注意事项:
- LB BROWSER (猎豹),UC WEB , 奇虎360安全浏览器,由于是不同公司内嵌主流内核浏览器,可能会有部分功能的差异,不在前端的自测范围内,但由于访问用户数较多,有问题可排查,保证基础功能。测试会按需检查,保证功能稳定。
- 产品及运营如有对其他版本的浏览器要求,需在PRD中浏览器兼容性里说明,否则按此常规标准开发及测试。
- 一账通登录,注册及网银功能区,需兼容ie6。新版本做好文案引导提示或升级提示。做好优雅降级处理。
HTML规范
DOCTYPE
统一使用HTML5标准文档模型 <!DOCTYPE html>
在DOCTYPE文档声明代码前不要加任何代码或空格,以免文档进入其他模式
通用HTML模板
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>页面标题</title> <meta name="description" content="网页简介和说明"/> <meta name="keywords" content="网页关键词"/> <meta name="author" content="网站名/机构名" /> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="renderer" content="webkit"> <link rel="stylesheet" href="xxx.css"/> </head> <body> <script src="xxx.js"></script> </body> </html>
HTML文档编码格式
统一使用UTF-8编码格式
CSS和javascript代码和文件
-
严格做到结构、表现、行为分离的准则,CSS和JS代码不要直接写在HTML里面混搭,尽量外链CSS和JS文件。
-
CSS文件统一外链并放置在head标签内
-
除JS框架文件(如:jquery.js、require.js),其他JS文件统一放在页面底部(body闭合标签上面,如上代码)
如此规范,是出于页面性能方面的考虑,若JS文件放在head里,可能会阻塞页面加载和渲染。
图片文件
-
img标签必须有alt属性和值,值是对图片的正确描述,如:产品名称
- img单标签必须用“/”封闭
推荐:
<img src="xxx.jpg" alt="图片名称" />
标签闭合
任何标签都必须有闭合
不推荐:
<p>balabalabala…… <img src="xxx.jpg" alt="xxx">
推荐:
<p>balabalabala……</p> <img src="xxx.jpg" alt="xxx"/>
标签嵌套
-
避免错误的嵌套
严禁:
<p> <span> 文字文字 </p> </span>
推荐:
<p> <span> 文字文字 </span> </p<
-
避免行级元素内嵌套块级元素
不推荐
<span> <div>文字文字</div> </span> <a href=""> <p>文字文字</p> </a>
推荐:
<span> <span>文字文字</span> </span> <a href=""> <span>文字文字</span> </a>
-
避免行级元素与块级元素在同一个DOM层级
不推荐:
<div class="mod_reg clearfix"> <span class="side_bar">float left</span> <div class="content"> <strong>文字文字</strong> <p>我也是文字</p> </div> </div>
推荐:
<div class="mod_reg"> <div class="side_bar">float left</div> <div class="content"> <p>文字文字</p> <p>我也是文字</p> </div> </div>
表单元素
为表单元素label加上for属性
不推荐:
<input type="radio" name="color" value="0" /><label>选项1</label> <input type="radio" name="color" value="1" /><label>选项2</label>
推荐:
<input type="radio" id="blue" name="color" value="0" /><label for="blue">选项1</label> <input type="radio" id="pink" name="color" value="1" /><label for="pink">选项2</label>
for属性能让点击label标签的时候,同时focus到对应的 input 和 textarea上,增加响应区域
HTML注释
优秀的程序员会写一手漂亮的代码注释,不仅要让机器读懂你的语言,而且要让人能更容易的读懂你的语言
每一个模块都必须加上“开始-结尾”的注释,增强代码可读性,以防后端工程师套错页面
规则:
在模块开始标签之前加<--模块class名-->
在模块结尾标签之后加<--/模块class名-->
类似于HTML标签的语法形式,无需中文,只需要复制模块名字,达到区分模块头尾的目的即可
推荐
<--mod_reg--> <div class="mod_reg"> <div class="side_bar">float left</div> <div class="content"> <p>文字文字</p> <p>我也是文字</p> </div> </div> <--/mod_reg-->
HTML代码其他细节
- 标签必须小写
- 标签上的属性值必须加双引号,必须有值
- 标签必须有封口
- 保持良好的HTML代码层级关系,子级的HTML空一个TAB键
- 拒绝多div癖,多class癖
- 每一对标签另起一行
- 编写语义化的HTML(请参考HTML和CSS设计模式中语义化的章节)
语义化
CSS规范
CSS reset
在开始CSS编码之前,有很多的CSS属性都必须被reset,避免浏览器出现各种兼容性问题。
推荐使用的CSS reset文件:
http://css2.pingan.com/app_css/g/base.css
CSS文件编码
统一使用UTF-8
文件编码方式
CSS文件引用
- 一律使用link的方式调用外部样式
- 不允许在页面中使用
<style>
块; - 不允许在
<style>
块中使用@import
; - 不允许使用
style
属性写行内样式。
一般情况下,在页面中只允许使用 标签来引用CSS文件
CSS文件命名格式
CSS文件命名用下划线做间隔
推荐命名要求:栏目/页面/活动名/类型+HTML页面名/CSS文件功能
- 能描述实际意义的英文单词
- 首字符必须是英文字母,禁用其他符号或数字
- 字母全部小写
- 单词之间用下划线间隔
- 命名要有实际意义,避免使用123,abc等无意义的名字,如test1.css, test2.css, abc.css
推荐
例如两个注册的功能页,其中一个注册表单页,另外一个注册结果页,CSS文件分别命名成register_form.css
和register_result.css
不推荐
register_Form.css
,不推荐这种下划线与驼峰混搭的命名方式,因为加入驼峰并不会给我们代码带来什么好处,而且看起来并不那么fasion和简约
register1.css
register2.css
,1和2不能表示实际意义,不便于后期的拓展性。如果到了下个版本,两个步骤中间再加一个注册步骤呢?再把register2.css
改成register3.css
,再新建一个register2.css
吗?
class命名规则
- 能描述实际意义的英文单词
- 首字符必须是英文字母,禁用其他符号或数字
- 字母全部小写
- 单词之间用下划线间隔
- 不允许使用拼音(约定俗成的除外,如:pingan, youku, baidu),更不允许拼音和英文混搭(如:icon_tupian)
- 命名要有实际意义,避免使用上下左右命名模块名称、颜色名称、数字123、abc等无意义的名字,如.test1, .test2, .abc,.ff6600,left_bar
- 不依据表现形式来命名
- 可根据内容来命名
- 可根据功能来命名
不推荐:
left, right, center, red, black
如果某一天产品经理一拍脑袋,要求你把左边的内容换到右边,中间的换到左边,右边的换到中间,红色换成绿色,黑色换成白色,你是不是就傻了眼?你的class命名还有意义么?
推荐:
nav, aside, news, type, search
为什么是下划线class命名方式?
- 为了与JS变量名驼峰命名方式有所区别,故不选驼峰命名。
减号分隔符用在JS里可能会造成误解和差错
var sub = 1; var nav = 2; var $demo = $("sub-nav");以上javascript代码,如果JQ选择器忘记加引号会是什么结果呢?可想而知,不是我们想要的结果。
- 下划线命名有点好处是:在很多编辑器里双击即可选中,而中划线则无法达到
我们使用中划线 “-” 作为连接字符,而不是下划线 "_"。
本guideline规定用下划线"_"命名间隔,而不是驼峰或中划线"-",各种命名方式都有很多支持者,你肯定有千百个反驳的理由,“规范”是没有绝对权威绝对好的,我们只选择最合适的,请不要花过多时间纠结于此。
class常用命名
页面结构 | |||||||||
---|---|---|---|---|---|---|---|---|---|
头 | 尾 | 侧栏 | 页面主体 | 栏 | 内容 | 容器 | 布局 | 模块 | |
header | footer | sidebar | main | column/col | content/cont | container | layout | module/mod | |
导航/菜单/模块 | |||||||||
导航 | 主导航 | 二级导航 | 置顶导航 | 侧边导航 | 菜单/目录 | 子菜单/二级菜单 | 标题 | 摘要 | |
nav | main_nav | sub_nav | top_nav | side_nav | menu | sub_menu | title | summary | |
功能 | |||||||||
标志 | 一种广告 | 登录 | 注册 | 搜索 | 标题栏 | 加入 | 状态 | 按钮 | 滚动 |
logo | banner | login | regsiter/reg | search | title_bar | join | status | btn | scroll |
标签 | 列表 | 信息 | 当前的 | 提示 | 图标 | 记录 | 详情 | 标准 | 服务 |
tab | list | message/msg | current/cur | tips | icon | note | detail | guild | service |
热点 | 新消息 | 下载 | 投票 | 搭档 | 友链 | 版权 | 信息 | ||
hot | news | download | vote | partner | friend_links | copyright | info | ||
class和ID使用规则
CSS一般只用class,ID留给javascript来调用
CSS编码格式
- 每个选择器后的CSS代码不要换行
- 分号后保持一个空格
- 冒号后保持一个空格
- 如有多个选择器,每个选择器占一行
- 多选择器后的逗号不加空格
不推荐:
h1,h2,h3,h4{font-weight:normal;} .mod_detail{ width:400px; height:300px; } .mod_detail .current,.mod_detail .news,.mod_detail .content{ color:#000; }
推荐:
h1, h2, h3, h4{font-weight:normal;} .mod_detail{width: 400px; height: 300px;} .mod_detail .current, .mod_detail .news, .mod_detail .content{color:#000;}
命名空间前缀
前缀 | 说明 | 示例 |
---|---|---|
mod_ | 模块命名前缀 | mod_nav_bar |
cell_ | 通用元件命名前缀 | cell_news_list |
plug_ | JS插件模块命名,一整套包含HTML、CSS、JS的功能块,最外层DOM请带上命名空间前缀plug | plug_popup, plug_slider |
layout_ | 页面布局框架命名前缀 | layout_col_a |
theme_ | 网站主题样式class命名,适用于多主题类型网站,theme_主题名_拓展名 | theme_qq_bg, theme_qq_color |
js_ | 特为JS调用的命名,可以用于ID和class,不涉及CSS样式 | js_reg_submit 或 jsRegSubmit |
模块化CSS代码,模块内所有元素必须继承自模块的最外层CSS命名空间,模块、元件、布局的命名必须拥有命名空间前缀.这样能有效的避免命名冲突,避免过多的全局class造成全局污染。
推荐:
.mod_detail .info { sRules; } .mod_detail .current { sRules; } .mod_detail .news { sRules; } .mod_detail .content { sRules; }
不推荐:
.info { sRules; } .current { sRules; } .news { sRules; } .content { sRules; }
CSS文件注释
/**
*@Description: 项目名称(可写中文)
*@Author: xukai (建议用英文字母)
*@Last Modify: xukai (2012-05-10)
*/
在CSS文件头部需要注明项目名称和说明、代码作者名、更新时间和作者名
减少使用滤镜和表达式
滤镜非常消耗性能,不推荐使用。
应该遵循一个原则:能用JS解决的问题不用CSS,能用CSS解决的问题不用JS。
CSS内嵌计算和表达式显然不是CSS擅长的事情,不建议写在CSS中,请尽量用JS的方式来解决。
字体不要用中文来定义
用英文字体名称。
例如:font-family:'微软雅黑';
不建议用这种写法,在长久的代码生命周期中,会造成不可预见的问题。比如某个同事某一天用了非UTF-8编码方式,中文可能是个乱码,更新CSS提交后就会造成BUG。
每个class的CSS属性都写在一行
以便于模块化的展现。
例如:
.mod_side_nav{float: left;margin-top: 20px;} .mod_side_nav li{width: 200px; height: 20px;margin-bottom: 8px;} .mod_side_nav li strong{font-size: 14px;} .mod_side_nav a{font-size: 12px; line-height: 20px;display:block;padding-left: 10px;color: #999;border-left: 1px solid #FFF;} .mod_side_nav a:hover{color: #333;border-left: 1px solid #F60;} .mod_side_nav .sub_t{margin-left: 15px;}
如果每个属性换行,就是这样的:
.mod_side_nav {float: left; margin-top: 20px;}
.mod_side_nav li {width: 200px; height: 20px; margin-bottom: 8px;}
.mod_side_nav li strong {font-size: 14px;}
.mod_side_nav a {font-size: 12px; line-height: 20px; display: block; padding-left: 10px; color: #999; border-left: 1px solid #FFF;}
.mod_side_nav a:hover {color: #333; border-left: 1px solid #F60;}
.mod_side_nav .sub_t {margin-left: 15px;}
这样代码的行数就会变的很多,比较难从纵向区分我们的模块化CSS代码。当然,我们会尊重你的个人习惯,如果你坚持觉得换行的写法比较舒服,那就多按回车键换行吧。
CSS模块注释
注释写在对应的模块上方,无换行。注释与上方的CSS模块空一行。
示例:
/**
@Name:模块名称
@Level:级别(Global, Channel, Function)
@Dependent:依赖关系,该模块必须依赖于何种模块
@Sample:用法示例,或指出改模块所作用的直接页面
@Explain:附加说明
@Author:创建者 日期(两位数年月日时)
@Last Modify:最终修改者 日期(两位数年月日时)
*/
.mod_side_nav{float: left;margin-top: 20px;}
.mod_side_nav li{width: 200px; height: 20px;margin-bottom: 8px;}
.mod_side_nav li strong{font-size: 14px;}
.mod_side_nav a{font-size: 12px; line-height: 20px;display:block;padding-left: 10px;color: #999;border-left: 1px solid #FFF;}
.mod_side_nav a:hover{color: #333;border-left: 1px solid #F60;}
.mod_side-nav .sub_t{margin-left: 15px;}
CSS定义的HTML模块是:
CSS属性的书写顺序
按照元素模型由外及内,由整体到细节书写,大致分为五组:
- 位置:position,left,right,float
- 盒模型属性:display,margin,padding,width,height
- 边框与背景:border,background
- 段落与文本:line-height,text-indent,font,color,text-decoration,...
- 其他属性:overflow,cursor,visibility,...
之所以有这样的建议,一是为了统一书写代码的风格,二是能提升CSS渲染页面的效率(待考证)
清浮动CSS
对于IE浏览器使用zoom:1
使盒子触发haslayout,可达到清浮动的目的。
对于非IE浏览器可以结合:after
伪类达到清浮动的目的。
CSS代码:
.mod_demo{zoom:1;} .mod_demo:after{display:block;clear:both;content:"";height:0;}
推荐的做法:
可以结合LESS或SASS预编译语言来编写干净的可维护性的CSS,在LESS里可以创建一个通用的mixin,如:
.clearfix(){ zoom:1; :after{display:block;clear:both;content:"";height:0;} } .mod_demo{ .clearfix; } .mod_header{ .clearfix; } .mod_footer{ .clearfix; }
不推荐做成公用类,将class赋值到HTML标签上,例如:
.clearfix{zoom:1;} .clearfix:after{display:block; clear:both; content:""; height:0;}
<div class="mod_reg clearfix"> <div class="side_bar">float left</div> <div class="content">float right</div> </div>
更不推荐在HTML中添加空标签来清浮动,糟糕的做法:
.clear{clear:both;}
<div class="mod_reg"> <div class="side_bar">float left</div> <div class="content">float right</div> <div class="clear"></div> </div>
这样会“污染”HTML,给HTML添加无意义的空标签。
CSS hack
- 尽量不好使用hack,另可换一种方法来实现
- 如果hack费用不可,请用一种比较靠谱的hack
使用hack,向后兼容性比较差,hack可能会被浏览器更新版本后修复
常用CSS hack:
.test { color: #000; /* 所有浏览器 */ color: #111\9; /* 所有IE */ color: #222\0; /* IE8 及以上版本, 非Webkit内核的Opera */ color: #333\9\0; /* IE8 及以上版本 */ color: #444\0/; /* IE8 及以上版本 */ *color: #666; /* IE7和IE6 */ _color: #777; /* IE6 */ }
IE条件注释:
<!--[if IE]>用于 IE <![endif]--> <!--[if IE 6]>用于 IE6 <![endif]--> <!--[if IE 7]>用于 IE7 <![endif]--> <!--[if IE 8]>用于 IE8 <![endif]--> <!--[if IE 9]>用于 IE9 <![endif]--> <!--[if gt IE 6]>用于 IE6 以上版本<![endif]--> <!--[if lte IE 7]>用于 IE7或更低版本 <![endif]--> <!--[if gte IE 8]>用于 IE8 或更高版本 <![endif]--> <!--[if lt IE 9]>用于 IE9 以下版本<![endif]--> <!--[if !IE]> -->用于非 IE <!-- <![endif]-->
- lt :就是Less than的简写,也就是小于的意思。
- lte :就是Less than or equal to的简写,也就是小于或等于的意思。
- gt :就是Greater than的简写,也就是大于的意思。
- gte:就是Greater than or equal to的简写,也就是大于或等于的意思。
- !:就是不等于的意思,跟javascript里的不等于判断符相同。
合理使用CSS选择器
避免使用标签+class/ID类型的选择器
避免画蛇添足:
.mod_news div.hot{color: #F00;} .mod_news li#list{color: #F00}
推荐化繁为简:
.mod_news .hot{color: #F00;} .mod_news #list{color: #F00} //规范规定CSS不要用ID命名,这里只是做个示例
避免选择器嵌套层级过深,建议控制在三层以内
不推荐:
.mod_news .content .box .list .tips{color: #F00}
推荐:
.mod_news .content .tips{color: #F00}
属性缩写与拆分
不推荐:
.mod_news{margin-left: 10px; margin-right: 20px; margin-bottom: 30px; margin-left: 40px;
推荐
.mod_news{margin:10px 20px 30px 40px;}
不推荐:
.mod_news{border-width: 1px; border-style: solid; border-color: #000 #000 #F60;}
推荐:
.mod_news{border: 1px solid #000; border-bottom-color: #F60;}
背景图片
小图标必须合并
通过CSS Sprite的方法将小图片合并成一张大背景图,减少HTTP请求数达到性能优化的目的,建议你使用工程化的方法,FIS工程化中自带的小图片自动合并优化功能,将为你减少很多工作量,给后期维护也带来许多方便。
图片格式选择与优化
- 色彩丰富的图片使用JPG格式,用photoshop输出,压缩比设置60-70%左右,保持不明显影响视觉效果为准
- 色彩不丰富的图片,优先使用PNG8格式。
- 除非是gif动画图片,请尽量少使用gif格式。
- 对于使用到半透明滤镜的图片才使用PNG24格式,但要注意IE6的兼容性
- 合并后图片大小不宜超过50K,建议大小在20K-50K之间
编后语
少说话,多做事,才能保持团队工作的高效。少说话——意思是减少无必要的沟通成本,就需要团队成员之间形成更多默契,默契就从每位工程师遵守一致的规范而来。无需沟通就很熟悉他人的代码编写习惯,就像是自己写的,不至于看到不喜欢的代码而反感和抗拒。遵守一致的规范,可有效减少沟通成本,提升代码质量,提升可维护性。编写规范、整洁、漂亮代码是我们一致的目标。
javascript规范
类型
-
原始值: 当你使用一个变量的原始值,赋值到新变量时
string字符串
number数字型
boolean布尔型
null空值
undefined不存在型
var foo = 1; var bar = foo; bar = 9; console.log(foo, bar); // => 1, 9
-
复杂类型: 当你把一个数组赋值到新变量上并改变数组内成员的值
object对象
array数组
function函数
var foo = [1, 2]; var bar = foo; bar[0] = 9; console.log(foo[0], bar[0]); // => 9, 9
对象
-
使用字面量的方法创建对象
// 不推荐 var item = new Object(); // 推荐 var item = {};
-
不要使用 javascript保留关键字作为key值. IE8不支持. 更多参考信息.
// 不推荐 var superman = { default: { clark: 'kent' }, private: true }; // 推荐 var superman = { defaults: { clark: 'kent' }, hidden: true };
-
使用意义接近的单词替代保留关键字
// 不推荐 var superman = { class: 'alien' }; // 不推荐 var superman = { klass: 'alien' }; // 推荐 var superman = { type: 'alien' };
数组
-
使用字面量的方法创建对象
// 不推荐 var items = new Array(); // 推荐 var items = [];
-
使用数组对象中push的方法替代直接增加数组成员的方法
var someStack = []; // 不推荐 someStack[someStack.length] = 'abracadabra'; // 推荐 someStack.push('abracadabra');
-
当你需要复制一个数组时请使用slice方法 参考
var len = items.length; var itemsCopy = []; var i; // 不推荐 for (i = 0; i < len; i++) { itemsCopy[i] = items[i]; } // 推荐 itemsCopy = items.slice();
-
使用slice方法将类似数组一样的对象转换成数组
function trigger() { var args = Array.prototype.slice.call(arguments); ... }
字符串
-
用单引号
''
创建字符串.// 不推荐 var name = "Bob Parr"; // 推荐 var name = 'Bob Parr'; // 不推荐 var fullName = "Bob " + this.lastName; // 推荐 var fullName = 'Bob ' + this.lastName;
建议字符长度超过80的字符串,拆分成小于80长度的字符串,使用字符串拼接的方法拼接
-
提示: 如果大量使用长字符串拼接,会对性能造成影响 jsPerf & Discussion.
// 不推荐 var errorMessage = 'This is a super long error that was thrown because of Batman. When you stop to think about how Batman had anything to do with this, you would get nowhere fast.'; // 不推荐 var errorMessage = 'This is a super long error that was thrown because \ of Batman. When you stop to think about how Batman had anything to do \ with this, you would get nowhere \ fast.'; // 推荐 var errorMessage = 'This is a super long error that was thrown because ' + 'of Batman. When you stop to think about how Batman had anything to do ' + 'with this, you would get nowhere fast.';
-
建议通过使用join方法来连接字符串,而不要直接将字符串拼接,特别是在IE下性能比较低: jsPerf.
var items; var messages; var length; var i; messages = [{ state: 'success', message: 'This one worked.' }, { state: 'success', message: 'This one worked as well.' }, { state: 'error', message: 'This one did not work.' }]; length = messages.length; // 不推荐 function inbox(messages) { items = '<ul>'; for (i = 0; i < length; i++) { items += '<li>' + messages[i].message + '</li>'; } return items + '</ul>'; } // 推荐 function inbox(messages) { items = []; for (i = 0; i < length; i++) { items[i] = '<li>' + messages[i].message + '</li>'; } return '<ul>' + items.join('') + '</ul>'; }
函数
-
函数表达式:
// 匿名函数表达式 var anonymous = function() { return true; }; // 有名函数表达式 var named = function named() { return true; }; // 自调用函数表达式 (function() { console.log('Welcome to the Internet. Please follow me.'); })();
绝对不要在非函数内直接定义一个函数,建议你将定义的函数赋给一个变量,虽然两种做法都可行,但在浏览器里解析是不一样的。
-
注意: ECMA-262 定义
块
型代码为一组一句. 函数声明不是一个语句. Read ECMA-262's note on this issue.// 不推荐 if (currentUser) { function test() { console.log('Nope.'); } } // 推荐 var test; if (currentUser) { test = function test() { console.log('Yup.'); }; }
-
绝对不要将参数命名为
arguments
,在作用域内的其他函数传参会有问题// 不推荐 function nope(name, options, arguments) { // ...stuff... } // 推荐 function yup(name, options, args) { // ...stuff... }
对象的属性
-
使用
.
表示法来访问对象的成员.var luke = { jedi: true, age: 28 }; // 不推荐 var isJedi = luke['jedi']; // 推荐 var isJedi = luke.jedi;
-
当对象的键是参数变量的时候,请使用
[]
的表示法访问对象的成员var luke = { jedi: true, age: 28 }; function getProp(prop) { return luke[prop]; } var isJedi = getProp('jedi');
变量
-
一定要使用
var
来声明变量.否则会导致全局变量污染.应该避免这样对全局命名空间污染.// 不推荐 superPower = new SuperPower(); // 推荐 var superPower = new SuperPower();
-
使用一个
var
只声明一个变量, 你绝对不用担心把 逗号,
和分号;
张冠李戴了.// 不推荐 var items = getItems(), goSportsTeam = true, dragonball = 'z'; // 不推荐 // (比较上面的代码,范了个错误) var items = getItems(), goSportsTeam = true; dragonball = 'z'; // 推荐 var items = getItems(); var goSportsTeam = true; var dragonball = 'z';
-
将未赋值的变量放在末尾,当需要用到前面已赋值变量就会很有帮助。
// 不推荐 var i, len, dragonball, items = getItems(), goSportsTeam = true; // 不推荐 var i; var items = getItems(); var dragonball; var goSportsTeam = true; var len; // 推荐 var items = getItems(); var goSportsTeam = true; var dragonball; var length; var i;
-
把变量声明在它的作用域顶部,有助于避免变量声明和赋值产生相关问题。
// 不推荐 function() { test(); console.log('doing stuff..'); //..other stuff.. var name = getName(); if (name === 'test') { return false; } return name; } // 推荐 function() { var name = getName(); test(); console.log('doing stuff..'); //..other stuff.. if (name === 'test') { return false; } return name; } // 不推荐 function() { var name = getName(); if (!arguments.length) { return false; } return true; } // 推荐 function() { if (!arguments.length) { return false; } var name = getName(); return true; }
Hoisting
-
Variable declarations get hoisted to the top of their scope, their assignment does not.
// we know this wouldn't work (assuming there // is no notDefined global variable) function example() { console.log(notDefined); // => throws a ReferenceError } // creating a variable declaration after you // reference the variable will work due to // variable hoisting. Note: the assignment // value of `true` is not hoisted. function example() { console.log(declaredButNotAssigned); // => undefined var declaredButNotAssigned = true; } // The interpreter is hoisting the variable // declaration to the top of the scope, // which means our example could be rewritten as: function example() { var declaredButNotAssigned; console.log(declaredButNotAssigned); // => undefined declaredButNotAssigned = true; }
-
Anonymous function expressions hoist their variable name, but not the function assignment.
function example() { console.log(anonymous); // => undefined anonymous(); // => TypeError anonymous is not a function var anonymous = function() { console.log('anonymous function expression'); }; }
-
Named function expressions hoist the variable name, not the function name or the function body.
function example() { console.log(named); // => undefined named(); // => TypeError named is not a function superPower(); // => ReferenceError superPower is not defined var named = function superPower() { console.log('Flying'); }; } // the same is true when the function name // is the same as the variable name. function example() { console.log(named); // => undefined named(); // => TypeError named is not a function var named = function named() { console.log('named'); } }
-
Function declarations hoist their name and the function body.
function example() { superPower(); // => Flying function superPower() { console.log('Flying'); } }
For more information refer to JavaScript Scoping & Hoisting by Ben Cherry.
条件语句和等号运算符
- 合理使用
===
和!==
以及==
和!=
. -
条件表达式的强制类型转换遵循以下规则:
- 对象 被计算为 true
- Undefined 被计算为 false
- Null 被计算为 false
- 布尔值 被计算为 布尔的值
- 数字 如果是 +0, -0, or NaN 被计算为 false , 否则为 true
-
字符串 如果是空字符串
''
则被计算为 false, 否则为 true
if ([0]) { // 返回true // 数组就是一个对象, 对象的返回值是true }
-
使用快捷写法.
// 不推荐 if (name !== '') { // ...stuff... } // 推荐 if (name) { // ...stuff... } // 不推荐 if (collection.length > 0) { // ...stuff... } // 推荐 if (collection.length) { // ...stuff... }
请参考更多资料 Truth Equality and JavaScript
代码块
-
多行代码块用大括号.
// 不推荐 if (test) return false; // 推荐 if (test) return false; // 推荐 if (test) { return false; } // 不推荐 function() { return false; } // 推荐 function() { return false; }
If you're using multi-line blocks with
if
andelse
, putelse
on the same line as yourif
block's closing brace.-
当你代码中使用了
if
和else
语句,请把else放在if语句封口大括号的同一行.// 不推荐 if (test) { thing1(); thing2(); } else { thing3(); } // 推荐 if (test) { thing1(); thing2(); } else { thing3(); }
注释
-
使用
/** ... */
写多行注释. I包括描述,指定类型以及所有的参数值和返回值.// 不推荐 // make() returns a new element // based on the passed in tag name // // @param {String} tag // @return {Element} element function make(tag) { // ...stuff... return element; } // 推荐 /** * make() returns a new element * based on the passed in tag name * * @param {String} tag * @return {Element} element */ function make(tag) { // ...stuff... return element; }
-
使用
//
写简单的单行注释. 单独占一行,写在需要评论的对象上面,注释和代码块前后空一行.// 不推荐 var active = true; // 当前的TAB // 推荐 // 当前的TAB var active = true; // 不推荐 function getType() { console.log('fetching type...'); // set the default type to 'no type' var type = this._type || 'no type'; return type; } // 推荐 function getType() { console.log('fetching type...'); // set the default type to 'no type' var type = this._type || 'no type'; return type; }
如果你有一个问题需要重新来看一下或如果你建议一个需要被实现的解决方法的话需要在你的注释前面加上
FIXME
或TODO
帮助其他人迅速理解-
使用
// FIXME:
来注释问题.function Calculator() { // FIXME: 这里不能使用全局变量 total = 0; return this; }
-
使用
// TODO:
注释问题的解决方法.function Calculator() { // TODO: total should be configurable by an options param this.total = 0; return this; }
空格
-
设置TAB贱为2个空格.
// 不推荐 function() { ∙∙∙∙var name; } // 不推荐 function() { ∙var name; } // 推荐 function() { ∙∙var name; }
-
大括号前加个空格.
// 不推荐 function test(){ console.log('test'); } // 推荐 function test() { console.log('test'); } // 不推荐 dog.set('attr',{ age: '1 year', breed: 'Bernese Mountain Dog' }); // 推荐 dog.set('attr', { age: '1 year', breed: 'Bernese Mountain Dog' });
Place 1 space before the opening parenthesis in control statements (
if
,while
etc.). Place no space before the argument list in function calls and declarations.// 不推荐 if(isJedi) { fight (); } // 推荐 if (isJedi) { fight(); } // 不推荐 function fight () { console.log ('Swooosh!'); } // 推荐 function fight() { console.log('Swooosh!'); }
-
Set off operators with spaces.
// 不推荐 var x=y+5; // 推荐 var x = y + 5;
- 语句的末尾添加回车换行符
// 不推荐 (function(global) { // ...stuff... })(this);
// 不推荐 (function(global) { // ...stuff... })(this);↵ ↵
// 推荐 (function(global) { // ...stuff... })(this);↵
-
写JQuery长方法链,每个方法占一行,代码要缩进保持良好可读性和美观.
// 不推荐 $('#items').find('.selected').highlight().end().find('.open').updateCount(); // 不推荐 $('#items'). find('.selected'). highlight(). end(). find('.open'). updateCount(); // 推荐 $('#items') .find('.selected') .highlight() .end() .find('.open') .updateCount(); // 不推荐 var leds = stage.selectAll('.led').data(data).enter().append('svg:svg').classed('led', true) .attr('width', (radius + margin) * 2).append('svg:g') .attr('transform', 'translate(' + (radius + margin) + ',' + (radius + margin) + ')') .call(tron.led); // 推荐 var leds = stage.selectAll('.led') .data(data) .enter().append('svg:svg') .classed('led', true) .attr('width', (radius + margin) * 2) .append('svg:g') .attr('transform', 'translate(' + (radius + margin) + ',' + (radius + margin) + ')') .call(tron.led);
-
在代码块后面,表达式前面,单独空一行
// 不推荐 if (foo) { return bar; } return baz; // 推荐 if (foo) { return bar; } return baz; // 不推荐 var obj = { foo: function() { }, bar: function() { } }; return obj; // 推荐 var obj = { foo: function() { }, bar: function() { } }; return obj;
逗号
-
不要将逗号放在最前
// 不推荐 var story = [ once , upon , aTime ]; // 推荐 var story = [ once, upon, aTime ]; // 不推荐 var hero = { firstName: 'Bob' , lastName: 'Parr' , heroName: 'Mr. Incredible' , superPower: 'strength' }; // 推荐 var hero = { firstName: 'Bob', lastName: 'Parr', heroName: 'Mr. Incredible', superPower: 'strength' };
-
Additional trailing comma: Nope. This can cause problems with IE6/7 and IE9 if it's in quirksmode. Also, in some implementations of ES3 would add length to an array if it had an additional trailing comma. This was clarified in ES5 (source):
Edition 5 clarifies the fact that a trailing comma at the end of an ArrayInitialiser does not add to the length of the array. This is not a semantic change from Edition 3 but some implementations may have previously misinterpreted this.
-
不要加多余的逗号,这可能会在IE下引起错误,同时如果多一个逗号某些ES3的实现会计算多数组的长度。
// 不推荐 var hero = { firstName: 'Kevin', lastName: 'Flynn', }; var heroes = [ 'Batman', 'Superman', ]; // 推荐 var hero = { firstName: 'Kevin', lastName: 'Flynn' }; var heroes = [ 'Batman', 'Superman' ];
分号
-
语句末尾一定要加分号
// 不推荐 (function() { var name = 'Skywalker' return name })() // 推荐 (function() { var name = 'Skywalker'; return name; })(); // 推荐 (guards against the function becoming an argument when two files with IIFEs are concatenated) ;(function() { var name = 'Skywalker'; return name; })();
类型转换
- 在语句的开始执行类型转换.
-
字符串:
// => this.reviewScore = 9; // 不推荐 var totalScore = this.reviewScore + ''; // 推荐 var totalScore = '' + this.reviewScore; // 不推荐 var totalScore = '' + this.reviewScore + ' total score'; // 推荐 var totalScore = this.reviewScore + ' total score';
-
对数字使用
parseInt
并且总是带上类型转换的进制数.var inputValue = '4'; // 不推荐 var val = new Number(inputValue); // 不推荐 var val = +inputValue; // 不推荐 var val = inputValue >> 0; // 不推荐 var val = parseInt(inputValue); // 推荐 var val = Number(inputValue); // 推荐 var val = parseInt(inputValue, 10);
-
If for whatever reason you are doing something wild and
parseInt
is your bottleneck and need to use Bitshift for performance reasons, leave a comment explaining why and what you're doing.// 推荐 /** * parseInt was the reason my code was slow. * Bitshifting the String to coerce it to a * Number made it a lot faster. */ var val = inputValue >> 0;
-
Note: Be careful when using bitshift operations. Numbers are represented as 64-bit values, but Bitshift operations always return a 32-bit integer (source). Bitshift can lead to unexpected behavior for integer values larger than 32 bits. Discussion. Largest signed 32-bit Int is 2,147,483,647:
2147483647 >> 0 //=> 2147483647 2147483648 >> 0 //=> -2147483648 2147483649 >> 0 //=> -2147483647
-
布尔值:
var age = 0; // 不推荐 var hasAge = new Boolean(age); // 推荐 var hasAge = Boolean(age); // 推荐 var hasAge = !!age;
命名约定
-
避免无意义的单个字母,要求有语义化的命名.
// 不推荐 function q() { // ...stuff... } // 推荐 function query() { // ..stuff.. }
-
当命名对象、函数和实例时,使用驼峰命名法,且命名在上下文中有实际意义,增强代码可读性.
// 不推荐 var OBJEcttsssss = {}; var this_is_my_object = {}; function c() {} var u = new user({ name: 'Bob Parr' }); // 推荐 var thisIsMyObject = {}; function thisIsMyFunction() {} var user = new User({ name: 'Bob Parr' });
-
当命名构造函数时,使用驼峰式命名,且首字母大写
// 不推荐 function user(options) { this.name = options.name; } var bad = new user({ name: 'nope' }); // 推荐 function User(options) { this.name = options.name; } var good = new User({ name: 'yup' });
-
- 私有属性命名,名称前加下划线
_
.
// 不推荐 this.__firstName__ = 'Panda'; this.firstName_ = 'Panda'; // 推荐 this._firstName = 'Panda';
- 私有属性命名,名称前加下划线
-
当
this
需要传值的时候,使用名称_this
,好有意义!// 不推荐 function() { var self = this; return function() { console.log(self); }; } // 不推荐 function() { var that = this; return function() { console.log(that); }; } // 推荐 function() { var _this = this; return function() { console.log(_this); }; }
-
给每个函数命名,避免匿名函数. 这样有助于在控制台调试和跟踪.
// 不推荐 var log = function(msg) { console.log(msg); }; // 推荐 var log = function logText(msg) { console.log(msg); };
Note: IE8 and below exhibit some quirks with named function expressions. See http://kangax.github.io/nfe/ for more info.
-
保持引用的文件名与接口名称一致。
// file contents class CheckBox { // ... } module.exports = CheckBox; // in some other file // 不推荐 var CheckBox = require('./checkBox'); // 不推荐 var CheckBox = require('./check_box'); // 推荐 var CheckBox = require('./CheckBox');
存取器
- 属性的存取器函数不是必需的.
-
如果你需要创建存取器请把他们命名为getVal() and setVal('hello').使用get和set单词,
get+什么
,set+什么
方式命名.// 不推荐 dragon.age(); // 推荐 dragon.getAge(); // 不推荐 dragon.age(25); // 推荐 dragon.setAge(25);
-
如果属性是一个布尔值,请命名为isVal() 或 hasVal().使用is和has单词,
get+什么
,set+什么
方式命名.// 不推荐 if (!dragon.age()) { return false; } // 推荐 if (!dragon.hasAge()) { return false; }
-
可以创建get()和set()函数,但是要保持一致.
function Jedi(options) { options || (options = {}); var lightsaber = options.lightsaber || 'blue'; this.set('lightsaber', lightsaber); } Jedi.prototype.set = function(key, val) { this[key] = val; }; Jedi.prototype.get = function(key) { return this[key]; };
构造器
-
Assign methods to the prototype object, instead of overwriting the prototype with a new object. Overwriting the prototype makes inheritance impossible: by resetting the prototype you'll overwrite the base!
function Jedi() { console.log('new jedi'); } // 不推荐 Jedi.prototype = { fight: function fight() { console.log('fighting'); }, block: function block() { console.log('blocking'); } }; // 推荐 Jedi.prototype.fight = function fight() { console.log('fighting'); }; Jedi.prototype.block = function block() { console.log('blocking'); };
-
方法可以返回
this
帮助方法可链。// 不推荐 Jedi.prototype.jump = function() { this.jumping = true; return true; }; Jedi.prototype.setHeight = function(height) { this.height = height; }; var luke = new Jedi(); luke.jump(); // => true luke.setHeight(20); // => undefined // 推荐 Jedi.prototype.jump = function() { this.jumping = true; return this; }; Jedi.prototype.setHeight = function(height) { this.height = height; return this; }; var luke = new Jedi(); luke.jump() .setHeight(20);
-
可以写一个与JS语言自带的同名toString()方法,但是确保它工作正常并且不会有副作用,比如让它继承在某个对象下面。
function Jedi(options) { options || (options = {}); this.name = options.name || 'no name'; } Jedi.prototype.getName = function getName() { return this.name; }; Jedi.prototype.toString = function toString() { return 'Jedi - ' + this.getName(); };
事件
- 当给事件附加数据时,传入一个哈希而不是原始值,这可以让后面的贡献者加入更多数据到事件数据里而不用找出并更新那个事件的事件处理器
// 不推荐
$(this).trigger('listingUpdated', listing.id);
...
$(this).on('listingUpdated', function(e, listingId) {
// do something with listingId
});
prefer:
// 推荐
$(this).trigger('listingUpdated', { listingId : listing.id });
...
$(this).on('listingUpdated', function(e, data) {
// do something with data.listingId
});
模块
- 模块应该以
!
开始,这保证了如果一个有问题的模块忘记包含最后的分号在合并后不会出现错误 - 这个文件应该以驼峰命名,并在同名文件夹下,同时导出的时候名字一致
- 加入一个名为noConflict()的方法来设置导出的模块为之前的版本并返回它
-
总是在模块顶部声明
'use strict';
// fancyInput/fancyInput.js !function(global) { 'use strict'; var previousFancyInput = global.FancyInput; function FancyInput(options) { this.options = options || {}; } FancyInput.noConflict = function noConflict() { global.FancyInput = previousFancyInput; return FancyInput; }; global.FancyInput = FancyInput; }(this);
jQuery
-
JQuery对象使用
$
做为变量前缀// 不推荐 var sidebar = $('.sidebar'); // 推荐 var $sidebar = $('.sidebar');
- 缓存Jquery查询的对象到一个变量中.
// 不推荐 function setSidebar() { $('.sidebar').hide(); // ...stuff... $('.sidebar').css({ 'background-color': 'pink' }); } // 推荐 function setSidebar() { var $sidebar = $('.sidebar'); $sidebar.hide(); // ...stuff... $sidebar.css({ 'background-color': 'pink' }); }
For DOM queries use Cascading
$('.sidebar ul')
or parent > child$('.sidebar > ul')
. jsPerf- Use
find
with scoped jQuery object queries. - 对DOM查询使用级联的
$('.sidebar ul')
或$('.sidebar ul')
,jsPerf -
对有作用域的jQuery对象查询使用
find
// 不推荐 $('ul', '.sidebar').hide(); // 不推荐 $('.sidebar').find('ul').hide(); // 推荐 $('.sidebar ul').hide(); // 推荐 $('.sidebar > ul').hide(); // 推荐 $sidebar.find('ul').hide();
ECMAScript 5 Compatibility
- Refer to Kangax's ES5 compatibility table.