logo头像
游魂的网络日志

前端开发规范

本文于2243天之前发表,文中内容可能已经过时。

前端开发规范

一般规范

统一编码

为了使我们所编写的项目在任何语言的操作系统上都能运行,达到国际化,编码统一使用 UTF-8

文件/资源命名

在 web 项目中,所有的文件名应该都遵循同一命名约定。以可读性而言,减号(-)是用来分隔文件名的不二之选。同时它也是常见的 URL 分隔符,所以理所当然的,减号应该也是用来分隔资源名称的好选择。

请确保文件命名总是以字母开头而不是数字。而以特殊字符开头命名的文件,一般都有特殊的含义与用处(比如 compass[1] 中的下划线就是用来标记跳过直接编译的文件用的)。

资源的字母名称必须全为小写,这是因为在某些对大小写字母敏感的操作系统中,当文件通过工具压缩混淆后,或者人为修改过后,大小写不同而导致引用文件不同的错误,很难被发现。

还有一些情况下,需要对文件增加前后缀或特定的扩展名(比如 .min.js, .min.css),抑或一串前缀(比如 3fa89b.main.min.css)。这种情况下,建议使用点分隔符来区分这些在文件名中带有清晰意义的元数据。

不推荐

1
2
3
4
5
MyScript.js
myCamelCaseName.css
i_love_underscores.html
1001-scripts.js
my-file-min.css

推荐

1
2
3
4
5
my-script.js
my-camel-case-name.css
i-love-underscores.html
thousand-and-one-scripts.js
my-file.min.css

协议

不要指定引入资源所带的具体协议。

当引入图片或其他媒体文件,还有样式和脚本时,URLs 所指向的具体路径,不要指定协议部分(http:, https:),除非这两者协议都不可用。

不指定协议使得 URL 从绝对的获取路径转变为相对的,在请求资源协议无法确定时非常好用,而且还能为文件大小节省几个字节。

不推荐

1
<script src="http://cdn.com/foundation.min.js"></script>

推荐

1
<script src="//cdn.com/foundation.min.js"></script>

不推荐

