代码风格
命名
1. 为方法、变量取一个好名字, 使代码易于理解
2. 文件中的私有属性和方法名应该以“__“开头
3. 常量定义全部大写, 并用下划线分隔单词
4. 方法的命名, 用动词和动宾结构, 并采用首字母小写的驼峰命名法。
说明:
get + 非布尔属性名()
is + 布尔属性名()
set + 属性名()
has + 名词/形容词()
动词()
动词 + 宾语()
5. 为保证可读性, 方法名不宜过长
6. 函数名、属性名遵循驼峰命名风格
7. 为全局属性、函数使用命名空间
8. 不要采用保留字作为键值或变量名, 如果确实需要, 请用保留字的同义词
9. jQuery类型的变量以$开头
注释
- 尽量让代码来解释自己
- 注释应解释代码的意图, 而不是描述代码怎么实现的
- 保证注释与代码一致, 避免产生误导
- 注释应与其描述代码位置相邻, 放在所注释代码的上方或右方, 并与代码采用同样的缩进
- 注释和上面的代码块要有空行, 注释的//和注释内容要有一个空格
- 不要用注释保留废弃的代码
- 不要用注释记录修改日志
- 一般单行用
//
块用/* */
文档注释用/** */
排版
1. 团队应使用一致的排版风格
2. 将排版风格固化到IDE的代码格式化配置文件中, 并让整个团队使用
3. 在不同概念之间 , 增加空行
4. 将逻辑紧密相关的代码放在一起
5. 控制一行的宽度, 不要超过120个字符
6. 在不同的概念间(关键字, 变量, 操作符)增加空格, 以便清楚区分概念。
7. 统一采用4空格缩进
8. 数组和对象初始化, 如果初始值不是很长, 就保持写在单行上。
9. 给if, for, do, while, switch等语句的执行体加大括号{}
10. 控制文件的长度, 最好不要超过500行
11. 如果参数中有匿名函数, 函数体从调用该函数的左边开始缩进4个空格, 而不是从function这个关键字开始, 这让匿名函数更加易读
12. 二元和三元操作符始终跟随着前行, 如果一行实在放不下, 按上述的缩进风格进行换行
13. 字符串优先使用单引号
14. 使用方法链时进行缩进, 同时使用前面加点, 强调这是方法调用而不是语句, 如:
$(".item")
.find("tr")
.end()
.find(".open");
15. 花括号和语句在同一行。
16. 花括号前加一个空格
17. 在控制语句(if, while等)的括号前放一个空格, 在函数调用及声明中, 不在函数的参数列表前加空格。
方法
1. 方法设计的第一原则是要短小
2. 方法设计要遵循单一职责原则(SRP), 一个方法仅完成一个功能
3. 方法设计应遵循单一抽象层次原则(SLAP)
SLAP原则, 是指一个方法中所有的操作处于相同的操作层, 否则, 跳跃的代码的抽象层次破坏了代码的流畅性。
4. 不要把方法的入参当做工作变量/临时变量, 除非特别需要
说明:
- 每个变量/参数都有自己独特的功能, 让一个变量承担多个职责, 变量名无法清晰表达其功能, 会使程序无法理解。
- 如果参数是传引用方式的, 则方法内对参数的修改, 会传递到方法外, 造成意外的错误, 如传引用时, 不想方法内修改入参的, 建议在参数前加final关键字。
5. 方法的参数个数不宜过多
如果参数超过了7个, 则维护的难度很大, 建议减少参数个数。 如果多个参数同时多次出现在多个方法中, 说明这些参数紧密相关, 可以将它们封装到一个对象中。
语言特性
1. 定义类时, 尽量在原型下定义方法, 在构造函数内定义属性, 从而最大程度发挥JavaScript引擎优化机制
说明: 原型可以降低内在占用, 提高运行效率。
2. 向数组添加元素时, 使用Array.push替代直接赋值
3. 有替代方案时, 禁用使用eval方法
说明:
- eval接受一个参数content, 如果content不是字符串, 则直接返回content, 否则执行content语句, 如果content语句执行结果是一个值, 则返回此值, 否则,返回undefined, 这让程序比较混乱, 导致可读性差。
- 当eval()里面包含用户输入的话, 存在安全风险, 可以用其它更佳、更清晰、更安全的方式来写代码。
4. 禁止使用with(){}
使用with让你的代码在语义上变得不清晰, 因为with的对象可能与局部变量产生冲突, 从而改变你程序原本的用义。
5. 仅在对象构造器、方法和闭包中使用this
- 在JavaScript里面, this指针代表的是执行当前代码对象的所有者, this语义很特别:
- 全局对象(大多数情况下)
- 调用者的作用域(使用eval时)
- DOM树中的节点(添加事件处理函数时)
- 新创建的对象(在构造器时)
- 其他对象(如果函数被call()或apply())
- 使用this时很容易出错, 所有只有在下面两种情况下使用:
- 在构造器中
- 对象的方法(包括创建的闭包)中
6. 块内函数必须使用函数表达式声明, 块内变量不能与函数内的其他变量同名
7. 使用JSON.parse方法来分析JSON字符串
8. 每句代码后必须加分号(;)
9. 使用JavaScript字面量而不是封装基本类
10. 禁止修改内置对象的原型
11. JavaScript与HTML分离
12. 声明变量必须加var关键字
13. 在函数内部使用”use strict"
说明:
- 消除JavaScript语法的一些不合理、不严谨之处, 减少一些怪异行为
- 消除代码运行的一些不安全之处, 保证代码运行的安全
- 提高编译器效率, 提高运行速度
- 为未来新版本的JavaScript做好铺垫
在开启“use strict"
后, 以下情况在运行脚本前会抛出SyntaError异常
- 八进制语法:
var n = 023
或var s = \047
- with语句
- 使用delete删除一个变量名或函数(不是属性名)
- 使用eval或arguments作为变量名或函数名
- 使用未来保留字
- 在语句块使用函数声明, 如
if(a>b){function f(){}}
- 对象字面量中使用相同的属性名
- 函数形参中使用多个相同的参数名
14. 访问外部对象或外部对象属性时, 必须先判断是否为空
15. 判断相等时, 使用===
和!==
16. 使用for/in循环必须结合实际具体应用场景, 正确使用hasOwnProperty方法
说明:
- for/in主要用于遍历对象的属性, 但不包括对象的内置成员(如toString, valueOf)
- 如果重写了Object的内置属性, 使用for/in遍历在不同浏览器上的结果是不同的
- 如果往原生对象上增加方法或属性, 会被遍历出来, 如果不是业务需要遍历property内的属性, 都要使用hasOwnProperty方法来提高代码的健壮性
- 遍历数组禁止使用for/in做遍历, 请用普通的for循环来做遍历。
17. 尽量避免给数组添加属性
18. 推荐使用简写的条件表达式
19. 不要写复杂的表达式
20. 采用括号明确计算的优先级
21. 在switch语句的每一个case和default中都放置一条break语句。
22. var声明会被提升至该作用域顶部, 所以声明变量应该写在作用域顶部(因为即使var声明不在顶部, 其实也会默认提升到作用域顶部, 所以显式地写在顶部可读性更强).
性能编程规范
1. 缓存jQuery查询结果
使用JavaScript访问DOM元素是比较慢的, 所以使用选择器的次数应该越少越好, 并且尽可能缓存选中的结果, 便于后续反复使用
2. 避免使用所有元素选择器$("*")
3. 优先使用ID选择器, 尽量给选择器指定上下文context
jQuery选择器性能最佳到最差如下:
- id选择器
- 元素选择器
- 类选择器
- 属性选择器
- 伪类选择器
4. 循环中减少DOM操作
5. 利用事件代理(冒泡)机制
每个JavaScript事件(如click)都会冒泡到父级节点, 当需要给多个元素调用同一个函数时, 这点很有用.
6. 尽量不要使用同步的ajax
同步的ajax请求会阻塞界面的渲染, 让界面产生卡顿情况
内存编程规范
1. 在满足业务特性需求的情况下, 减少DOM对象的动态创建和删除.
2. 禁止在循环、事件操作回调中使用文本+变量拼接的方式使用jQuery创建DOM。
说明:
jQuery在内部实现中为了提高频繁的DOM创建的性能, 会使用DocumentFragment缓存用户创建的DOM, 在重复创建的过程中使用缓存的DOM对象clone创建DOM, 从而提高DOM创建的效率。 但是, 这一优化, 仅适用于用户频繁创建的DOM内容没有变化的情况, 对于内容不断变化的话, 在页面没有卸载的情况下长时间运行
例如(不好)
$("#btn").on("click", function(e){
var ele = $("#hostelement");
var content = getValue();
var $span = $("<span>" + content + "</span>")
ele.append($span);
});
该方式下, jQuery不会生成DocumentFragment对象, 也不会缓存DOM对象在其内部的变量中, 因此重复、频繁使用时不会造成DOM泄露产生的问题内存上涨。
3. 尽量不要使用iframe
说明:
iframe适用于第三方集成, 文件异步上传等业务, 非此类业务时, 建议使用HTML方式构建, 而不要使用iframe, iframe在加载性能和反复删除情况下的内存回收都不太理想, 尤其是低版本的浏览器。
事件监听
1. 正确使用DOM Event Level 2/3 标准进行事件的绑定和注销。
2. 页面卸载、动态删除的DOM对象时要注销绑定的事件监听。
说明:
通常事件监听都是与DOM对象挂钩的, 当事件监听没有被注销时, 可能会造成JavaScript对象与DOM对象之间存在引用关系, 尤其在大量使用闭包的情况下, 容易造成JavaScript对象和DOM的循环引用。 在IE、firefox上会造成内存泄露, 原因是由于IE和firefox都使用引用计数方式的垃圾回收算法。
var $btn = $(".btn");
$btn.off();
$btn.remove();
3. 不要在HTML标签上嵌入JavaScript事件回调代码
说明:
直接将JavaScript字符串作为DOM的事件处理逻辑, 容易引发IE8版本下无法释放内存的问题。
其它
1. 页面局部刷新调整时, 要注销相应的定时器和延时器。
说明:
定时器和延时器容易造成JavaScript对DOM的引用, 在JavaScript对象本身没有被释放的情况下, 容易引起DOM对象的泄露。
在局部页面刷新时, 必须清理相应的定时器, 否则会造成多个定时器同时执行的业务问题和每个定时器对应闭包所引用的内存无法释放的问题。
2. 不要在JavaScript代码中使用console.log输出。
说明:
console.log输出会导致浏览器内存上涨。 在IE下, 只要打开F12, console就会被激活, 后面即使关闭F12工具也无法关闭掉console, 内存依然占用。
3. 使用完数组要将数组的长度置空
说明:
JavaScript是通过引用传参, 需要在接口中或模块中明确数组对象的生命周期责任, 否则可能会造成一处已经置null, 而别处仍然持有引用, 导致内存没有释放。