三、CSS 书写规范
类名和 ID
类名用于样式化,ID 用于脚本访问
格式风格
不当的格式风格和推荐的格式风格对比:
// 不当的,{ 应放在类名后,而不是重启一行
.user-box
{
...
}
// 应该这样:
.user-box {
...
}
// 不当的样式
.user-box{
...
}
// 应该这样,{ 与类名之间应该有空格
.user-box {
...
}
// 不当的,样式不应写在一行:
.user-box { font-size: 14px; line-height: 2; }
// 应该这样:
.user-box {
font-size: 14px;
line-height: 2;
}
// 不当的样式
font-size:14px;
// 应该这样,键和值应以空格隔开
font-size: 14px;
// 不当的,最后一行样式也应以 ; 结束
.user-box {
font-size: 14px;
line-height: 2
}
// 应该这样:
.user-box {
font-size: 14px;
line-height: 2;
}
// 不当的样式
.user-list a, .news-list a {
...
}
// 应该这样,每个 selector 占一行
.user-list a,
.news-list a {
...
}
层级不可过多
// 不当,层级过多
.user-box .user-list .user-item .user-avatar {
...
}
// 应该减少层级,这样能提高 CSS 性能
.user-box .user-avatar {
...
}
// 采用命名空间的情况下,最好一级
.user-avatar {
...
}
// 这种情况下,即 .class tag,可以两级
.user-avatar a {
...
}
// 最长不要超过三级
使用命名空间
多使用 namespace-*
形式的类名,每个业务场景定义一个前缀作为命名空间,如用 site- 作为全站的命名空间,用 user- 作为用户中心的命名空间,用 news- 作为咨询的命名空间:
.site-top
.user-nav
.news-left
针对 tag 的样式
-
永远不要针对 div/span/p 的样式,这些标签太通用:
// 不当的
.user-box div
.news-box span
.article-list p
-
可以对 em strong 等含有丰富语义的标签应用样式,但不要嵌套
// 恰当的
.user-list em {
...
}
// 不当的,进行了多级嵌套
.user-list li em {
...
}
// 最不当的,单独的 tag 样式是魔鬼
// 合理的 css reset 除外,全站应有且只有一个统一的 css reset
em {
...
}
无论如何,少用 tag 样式
注释
-
less
中允许使用 //
注释,而 css
语法中不允许,只能使用 /**/
四、JS 书写规范
; 号的使用
虽然很多正规的教程,都要求每条 js 语句都应以 ;
结束,但这里,我们选择所有的语句省略
;
浪费了键盘输入,窃以为是 js 中的糟粕,不用纠结,大胆省略吧
-
有时,省略 ;
会导致代码出错,可加一个前置 ;
,例如
var str = ''
; [1, 2, 3].forEach(function(n) {
console.log(n)
})
代码风格
// 所有的二元运算符前后应有空格
var str = 'hi, ' + name
// 函数定义与 { 应处于同一行,并以空格分割
function lineTo(from, to) {
...
}
// 应尽量避免写 while 与 do .. while 循环,用 for 替代
// 可以尝试用 ECMAScript 5 新规范新增的 Array 方法,彻底替代循环结构,这些方法有:
// Array.prototype.forEach
// Array.prototype.map
// Array.prototype.every
// Array.prototype.some
// Array.prototype.filter
// Array.prototype.reduce
// Array.prototype.reduceRight
// 这些方法的使用,符合函数式理念,一旦习惯,就会爱上这种方式
// for 与 (,for 内的 ; 之间,) 与 { 都应有空格
for (var i = 1; i <= 9; i++) {
...
}
// if 与 for 同理,else 应与结束的 } 处于同一行
if (sex == 'girl') {
...
} else if (sex == 'boy') {
...
} else {
...
}
// 始终在 if/else/for 中使用 {},那怕只有一条语句
if (condition) {
// only one statement
return true
}
// switch 的 case 应该这样缩进,default 应始终在最后
switch (sex) {
case 'boy':
....
break;
case 'girl':
....
break;
default:
...
}
// 尽量避免使用 switch 语句,可以采用这样的结构
var map = {
boy: ...,
girl: ...
}
var val = map[sex]
// 甚至可以用 map 执行函数
var actionMap = {
doThis: function() {
...
},
doThat: function() {
...
}
}
// 函数调用与 ( 之间不应有空格,以便和 for if 语句加以区分
lineTo(5, 6)
// 永远不要这样写
var arr = new Array()
var obj = new Object()
// 应这样写
var arr = []
var obj = {}
// 一行一个变量,应该这样
var v1 = 1
var v2 = 2
// 避免这样
var v1 = 1, v2 = 2
// 变量名和函数名采用陀峰样式,不要使用 _,如
var myVar = 22
// 尽量使用 ES6 的语法,前提是环境支持(或 webpack + babel 编译支持,或原生环境支持)
尽量保持一个出口
很多人这样写代码:
function someFunc(...) {
if (aCondition) {
...
return aValue
}
if (bCondition) {
...
return bValue
}
...
return cValue
}
这样的代码,说实话,在我眼里,很拙劣,无论你有千般理由!什么性能啦、逻辑清晰啦,都是你对代码没有掌控力的借口!
不要纠结于局部的、指令级的性能,你不是在写汇编!
多处 return,你是爽了,但这种代码,很容易出现维护性问题。
我宁愿多用嵌套 if ... else,始终保持一个出口,函数采用三段式(第一段前置全函数变量声明、第二段逻辑处理、第三段返回)来写,例如:
function someFunc(...) {
var result
if (...) {
if (...) {
...
} else {
...
}
} else {
...
}
return result
}
如果嵌套过多,可以将之提取出来,放到一个新函数里,并且,给这个新函数,取一个含义明确的名字,这样代码就能实现“自注释”,例如:
function someFunc(...) {
var result
if (...) {
result = newFuncWithWellDescription(...)
} else {
...
}
return result
}
function newFuncWithWellDescription(...) {
...
}
并且,应在 else 里处理例外情况(错误处理),在 if 里处理正常流程。
不要怕函数多了影响性能,记住,你不是在写汇编,局部的、指令级的性能优化是没有意义的,系统整体的性能优化,才是重点
变量就地声明,声明的同时进行赋值
有些人这样写:
function someFunc(items) {
var i, len = items.length
...
for (i = 0; i < len; i++) {
...
}
...
}
这样写没有任何好处,i 只是一个循环变量,没有必要像 C 语言那样作前置声明,应该直接这样:
function someFunc(items) {
...
for (var i = 0, len = items.length; i < len; i++) {
...
}
...
}
变量就地声明并赋值,赋值后保持不变,不仅仅符合函数式编程思想,也是一种良好的编程习惯
嵌套函数
嵌套函数应该这样写:
function doSomething(...) {
...
var doSomethingInner = function(...) {
...
}
...
}
而不应该这样:
function doSomething(...) {
...
function doSomethingInner(...) {
...
}
...
}
要牢记,在 JS 这类函数式语言中,函数也是值
在 seajs 中,所有的函数都声明在 define(function() { ... }) 内,也即,所有的函数都是嵌套函数,但这里,我们忽略 seajs 这一级,将 seajs 内的第一级函数,当成顶级函数,例如:
define(function(require) {
...
function someFunc(...) {
...
}
...
})
而不要这样:
define(function(require) {
...
var someFunc = function(...) {
...
}
...
})
佛说,不要着相
五、函数式编程
js 本质上是函数式编程语言,受当时面向对象思潮的影响,也沾染上一些面向对象的糟粕。让我们去芜存菁,回归函数式本质。
关于函数式,可通过搜索引擎进行学习,这里只谈函数式的一个很重要的特性:不变性。
不变性
简单理解,一个变量,一旦定义并赋值(定义时同时赋值,是一个良好的编程习惯),其值应保持不变。
看以下代码:
function onSuccess(data) {
data = parseJson(data)
if (data.s == 200) {
...
}
}
上述代码存在的问题是,data 的值在第一行发生了变化!即使在命令式编程语言中,这种代码也是很拙劣的!
一般,稍微有经验的程序员,无论是否受到过函数式编程思想的影响,都不会写出这样的代码。多声明一个变量会死呀?应该这样:
function onSuccess(data) {
var json = parseJson(data)
if (json.s == 200) {
...
}
}
再看以下代码:
function doubleIt(items) {
for (var i = 0, len = items.length; i < len; i++) {
items[i] = items[i] * 2
}
return items
}
很多命令式编程语言的程序员,觉得上述代码没什么,甚至会告诉你,这样节省内存
上述代码不符合函数式精神,items 中的元素,仍然发生了变化,这样写:
function doubleIt(items) {
var newItems = []
for (var i = 0, len = items.length; i < len; i++) {
newItems[i] = items[i] * 2
}
return newItems
}
上述代码,只有循环变量 i 在变,并且局限在局部。更彻底地,我们使用 Array.prototype.map:
function doubleIt(items) {
return items.map(function(item) {
return item * 2
})
}
回忆高中时,数学中关于函数的定义:函数是一个集合到另一个集合!这样的代码,才算是,真正回归了,编程语言的数学本质
不要担心函数调用的性能,现代的 JS 引擎,会对这样的调用进行优化
JS 性能点不在这里,而在 DOM 操作。一个 DOM 操作所耗时间,约是 JS 对象操作的 1000 倍以上,所以应该尽量减少 DOM 操作
六、补充材料
解读ECMAScript[1]——执行环境、作用域及闭包
解读ECMAScript[2]——函数、构造器及原型
闭包漫谈(从抽象代数及函数式编程角度)
CSS 相对绝对定位系列(一)
SeaJS 官方文档
LESS 中文文档
使用SeaJS实现模块化JavaScript开发
七、拥抱 ES6+
从现在开始,重视并书写 ES6+,这里有个很好的教程 http://es6.ruanyifeng.com/