1
2
3
.example {
background: url(http://static.example.com/images/bg.jpg);
}

推荐

1
2
3
.example {
background: url(//static.example.com/images/bg.jpg);
}

文本缩进

一次缩进两个空格。

html 代码:

1
2
3
4
5
6
7
<ul>
<li>Fantastic</li>
<li>Great</li>
<li>
<a href="#">Test</a>
</li>
</ul>

css 代码:

1
2
3
4
5
@media screen and (min-width: 1100px) {
body {
font-size: 100%;
}
}

js 代码:

1
2
3
4
5
6
7
8
9
10
(function(){
var x = 10;

function y(a, b) {
return {
result: (a + b) * x
}

}
}());

这个其实也就是一直在讨论的代码风格,例如分号到底是加还是不加,使用单引号还是双引号等等。

为了约定大家的代码风格,所以在社区中诞生了一些比较规范的代码风格规范:

注释

注释是你自己与你的小伙伴们了解代码写法和目的的唯一途径。特别是在写一些看似琐碎的无关紧要的代码时,由于记忆点不深刻,注释就变得尤为重要了。

编写自解释代码只是一个传说,没有任何代码是可以完全自解释的。而代码注释,则是永远也不嫌多。当然,尽量让自己写的代码一目了然,减少注释也就减少项目体积。

当你写注释时一定要注意:尽量不要写你的代码都干了些什么,而要写你的代码为什么要这么写,背后的考量是什么。当然也可以加入所思考问题或是解决方案的链接地址。

大区块必须注释,小区块适量注释。

例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//时间戳转换日期时间
inspiry.getDate = function (dt) {
dt = new Date(dt);
var year = dt.getFullYear();
var month = dt.getMonth() + 1;
var day = dt.getDate();
var hour = dt.getHours();
var min = dt.getMinutes();
var s = dt.getSeconds();
//修改双位数
month = month < 10 ? "0" + month : month;
day = day < 10 ? "0" + day : day;
hour = hour < 10 ? "0" + hour : hour;
min = min < 10 ? "0" + min : min;
s = s < 10 ? "0" + s : s;
//返回转换后的日期(返回格式可根据项目需求更改)
return year + "-" + month + "-" + day + " " + hour + ":" + min + ":" + s;
}

代码检查

对于比较宽松自由的编程语言来说,严格遵循编码规范和格式化风格指南就显得极为重要。遵循规范固然很好,但是有自动化流程来确保其执行情况,岂不更佳。Trust is good, control is better.

对于 JavaScript,建议使用 JSLintJSHint。当然可能对国人不太友好,我个人经常在用的是Fundebug,目前完全免费。

HTML规范

文档类型

推荐使用 HTML5 的文档类型申明: <!DOCTYPE html>.

(建议使用 text/html 格式的 HTML。避免使用 XHTML。XHTML 以及它的属性,比如 application/xhtml+xml 在浏览器中的应用支持与优化空间都十分有限)。

HTML 中最好不要将无内容元素[1] 的标签闭合,例如:使用 <br> 而非 <br />.

HTML验证

一般情况下,建议使用能通过标准规范验证的 HTML 代码,除非在性能优化和控制文件大小上不得不做出让步。

使用诸如 W3C HTML validator 这样的工具来进行检测。

规范化的 HTML 是显现技术要求与局限的显著质量基线,它促进了 HTML 被更好地运用。

省略可选标签

HTML5 规范中规定了 HTML 标签是可以省略的。但从可读性来说,在开发的源文件中最好不要这样做,因为省略标签可能会导致一些问题。

省略一些可选的标签确实使得页面大小减少,这很有用,尤其是对于一些大型网站来说。为了达到这一目的,我们可以在开发后期对页面进行压缩处理,在这个环节中这些可选的标签完全就可以省略掉了。

不推荐

1
2
<title>Test</title>
<article>This is only a test.

推荐

1
2
3
4
<!DOCTYPE html>
<meta charset="utf-8">
<title>Test</title>
<article>This is only a test.</article>

脚本加载

出于性能考虑,脚本异步加载很关键。一段脚本放置在 <head> 内,比如 <script src="main.js"></script>,其加载会一直阻塞 DOM 解析,直至它完全地加载和执行完毕。这会造成页面显示的延迟。特别是一些重量级的脚本,对用户体验来说那真是一个巨大的影响。

异步加载脚本可缓解这种性能影响。如果只需兼容 IE10+,可将 HTML5 的 async 属性加至脚本中,它可防止阻塞 DOM 的解析,甚至你可以将脚本引用写在 <head> 里也没有影响。

如需兼容老旧的浏览器,实践表明可使用用来动态注入脚本的脚本加载器。你可以考虑 yepnopelabjs。注入脚本的一个问题是:一直要等到 CSS 对象文档已就绪,它们才开始加载(短暂地在 CSS 加载完毕之后),这就对需要及时触发的 JS 造成了一定的延迟,这多多少少也影响了用户体验吧。

终上所述,兼容老旧浏览器(IE9-)时,应该遵循以下最佳实践。

脚本引用写在 body 结束标签之前,并带上 async 属性。这虽然在老旧浏览器中不会异步加载脚本,但它只阻塞了 body 结束标签之前的 DOM 解析,这就大大降低了其阻塞影响。而在现代浏览器中,脚本将在 DOM 解析器发现 body 尾部的 script 标签才进行加载,此时加载属于异步加载,不会阻塞 CSSOM(但其执行仍发生在 CSSOM 之后)。

所有浏览器中(考虑兼容),推荐

1
2
3
4
5
6
7
8
9
10
<html>
<head>
<link rel="stylesheet" href="main.css">
</head>
<body>
<!-- body goes here -->

<script src="main.js" async></script>
</body>
</html>

只在现代浏览器中(不考虑兼容),推荐

1
2
3
4
5
6
7
8
9
<html>
<head>
<link rel="stylesheet" href="main.css">
<script src="main.js" async></script>
</head>
<body>
<!-- body goes here -->
</body>
</html>

语义化

根据元素(有时被错误地称作“标签”)其被创造出来时的初始意义来使用它。打个比方,用 heading 元素来定义头部标题,p 元素来定义文字段落,用 a 元素来定义链接锚点,等等。

有根据有目的地使用 HTML 元素,对于可访问性、代码重用、代码效率来说意义重大。

以下示例列出了一些的语义化 HTML 主要情况:

不推荐

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<b>My page title</b>
<div class="top-navigation">
<div class="nav-item"><a href="#home">Home</a></div>
<div class="nav-item"><a href="#news">News</a></div>
<div class="nav-item"><a href="#about">About</a></div>
</div>

<div class="news-page">
<div class="page-section news">
<div class="title">All news articles</div>
<div class="news-article">
<h2>Bad article</h2>
<div class="intro">Introduction sub-title</div>
<div class="content">This is a very bad example for HTML semantics</div>
<div class="article-side-notes">I think I'm more on the side and should not receive the main credits</div>
<div class="article-foot-notes">
This article was created by David <div class="time">2014-01-01 00:00</div>
</div>
</div>

<div class="section-footer">
Related sections: Events, Public holidays
</div>
</div>
</div>

<div class="page-footer">
Copyright 2014
</div>

推荐

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
<!-- 页面头部 -->
<header>
<!-- 页面标题 一个页面要求只能有一个h1标签 -->
<h1>My page title</h1>
</header>

<!-- 导航 -->
<nav class="top-navigation">
<ul>
<li class="nav-item"><a href="#home">Home</a></li>
<li class="nav-item"><a href="#news">News</a></li>
<li class="nav-item"><a href="#about">About</a></li>
</ul>
</nav>

<!-- 页面主体内容 -->
<main class="news-page" role="main">
<!-- 页面的一部分应该进入一个section元素。将页面划分为具有语义元素的部分. -->
<section class="page-section news">
<!-- 一个节头应该进入一个节元素 -->
<header>
<h2 class="title">All news articles</h2>
</header>

<!-- 文章模块 -->
<article class="news-article">
<!-- 文章模块头部 -->
<header>
<div class="article-title">Good article</div>
<small class="intro">Introduction sub-title</small>
</header>

<!-- 文章模块内容 -->
<div class="content">
<p>This is a good example for HTML semantics</p>
</div>
<!-- 文章侧边栏 -->
<aside class="article-side-notes">
<p>I think I'm more on the side and should not receive the main credits</p>
</aside>
<!-- 文章页脚 -->
<footer class="article-foot-notes">
<p>This article was created by David <time datetime="2014-01-01 00:00" class="time">1 month ago</time></p>
</footer>
</article>
<!-- 区域页脚 -->
<footer class="section-footer">
<p>Related sections: Events, Public holidays</p>
</footer>
</section>
</main>

<!-- 页面尾部 -->
<footer class="page-footer">
Copyright 2014
</footer>

多媒体回溯

对页面上的媒体而言,像图片、视频、canvas 动画等,要确保其有可替代的接入接口。图片文件我们可采用有意义的备选文本(alt),视频和音频文件我们可以为其加上说明文字或字幕。

提供可替代内容对可用性来说十分重要。试想,一位盲人用户如何能知晓一张图片是什么,要是没有 @alt 的话。

(图片的 alt 属性是可不填写内容的,纯装饰性的图片就可用这么做:alt="")。

不推荐

1
<img src="luke-skywalker.jpg">

推荐

1
<img src="luke-skywalker.jpg" alt="天行者骑着一匹陌生的马">

尽量用 alt 标签去描述图片,设想你需要对于那些只能通过语音或者看不见图片的用户表达图片到底是什么。

不推荐

1
<img src="huge-spaceship-approaching-earth.jpg" alt="头部图片">

推荐

1
<img src="huge-spaceship-approaching-earth.jpg" alt="一艘正在接近地球的巨大宇宙飞船">

关注点分离

理解 web 中如何和为何区分不同的关注点,这很重要。这里的关注点主要指的是:信息(HTML 结构)、外观(CSS)和行为(JavaScript)。为了使它们成为可维护的干净整洁的代码,我们要尽可能的将它们分离开来。

严格地保证结构、表现、行为三者分离,并尽量使三者之间没有太多的交互和联系。

就是说,尽量在文档和模板中只包含结构性的 HTML;而将所有表现代码,移入样式表中;将所有动作行为,移入脚本之中。

在此之外,为使得它们之间的联系尽可能的小,在文档和模板中也尽量少地引入样式和脚本文件。

清晰的分层意味着:

  • 不使用超过一到两张样式表(i.e. main.css, vendor.css)
  • 不使用超过一到两个脚本(学会用合并脚本)
  • 不使用行内样式(<style>.no-good {}</style>
  • 不在元素上使用 style 属性(<hr style="border-top: 5px solid black">
  • 不使用行内脚本(<script>alert('no good')</script>
  • 不使用表象元素(i.e. <b>, <u>, <center>, <font>, <b>
  • 不使用表象 class 名(i.e. red, left, center)

不推荐

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="base.css">
<link rel="stylesheet" href="grid.css">
<link rel="stylesheet" href="type.css">
<link rel="stylesheet" href="modules/teaser.css">
</head>
<body>
<h1 style="font-size: 3rem"></h1>
<b>I'm a subtitle and I'm bold!</b>
<center>Dare you center me!</center>
<script>
alert('Just dont...');
</script>
<div class="red">I'm important!</div>
</body>
</html>

推荐

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<!DOCTYPE html>
<html>
<head>
<!-- 将你的样式表统一成一个单一的样式 -->
<link rel="stylesheet" href="main.css">
</head>
<body>
<!-- 不要使用行内样式,而是在样式表中指定合理的类并应用样式 -->
<h1 class="title"></h1>
<!-- 不要使用表意不明的类 -->
<div class="sub-title">I'm a subtitle and I'm bold!</div>
<span class="comment">Dare you center me!</span>
<div class="important">I'm important!</div>

<!-- 将所有脚本放入文件中,并将其放入一个单独的文件中 -->
<script async src="main.js"></script>
</body>
</html>

Type 属性

省略样式表与脚本上的 type 属性。鉴于 HTML5 中以上两者默认的 type 值就是 text/css 和 text/javascript,所以 type 属性一般是可以忽略掉的。甚至在老旧版本的浏览器中这么做也是安全可靠的。

不推荐

1
2
<link rel="stylesheet" href="main.css" type="text/css">
<script src="main.js" type="text/javascript"></script>

推荐

1
2
<link rel="stylesheet" href="main.css">
<script src="main.js"></script>

微格式在 SEO 和可用性上的运用

如果 SEO 和可用性环境条件允许的话,建议考虑采用微格式。微格式是通过在元素标签上申明一系列特定数据来达成特定语义的方法。

谷歌、微软和雅虎对如何使用这些额外的数据一定程度上的达成一致,如果正确的使用,这将给搜索引擎优化带来巨大的好处。

你可以访问 schema.org 获得更多内容细节。

看一个电影网站的简单例子:

不带微格式

1
2
3
4
5
6
<div>
<h1>Avatar</h1>
<span>Director: James Cameron (born August 16, 1954)</span>
<span>Science fiction</span>
<a href="../movies/avatar-theatrical-trailer.html">Trailer</a>
</div>

带有微格式

1
2
3
4
5
6
7
8
<div itemscope itemtype ="http://schema.org/Movie">
<h1 itemprop="name">Avatar</h1>
<div itemprop="director" itemscope itemtype="http://schema.org/Person">
Director: <span itemprop="name">James Cameron</span> (born <span itemprop="birthDate">August 16, 1954)</span>
</div>
<span itemprop="genre">Science fiction</span>
<a href="../movies/avatar-theatrical-trailer.html" itemprop="trailer">Trailer</a>
</div>

ID 和锚点

通常一个比较好的做法是将页面内所有的头部标题元素都加上 ID. 这样做,页面 URL 的 hash 中带上对应的 ID 名称,即形成描点,方便跳转至对应元素所处位置。

格式化规则

在每一个块状元素,列表元素和表格元素后,加上一新空白行,并对其子孙元素进行缩进。内联元素写在一行内,块状元素还有列表和表格要另起一行。

(如果由于换行的空格引发了不可预计的问题,那将所有元素并入一行也是可以接受的,格式警告总好过错误警告)。

推荐

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<blockquote>
<p><em>Space</em>, the final frontier.</p>
</blockquote>

<ul>
<li>Moe</li>
<li>Larry</li>
<li>Curly</li>
</ul>

<table>
<thead>
<tr>
<th scope="col">Income</th>
<th scope="col">Taxes</th>
</tr>
</thead>
<tbody>
<tr>
<td>$ 5.00</td>
<td>$ 4.50</td>
</tr>
</tbody>
</table>

HTML 引号

使用双引号(“”) 而不是单引号(”) 。

CSS规范

class 命名

  • class 名称中只能出现小写字符和破折号(dashe)(不是下划线,也不是驼峰命名法)。破折号应当用于相关 class 的命名(类似于命名空间)(例如,.btn.btn-danger)。
  • 避免过度任意的简写。.btn 代表 button,但是 .s 不能表达任何意思。
  • class 名称应当尽可能短,并且意义明确。
  • 使用有意义的名称。使用有组织的或目的明确的名称,不要使用表现形式(presentational)的名称。
  • 基于最近的父 class 或基本(base) class 作为新 class 的前缀。
  • 使用 .js-* class 来标识行为(与样式相对),并且不要将这些 class 包含到 CSS 文件中。

在为 Sass 和 Less 变量命名时也可以参考上面列出的各项规范。

不推荐

1
2
3
.t { ... }
.red { ... }
.header { ... }

推荐

1
2
3
.tweet { ... }
.important { ... }
.tweet-header { ... }

css代码组织(按块书写css样式)

  • 以组件为单位组织代码段。
  • 制定一致的注释规范。
  • 使用一致的空白符将代码分隔成块,这样利于扫描较大的文档。
  • 如果使用了多个 CSS 文件,将其按照组件而非页面的形式分拆,因为页面会被重组,而组件只会被移动。

把父类下边所有的子类都写在父类后边,兄弟类同级,类似于一颗大树,分支条理清晰。

推荐

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/*第一屏*/
.screen01 {
width: 100%;
height: 100%;
position: relative;
background: url("../images/01-bg.png") no-repeat center bottom;
}
.screen01 .text {
width: 470px;
height: 50px;
position: absolute;
left: 50%;
margin-left: -235px;
transform: translate(250px,100px);
animation: screen01-text 0.75s linear infinite alternate;
}
.screen01 .sofa {
width: 134px;
height: 147px;
position: absolute;
left: 50%;
margin-left: -67px;
transform: translate(-160px,200px);
animation: screen01-sofa 1s linear infinite alternate;
}

合理的避免使用ID

一般情况下ID不应该被应用于样式。
ID的样式不能被复用并且每个页面中你只能使用一次ID。
使用ID唯一有效的是确定网页或整个站点中的位置。
尽管如此,你应该始终考虑使用class,而不是id,除非只使用一次。

CSS选择器中避免标签名及合理的语义类名

当构建选择器时应该使用清晰, 准确和有语义的class(类)名。不要使用标签选择器。 如果你只关心你的class(类)名,而不是你的代码元素,这样会更容易维护。

从分离的角度考虑,在表现层中不应该分配html标记/语义。
它可能是一个有序列表需要被改成一个无序列表,或者一个div将被转换成article。
如果你只使用具有实际意义的class(类)名,
并且不使用元素选择器,那么你只需要改变你的html标记,而不用改动你的CSS。

尽可能的精确

很多前端开发人员写选择器链的时候不使用 直接子选择器(注:直接子选择器和后代选择器的区别)。
有时,这可能会导致疼痛的设计问题并且有时候可能会很耗性能。
然而,在任何情况下,这是一个非常不好的做法。
如果你不写很通用的,需要匹配到DOM末端的选择器, 你应该总是考虑直接子选择器。

缩写属性

CSS提供了各种缩写属性(如 font 字体)应该尽可能使用,即使在只设置一个值的情况下。

使用缩写属性对于代码效率和可读性是有很有用的。

不推荐

1
2
3
4
5
6
7
8
border-top-style: none;
font-family: palatino, georgia, serif;
font-size: 100%;
line-height: 1.6;
padding-bottom: 2em;
padding-left: 1em;
padding-right: 1em;
padding-top: 0;

推荐

1
2
3
border-top: 0;
font: 100%/1.6 palatino, georgia, serif;
padding: 0 1em 2em;

0 和 单位

省略“0”值后面的单位。不要在0值后面使用单位,除非有值。

对于属性值或颜色参数,省略小于 1 的小数前面的 0 (例如,.5代替 0.5-.5px 代替 -0.5px )。

不推荐

1
2
padding-bottom: 0px;
margin: 0em;

推荐

1
2
padding-bottom: 0;
margin: 0;

十六进制表示法

在可能的情况下,使用3个字符的十六进制表示法。
颜色值允许这样表示,
3个字符的十六进制表示法更简短。

始终使用小写的十六进制数字。

不推荐

1
color: #FF33AA;

推荐

1
color: #f3a;

声明结束和属性名结束

为了保证一致性和可扩展性,每个声明应该用分号结束,每个声明换行。

属性名的冒号后使用一个空格。出于一致性的原因,
属性和值(但属性和冒号之间没有空格)的之间始终使用一个空格。

不推荐

1
2
3
.test {
display: block; height:100px
}

推荐

1
2
3
4
.test {
display: block;
height: 100px;
}

规则分隔

规则之间始终有一个空行(双换行符)分隔。

推荐

1
2
3
4
5
6
7
8
html {
background: #fff;
}

body {
margin: auto;
width: 50%;
}

CSS引号

属性选择器或属性值用双引号(””),而不是单引号(”)括起来。
URI值(url())不要使用引号。

不推荐

1
2
3
4
5
6
7
8
9
@import url('//cdn.com/foundation.css');

html {
font-family: 'open sans', arial, sans-serif;
}

body:after {
content: 'pause';
}

推荐

1
2
3
4
5
6
7
8
9
@import url(//cdn.com/foundation.css);

html {
font-family: "open sans", arial, sans-serif;
}

body:after {
content: "pause";
}

属性顺序

HTML 属性应当按照以下给出的顺序依次排列,确保代码的易读性。

  • class
  • id, name
  • data-*
  • src, for, type, href, value
  • title, alt
  • role, aria-*

class 用于标识高度可复用组件,因此应该排在首位。id 用于标识具体组件,应当谨慎使用(例如,页面内的书签),因此排在第二位。

Less 和 Sass 中的嵌套

避免不必要的嵌套。这是因为虽然你可以使用嵌套,但是并不意味着应该使用嵌套。只有在必须将样式限制在父元素内(也就是后代选择器),并且存在多个需要嵌套的元素时才使用嵌套。

Less 和 Sass 中的操作符

为了提高可读性,在圆括号中的数学计算表达式的数值、变量和操作符之间均添加一个空格。

JavaScript规范

变量声明

总是使用 var 来声明变量。如不指定 var,变量将被隐式地声明为全局变量,这将对变量难以控制。如果没有声明,变量处于什么定义域就变得不清(可以是在 Document 或 Window 中,也可以很容易地进入本地定义域)。所以,请总是使用 var 来声明变量。

ES6中有了模块作用域,也就是let(声明变量)和const(声明常量) 。
如果采用前端工程化开发,使用到了ES6,推荐使用letconst

采用严格模式带来的好处是,当你手误输入错误的变量名时,它可以通过报错信息来帮助你定位错误出处。

不推荐

1
2
x = 10;
y = 100;

推荐

1
2
var x = 10,
y = 100;

全局命名空间污染与 IIFE

总是将代码包裹成一个 IIFE(Immediately-Invoked Function Expression),用以创建独立隔绝的定义域。这一举措可防止全局命名空间被污染。

IIFE 还可确保你的代码不会轻易被其它全局命名空间里的代码所修改(i.e. 第三方库,window 引用,被覆盖的未定义的关键字等等)。

不推荐

1
2
3
4
var x = 10,
y = 100;

console.log(window.x + ' ' + window.y);

推荐

1
2
3
4
5
6
7
8
9
(function(log, w, undefined){
'use strict';

var x = 10,
y = 100;

log((w.x === undefined) + ' ' + (w.y === undefined));

}(window.console.log, window));

IIFE(立即执行的函数表达式)

无论何时,想要创建一个新的封闭的定义域,那就用 IIFE。它不仅避免了干扰,也使得内存在执行完后立即释放。

所有脚本文件建议都从 IIFE 开始。

立即执行的函数表达式的执行括号应该写在外包括号内。虽然写在内还是写在外都是有效的,但写在内使得整个表达式看起来更像一个整体,因此推荐这么做。

不推荐

1
(function(){})();

推荐

1
(function(){}());

so,用下列写法来格式化你的 IIFE 代码:

1
2
3
4
5
6
(function(){
'use strict';

// Code goes here

}());

如果你想引用全局变量或者是外层 IIFE 的变量,可以通过下列方式传参:

1
2
3
4
5
6
7
(function($, w, d){
'use strict';

$(function() {
w.alert(d.querySelectorAll('div').length);
});
}(jQuery, window, document));

总是使用带类型判断的比较判断

总是使用 === 精确的比较操作符,避免在判断的过程中,由 JavaScript 的强制类型转换所造成的困扰。

如果你使用 === 操作符,那比较的双方必须是同一类型为前提的条件下才会有效。

如果你想了解更多关于强制类型转换的信息,你可以读一读 Dmitry Soshnikov 的这篇文章

在只使用 == 的情况下,JavaScript 所带来的强制类型转换使得判断结果跟踪变得复杂,下面的例子可以看出这样的结果有多怪了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
(function(log){
'use strict';

log('0' == 0); // true
log('' == false); // true
log('1' == true); // true
log(null == undefined); // true

var x = {
valueOf: function() {
return 'X';
}
};

log(x == 'X');

}(window.console.log));

明智地使用真假判断

当我们在一个 if 条件语句中使用变量或表达式时,会做真假判断。if(a == true) 是不同于 if(a)的。后者的判断比较特殊,我们称其为真假判断。这种判断会通过特殊的操作将其转换为 true 或 false,下列表达式统统返回 false:false, 0, undefined, null, NaN, ''(空字符串).

这种真假判断在我们只求结果而不关心过程的情况下,非常的有帮助。

以下示例展示了真假判断是如何工作的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
(function(log){
'use strict';

function logTruthyFalsy(expr) {
if(expr) {
log('truthy');
} else {
log('falsy');
}
}

logTruthyFalsy(true); // truthy
logTruthyFalsy(1); // truthy
logTruthyFalsy({}); // truthy
logTruthyFalsy([]); // truthy
logTruthyFalsy('0'); // truthy

logTruthyFalsy(false); // falsy
logTruthyFalsy(0); // falsy
logTruthyFalsy(undefined); // falsy
logTruthyFalsy(null); // falsy
logTruthyFalsy(NaN); // falsy
logTruthyFalsy(''); // falsy

}(window.console.log));

变量赋值时的逻辑操作

逻辑操作符 ||&& 也可被用来返回布尔值。如果操作对象为非布尔对象,那每个表达式将会被自左向右地做真假判断。基于此操作,最终总有一个表达式被返回回来。这在变量赋值时,是可以用来简化你的代码的。参考

不推荐

1
2
3
4
5
6
7
if(!x) {
if(!y) {
x = 1;
} else {
x = y;
}
}

推荐

1
x = x || y || 1;

这一小技巧经常用来给方法设定默认的参数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
(function(log){
'use strict';

function multiply(a, b) {
a = a || 1;
b = b || 1;

log('Result ' + a * b);
}

multiply(); // Result 1
multiply(10); // Result 10
multiply(3, NaN); // Result 3
multiply(9, 5); // Result 45

}(window.console.log));

阻止兼容模式

有时候 IE 会在用户不知道的情况下自作主张切换到兼容模式。要阻止你的站点缺省进入兼容模式,可以在站点的<head> 部分加入下列代码:

1
<meta http-equiv="X-UA-Compatible" content="IE=edge">

优化 JavaScript 的特征

  • 编写可维护的代码
  • 单变量模式
  • Hoisting:把所有变量声明统一放到函数的起始位置 (在后部声明的变量也会被JS视为在头部定义,由此会产生问题)
  • 不要扩充内置原型(虽然给Object(), Function()之类的内置原型增加属性和方法很巧妙,但是会破坏可维护性)
  • 不要用隐含的类型转换
  • 不要用 eval()
  • 用 parseInt() 进行数字转换
  • (规范)左大括号的位置
  • 构造器首字母大写
  • 写注释
  • 不要用 void
  • 不要用 with 语句
  • 不要用 continue 语句
  • 尽量不要用位运算

GIT开发规范

设置和初始化工作:

全局编码设置:
统一采用utf-8, 请检查自己的编码,编码不一致可能会导致没实际上没更改的文档也会显示为更新。
查看设置:git config -l

设置编码:

1
2
3
git config --global gui.encoding utf-8
git config --global i18n.commitencoding utf-8
git config --global i18n.logoutputencoding utf-8

设置用户名和email:

1
2
git config --global user.name 用户名
git config --global user.email 邮箱

开发流程

项目分支

一般来说,互联网项目有线上/预上线分支(master),测试分支(stable),开发分支(dev)等.

保证不同的分支做不同的事情,防止分支污染。

  1. 线上/预上线分支(master):是预上线环境和线上环境的分支,以这个分支为准,其他分支都是以这个分支为基础拉取。
  2. 测试分支(stable):测试环境分支,是给测试团队测试使用,如果有些功能在本地及开发不容易测试,开发人员可以测试分支进行自测。
  3. 开发分支(dev):开发人员自测。

开发流程

步骤:

  1. 第一步,新需求或BUG,从上线分支拉取一个开发分支。
  2. 第二步,在开发分支进行开发,自测。
  3. 第三步,合并到测试分支,通知QA测试。
  4. 第四步,如果通过测试,合并到master,然后继续测试。如果不通过测试,进入第二步。
  5. 第五步,如果预上线测试通过,将预上线tag 直接部署到线上。如果不通过测试,进入第二步。
  6. 第六步,上线,然后线上测试。如果通过测试,那么这个需求开发就结束了。如果没有通过测试,就回归上一版本,然后进入第二步。

img

当遇到一个新需求或者更改BUG时,必须重新建立分支

1
2
3
4
5
6
# 获取主干最新代码
git checkout master
git pull

# 新建一个开发分支myfeature并切换到这个分支
git checkout -b myfeature

当修改完毕后,提交分支

1
2
3
4
5
6
7
8
# 查看发生变动的文件
git status

# 保存所有变化(包括新建、修改和删除)
git add .

# 撰写提交信息
git commit -m "修改样式错位"

将本地分支推送至远程仓库 提交后合并到stable 提测

1
2
3
4
5
git push

# 切换到stable分支 合并
git checkout stable
git merge myfeature

如果通过测试则合并到master,去公司的git地址创建合并请求,等待master管理员合并
未通过测试,则继续修改重新测试

命令汇总

git config配置本地仓库

常用git config –global user.name、git config –global user.email

git config –list查看配置详情

git init 初始一个仓库,添加–bare可以初始化一个共享(裸)仓库

git status 可以查看当前仓库的状态

git add"文件" 将工作区中的文件添加到暂存区中,其中file可是一个单独的文件,也可以是一个目录、"*"、-A

git commit -m “备注信息' 将暂存区的文件,提交到本地仓库

git log 可以查看本地仓库的提交历史

git branch查看分支

git branch"分支名称" 创建一个新的分支

git checkout"分支名称" 切换分支

git checkout -b deeveloper 新建并切到developer分支

git merge"分支名称" 合并分支

git branch -d "分支名称" 删除分支

git clone "仓库地址"获取已有仓库的副本

git push origin "本地分支名称:远程分支名称"将本地分支推送至远程仓库,

git push origin hotfix(通常的写法)相当于

git push origin hotfix:hotfix

git push origin hotfix:newfeature

本地仓库分支名称和远程仓库分支名称一样的情况下可以简写成一个,即git push "仓库地址" "分支名称",如果远程仓库没有对应分支,将会自动创建

git remote add "主机名称" "远程仓库地址"添加远程主机,即给远程主机起个别名,方便使用

git remote 可以查看已添加的远程主机

git remote show "主机名称"可以查看远程主机的信息

GitLib权限管理

GitLib有五种身份权限,分别是:

  • Owner 项目所有者,拥有所有的操作权限
  • Master 项目的管理者,除更改、删除项目元信息外其它操作均可
  • Developer 项目的开发人员,做一些开发工作,对受保护内容无权限
  • Reporter 项目的报告者,只有项目的读权限,可以创建代码片断
  • Guest 项目的游客,只能提交问题和评论内容

具体参见GitLab权限,为项目添加成员时可指定成员的身份权限

Git高级

熟悉掌握以上操作,基本上是可以满足日常开的需要的,但是在解决一些特殊问题时,就又需要我们能够掌握更多的命令。

git ignore忽略文件

在项目根目录下创建一个.gitignore文件,可以将不希望提交的罗列在这个文件里,如项目的配置文件、node_modules等

https://github.com/github/gitignore

比较差异

当内容被修改,我们无法确定修改哪些内容时,可以通过git diff来进行差异比较。

git difftool 比较的是工作区和暂存的差异

git difftool "SHA"比较与特定提交的差异

git difftool "SHA""SHA"比较某两次提交的差异

git difftool 分支名称 比较与某个分支的差异

回滚(撤销)操作

HEAD 默认指向当前分支的"末端",即最后的一次提交,但是我们通过git reset 可以改变HEAD的指向。

稍微复杂一些,理解就好

1、git reset

–hard 工作区会变、历史(HEAD)会变, 暂存区也变

–soft 只会变历史(HEAD)

–mixed(默认是这个选项)历史(HEAD)会变、暂存区也变,工作区不变

2、git checkout

git checkout SHA – "某个文件",代表只是从SHA这个版中取出特定的文件,

和git reset 是有区别的,reset 重写了历史,checkout 则没有。

更新仓库

在项目开发过程中,经常性的会遇到远程(共享)仓库和本地仓库不一致,我们可以通过git fetch 命令来更新本地仓库,使本地仓库和远程(共享)仓库保持一致。

git fetch "远程主机"

或者

git fetch "远程主机" "分支名称"

我们要注意的是,利用git fetch 获取的更新会保存在本地仓库中,但是并没有体现到我们的工作目录中,需要我们再次利用git merge来将对应的分支合并(融合)到特定分支。如下

git pull origin 某个分支, 上操作相当于下面两步

git fetch

git merge origin/某个分支

查看远程主机上总共有多少个分支

git branch -a 便可以查看所有(本地+远程仓库)分支了

其它

删除远程分支git push origin –delete 分支名称

删除远程分支git push origin :分支名称