• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# JavaScript语言通用编程规范
2
3## 目的
4规则并不是完美的,通过禁止在特定情况下有用的特性,可能会对代码实现造成影响。但是我们制定规则的目的“为了大多数程序员可以得到更多的好处”, 如果在团队运作中认为某个规则无法遵循,希望可以共同改进该规则。
5参考该规范之前,希望您具有相应的JavaScript语言基础能力,而不是通过该文档来学习JavaScript语言。
6
7## 总体原则
8代码需要在保证功能正确的前提下,满足**可读、可维护、安全、可靠、可测试、高效、可移植**的特征要求。
9
10## 约定
11
12**规则**:编程时必须遵守的约定
13**建议**:编程时必须加以考虑的约定
14
15无论是“规则”还是“建议”,都必须理解该条目这么规定的原因,并努力遵守。
16
17## 例外
18
19在不违背总体原则,经过充分考虑,有充足的理由的前提下,可以适当违背规范中约定。
20例外破坏了代码的一致性,请尽量避免。“规则”的例外应该是极少的。
21
22下列情况,应风格一致性原则优先:
23**修改外部开源代码、第三方代码时,应该遵守开源代码、第三方代码已有规范,保持风格统一。**
24
25## 编程规范
26
27### 命名规范
28
29#### 规则1.1 须使用正确的英文拼写进行命名,禁止使用拼音拼写。
30
31**反例:**`xingming`、`zhanghao`
32
33**正例:**`username`、`account`
34
35#### 规则1.2 命名尽量少用缩写,除非是常见词或者业务线的领域词汇。比如:`context`可以简写成`ctx`,`request`可简写成`req`,`response`可简写成`resp`。
36
37**说明:** 完整的单词拼写可以避免不必要的阅读障碍。
38
39**例外:** 循环语种中可以使用`i`、`j`循环条件变量名。
40
41#### 规则1.3 类名、枚举名、命名空间名采用`UpperCamelCase`风格。
42
43**正例:**
44
45```javascript
46// 类名
47class User {
48  constructor(username) {
49    this.username = username;
50  }
51
52  sayHi() {
53    console.log(`hi, ${this.username}`);
54  }
55}
56
57// 枚举名
58const UserType = {
59  TEACHER: 0,
60  STUDENT: 1
61};
62
63// 命名空间
64const Base64Utils = {
65  encrypt: function(text) {
66    // todo encrypt
67  },
68  decrypt: function(text) {
69    // todo decrypt
70  }
71};
72```
73
74#### 规则1.4 变量名、方法名、参数名采用`lowerCamelCase`风格。
75
76**正例:**
77
78```javascript
79let msg = 'Hello world';
80
81function sendMsg(msg) {
82  // todo send message
83}
84
85function findUser(userID) {
86  // todo find user by user ID
87}
88```
89
90#### 规则1.5 静态常量名、枚举值名采用全部大写,单词间使用下划线隔开。
91
92**正例:**
93
94```javascript
95const MAX_USER_SIZE = 10000;
96
97const UserType = {
98  TEACHER: 0,
99  STUDENT: 1
100};
101```
102
103#### 建议1.6 避免使用否定的布尔变量名,布尔型的局部变量或方法须加上表达是非意义的前缀。
104
105**反例:**
106
107```javascript
108let isNoError = true;
109let isNotFound = false;
110function empty() {}
111function next() {}
112```
113
114**正例:**
115
116```javascript
117let isError = false;
118let isFound = true;
119function isEmpty() {}
120function hasNext() {}
121```
122
123### 代码格式
124
125#### 规则2.1 采用2个空格缩进,禁止使用`tab`字符
126
127**说明:** 只允许使用空格(space)进行缩进,每次缩进为2个空格。不允许使用Tab 符进行缩进。
128
129**正例:**
130
131```javascript
132const dataSource = [
133  {
134    id: 1,
135    title: 'Title 1',
136    content: 'Content 1'
137  },
138  {
139    id: 2,
140    title: 'Title 2',
141    content: 'Content 2'
142  }
143];
144
145function render(container, dataSource) {
146  if (!container || !dataSource || !dataSource.length) {
147    return void 0;
148  }
149
150  const fragment = document.createDocumentFragment();
151  for (let data of dataSource) {
152    if (!data || !data.id || !data.title || !data.content) {
153      continue;
154    }
155    const element = document.createElement("div");
156    const textNode = document.createTextNode(`${data.title}: ${data.content}`);
157    element.appendChild(textNode);
158    element.setAttribute("id", data.id);
159    fragment.appendChild(element);
160  }
161  container.appendChild(fragment);
162}
163
164```
165
166#### 规则2.2 行宽不超过`120`个字符
167
168**说明:** 建议每行字符数不要超过 120 个。如果超过120个字符,请选择合理的方式进行换行。
169
170**例外:** 如果一行注释包含了超过120 个字符的命令或URL,则可以保持一行,以方便复制、粘贴和通过grep查找; 预处理的 error 信息在一行便于阅读和理解,即使超过 120 个字符。
171
172#### 规则3.3 大括号的使用须遵循约定:
173
1741. 如果大括号内为空,则允许简写成`{}`,且无需换行;
1752. 左大括号前不换行,括号后换行;
1763. 右大括号前换行,括号后还有`else`、`catch`等情况下不换行,其他情况都换行。
177
178#### 规则3.4 条件语句和循环语句的实现必须使用大括号包裹,即使只有一条语句。
179
180**反例:**
181
182```javascript
183if (condition)
184  console.log('success');
185
186for(let idx = 0; idx < 5; ++idx)
187  console.log(idx);
188```
189
190**正例:**
191
192```javascript
193if (condition) {
194  console.log('success');
195}
196
197for(let idx = 0; idx < 5; ++idx) {
198  console.log(idx);
199}
200```
201
202#### 规则2.5 条件语句和循环语句都不允许写在同一行。
203
204**反例:**
205
206```javascript
207if (condition) { /* todo something */ } else { /* todo other */ }
208
209let idx = 0;
210while(idx < 10) console.log(idx);
211```
212
213**正例:**
214
215```javascript
216if (condition) {
217  /* todo something */
218} else {
219  /* todo other */
220}
221
222let idx = 0;
223while(idx < 10) {
224  console.log(idx);
225}
226```
227
228#### 规则2.6 `switch`语句的`case`和`default` 须缩进一层。
229
230**正例:**
231
232```javascript
233switch(condition) {
234  case 0:
235    doSomething();
236    break;
237  case 1: { // 可以带括号也可以不带
238    doOtherthing();
239    break;
240  }
241  default:
242    break;
243}
244```
245
246#### 规则2.7 表达式换行须保持一致性,运算符放行末。
247
248**说明:** 较长的表达式,不满足行宽要求时,需要在适当的位置进行换行。一般在较低优先级运算符或连接符后面阶段,运算符或连接符放行末。运算符、连接符放在行末,表示“未结束,后续还有”。
249
250**正例:**
251
252```javascript
253// 假设条件语句超出行宽
254if (userCount > MAX_USER_COUNT ||
255  userCount < MIN_USER_COUNT) {
256  doSomething();
257}
258
259const sum =
260  number1 +
261  number2 +
262  number3 +
263  number4 +
264  number5 +
265  number6 +
266  number7 +
267  number8 +
268  number9;
269```
270
271#### 规则2.8 多个变量定义和赋值语句不允许写在一行。
272
273**反例:**
274
275```javascript
276let maxCount = 10, isCompleted = false;
277
278let pointX, pointY;
279pointX = 10; pointY = 0;
280```
281
282**正例:**
283
284```javascript
285let maxCount = 10;
286let isCompleted = false;
287
288let pointX = 0;
289let pointY = 0;
290```
291
292#### 规则2.9 空格应该突出关键字和重要信息,避免不必要的空格。
293
294**说明:** 空格可以减低代码密度,增加代码的可读性。总体规则如下:
295
2961. `if`、`elseif`、`else`、`switch`、`while`、`for`等关键字之后加空格;
2972. 小括号内部两侧不加空格;
2983. 大括号内部两侧须加空格,但`{}`等简单场景例外;
2994. 多重括号之间不加空格;
3005. 一元操作符(`&`、`*`、`+`、`-`、`!`等)之后不加空格;
3016. 二元操作符(`=`、`+`、`-`、`*`、`/`、`%`、`|`、`&`、`||`、`&&`、`<`、`>`、`<=`、`>=`、`==`、`!=`、`===`、`!==`等)左右两侧加空格;
3027. 三元运算符(`?: `)的两侧添加空格;
3038. 前置或后置的自增、自减(`++`、`--`)和变量之间不加空格;
3049. 逗号(`,`)前面不加空格,后面加空格;
30510. 单行注释`//`后加空格;
30611. 行尾不加空格。
307
308#### 规则2.10 表达式语句须以分号结尾。
309
310**反例:**
311
312```javascript
313let username = 'jack'
314let birthday = '1997-09-01'
315
316console.log(`${username}'s birthday is ${birthday}`)
317```
318
319**正例:**
320
321```javascript
322let username = 'jack';
323let birthday = '1997-09-01';
324
325console.log(`${username}'s birthday is ${birthday}`);
326```
327
328#### 建议2.11 优先使用单引号包裹字符串。
329
330**正例:**
331
332```javascript
333let message = 'wolrd';
334console.log(message);
335```
336
337### 代码规范
338
339#### 规则3.1 声明变量时须使用`var`、`let`或`const`关键字进行声明,避免变量暴露到全局作用域上。
340
341**说明:** 不使用`var`、`let`或`const`关键字声明变量,会导致将变量暴露到全局作用域上,这样可能会覆盖全局作用域上的同名变量,进而引发GC无法有效回收内存的问题;另外,当变量中包含敏感信息时,暴露到全局作用域可能会导致信息泄露问题。另外,**尽量对所有的引用使用`const`,不要使用 `var`;如果你一定需要可变动的引用,使用 `let` 代替 `var`**。因为`const`和`let`的作用域更小,写代码更容易控制;const可确保无法对引用重新赋值,const引用的指针不可变,重新赋值会报错,避免了不小心的重新赋值给覆盖了。
342
343**反例:**
344
345```javascript
346function open() {
347  url = 'http://127.0.0.1:8080'; //url会暴露到全局作用域上
348  //todo something
349}
350open();
351console.log(url); //url可以被访问到,输出内容:http://127.0.0.1:8080
352```
353
354**正例:**
355
356```javascript
357// ES5.1使用var声明变量
358function open() {
359  var url = 'http://127.0.0.1:8080';
360  // todo something
361}
362open();
363console.log(url); //报错:Uncaught ReferenceError: url is not defined
364```
365
366```javascript
367// ES6中,优先推荐使用let和const关键字来声明变量
368function open() {
369  const url = 'http://127.0.0.1:8080';
370  //todo something
371}
372open();
373console.log(url); //报错:Uncaught ReferenceError: url is not defined
374```
375
376#### 规则3.2 函数块内须使用函数表达式声明函数
377
378**说明:** 虽然很多 JS 引擎都支持块内声明函数,但它不属于 ECMAScript规范,各个浏览器糟糕的实现相互不兼容,有些也与未来 ECMAScript草案相违背。另外,ECMAScript5不支持块作用域,对于所有的控制流都不是独立的作用域,其内部声明的变量或者函数都是在其父函数或者脚本的作用域中,导致块内函数或者变量的声明会存在覆盖现象。如果确实需要在块中定义函数,应该使用函数表达式来初始化。
379
380**反例:**
381
382```javascript
383function bar(name) {
384  if (name === "hotel") {
385    // 1、定义一个foo函数,其作用域不是if代码块,而是bar函数作用域。
386    function foo() {
387      console.log("hotel foo A");
388    }
389  } else {
390    // 2、再重复定义一次foo函数,覆盖上面if条件分支下的foo函数定义。
391    function foo() {
392      console.log("hotel foo 2");
393    }
394  }
395  foo && foo();
396}
397bar("hotel"); // 输出结果总是显示"hotel foo 2"
398```
399
400**正例:**
401
402```javascript
403function bar(name) {
404  var foo;
405  if (name == "hotel") {
406    foo = function () {
407      console.log("hotel foo 1");
408    };
409  } else {
410    foo = function () {
411      console.log("hotel foo 2");
412    }
413  }
414  foo && foo();
415}
416bar("hotel"); // 正确输出"hotel foo 1"
417```
418
419#### 规则3.3 禁止封装基本类型
420
421**说明:** JavaScript中有五种基本数据类型:Undefined、Null、Boolean、Number和String。基本数据类型的值是不可更改的。JavaScript中使用基本数据类型对象只是值,不包含器封装对象的方法和属性,在不需要使用属性和方法的时候,不需要使用其封装类型。
422
423**反例:**
424
425```javascript
426var isShow = new Boolean(false);
427if (isShow) {
428  alert('hi'); //被执行,界面弹出显示:hi
429}
430```
431
432**正例:**
433
434```javascript
435var isShow = false;
436if (isShow) {
437  alert('hi');
438}
439```
440
441#### 规则3.4 禁止使用`with`
442
443**说明:** 使用 with让你的代码在语义上变得不清晰,因为with的对象,可能会与局部变量产生冲突,从而改变程序原本的用义。
444
445**反例:**
446
447```javascript
448var foo = { x: 5 };
449with(foo) {
450  var x = 3;
451  console.log(x); //输出:5
452}
453```
454
455#### 规则3.5 `this`仅可在对象构造函数、方法、闭包中使用
456
457**说明:** 在JavaScript里面,this指针代表的是执行当前代码的对象的所有者。this具有特殊的语义:
458
459+ 全局对象(大多数情况下)
460+ 调用者的作用域(使用eval时)
461+ DOM 树中的节点(添加事件处理函数时)
462+ 新创建的对象(使用一个构造器)
463+ 其他对象(如果函数被 call() 或 apply())
464
465```javascript
466var User = function(username) {
467  this.username = username;
468};
469var user = new User('John');
470console.log(user.username); // 输出:John
471
472var ClickCounter = {
473  value: 0,
474  click: function() {
475    ++this.value;
476  },
477  getValue() {
478    return this.value;
479  }
480};
481console.log(Counter.getValue()); //输出:0
482Counter.click();
483console.log(Counter.getValue()); //输出:1
484```
485
486#### 规则3.6 禁止使用IE下的条件注释
487
488**说明:** 在IE下使用`\@cc_on`语句或使用`\@if`或`\@set`语句可以激活条件编译。尽管可以通过注释来兼容IE以外的浏览器,但它妨碍自动化工具的执行,因为在运行时,它们会改变JavaScript 语法树。
489
490**反例:**
491
492```javascript
493var f = function () {
494  /*@cc_on @*/
495  /*@if (@_jscript_version >= 4)
496       alert("JavaScript version 4 or better");
497    @else @*/
498  alert("Conditional compilation not supported by this scripting engine.");
499  /*@end @*/
500};
501```
502
503#### 规则3.7 禁止修改内置对象的原型
504
505**说明:** 内置对象作为一套公共接口,具有约定俗成的行为方式,修改其原型,可能破坏接口语义,或导致调试时的诡异现象。
506
507**反例:**
508
509```javascript
510Array.prototype.indexOf = function () { return -1 }
511var arr = [1, 1, 1, 1, 1, 2, 1, 1, 1];
512console.log(aar.indexOf(2)); // 输出:-1
513```
514
515#### 规则3.8 禁止直接使用`Object.prototype`的内置属性
516
517**说明:** ECMAScript5.1新增了`Object.create`,它创建一个新对象,使用现有的对象来提供新创建的对象的proto。`Object.create(null)`是用于创建用作map的对象的常用模式。当该对象具有`Object.prototype`同名的属性时,可能会导致意外行为或漏洞。例如,web服务器解析来自客户端的JSON输入并且使用`hasOwnProperty`直接调用生成的对象是不安全的,因为恶意客户端可能会发送类似的JSON值`'{ "hasOwnProperty": 1 }'` 并导致服务器崩溃。
518
519**反例:**
520
521```javascript
522var hasBarProperty = foo.hasOwnProperty("bar");
523var isPrototypeOfBar = foo.isPrototypeOf(bar);
524var barIsEnumerable = foo.propertyIsEnumerable("bar");
525```
526
527**正例:**
528
529```javascript
530var hasBarProperty = Object.prototype.hasOwnProperty.call(foo, "bar");
531var isPrototypeOfBar = Object.prototype.isPrototypeOf.call(foo, bar);
532var barIsEnumerable = {}.propertyIsEnumerable.call(foo, "bar");
533```
534
535#### 规则3.9 使用`Object.getPrototypeOf`函数而不要使用`_proto_`
536
537说明:ES5引入`Object.getPrototypeOf`函数作为获取对象原型的标准API,但在这之前大量的JavaScript引擎早就使用一个特殊的`proto`属性来达到相同的目的。然而,`proto`它本质上是一个内部属性,而不是一个正式的对外的API,目前浏览器必须部署这个属性,但其他运行环境不一定部署,因此,该属性并不是完全兼容的。例如,对于拥有null原型的对象,不同的环境处理的不一样。
538
539```javascript
540var empty = Object.create(null);
541"_proto_" in empty; //有些环境返回false,有些环境返回true
542```
543
544所以无论从语义的角度,还是从兼容性的角度,都不要使用proto这个属性,而是使用`Object.getPrototypeOf()`来代替。无论在什么环境中,`Object.getPrototypeOf`函数都是有效的,而且也是提取对象原型更加标准、可移植的方法。
545
546#### 规则3.10 不要使用函数构造器创建函数
547
548说明:定义函数的方法包括3种:函数声明、Function构造函数和函数表达式。不管用哪种方法定义函数,它们都是Function对象的实例,并将继承Function对象所有默认或自定义的方法和属性。以函数构造器创建函数的方式类似于字符串`eval()`,可以接受任何字符串形式作为它的函数体,这就会有安全漏洞的风险。
549
550**反例:**
551
552```javascript
553var func = new Function('a', 'b', 'return a + b');
554var func2 = new Function('alert("Hello")');
555```
556
557**正例:**
558
559```javascript
560function func(a, b) {
561  return a + b;
562}
563
564var func2 = function(a, b) {
565  return a + b;
566}
567```
568
569#### 建议3.11 在使用原型`prototype`实现继承时,尽量使用现有稳定的库方法而非自行实现
570
571**说明:** 多级原型结构是指JavaScript中的继承关系。当定义一个D类,且把B类作为其原型,那么就获得了一个多级原型结构。这些原型结构会变得很复杂。使用现有的稳定的库方法如`the Closure`库的`goog.inherits()`或其他类似的函数,可避免不必要的编码失误,将是更好的选择。
572
573#### 建议3.12 定义类时,应在原型下定义方法,在构造方法内定义属性
574
575**说明:** JavaScript中有多种方法可以给构造函数添加方法或成员,但是使用原型定义方法,可以降低内存占用,提高运行效率。
576
577**反例:**
578
579```javascript
580function Animals() {
581  this.walk = function() {}; // 这样会导致每个实例上都创建一个walk方法
582}
583```
584
585**正例:**
586
587```javascript
588function Animals() {}
589
590Animals.prototype.walk = function() {};
591```
592
593#### 建议3.13 使用闭包时,应避免构成循环引用,导致内存泄露
594
595**说明:** JavaScript是一种垃圾收集式语言,其对象的内存是根据对象的创建分配给该对象的,并且会在没有对该对象的引用时由浏览器收回。JavaScript的垃圾收集机制本身并没有问题,但浏览器在为DOM对象分配和恢复内存的方式上有些出入。IE和Firefox均使用引用计数来为DOM对象处理内存。在引用计数系统中,每个所引用的对象都会保留一个计数,以获悉有多少对象正在引用它。如果计数为零,那么该对象就会被销毁,其占用的内存也会返回给堆。虽然这种解决方案总的来说还算有效,但是在循环引用方面却存在一些盲点。 当两个对象互相引用时,就构成了循环引用,其中对象的引用计数值都被赋为1。在纯垃圾收集系统中,循环引用问题不大:如果涉及的两个对象中有一个对象被任何其他对象引用,那么这两个对象都将被垃圾收集。而在引用计数系统中,这两个对象都不能被销毁,原因是引用计数永远不能为0。在同时使用了垃圾收集和引用计数的混合系统中,将会发生泄漏,因为系统不能正确识别循环引用。在这种情况下,DOM对象和JavaScript对象均不能被销毁。 循环引用很容易创建。在JavaScript最为方便的编程结构之一——闭包中,循环引用尤其突出。闭包会持有其外部作用域(包括局部变量、参数及方法)的引用,当闭包本身又被作用域成员(常见于DOM对象)持有时便构成循环引用,进一步导致内存泄露。
596
597**反例:**
598
599```javascript
600function setClickListener(element, a, b) {
601  element.onclick = function() {
602    // 在这里用到a和b
603  };
604};
605```
606
607在上述代码中,即使没有使用element,闭包也保留了element、a和b的引用。由于element也保留了对闭包的引用,因此产生了循环引用,不能被GC回收。
608
609**正例:**
610
611```javascript
612function setClickListener(element, a, b) {
613  element.onclick = createHandler(a, b);
614}
615
616function createHandler(a, b) {
617  // 通过添加另外一个函数来避免闭包本身,进而组织内存泄露
618  return function() {
619    // 在这里用到a和b
620  }
621}
622```
623
624#### 建议3.14 警惕JavaScript的浮点数
625
626**说明:** JavaScript具有单一数字类型:`IEEE 754`双精度浮点数。拥有单一数字类型是JavaScript的最佳功能之一。多种数字类型可能是复杂性,混淆和错误的根源。但是,二进制浮点类型有一个最大的缺点是,它不能准确地表示小数部分,会导致让人意外的精度问题,见下面示例。
627
628示例代码1:
629
630```javascript
631console.log(0.1 + 0.2 === 0.3); // 输出:false。所以通常禁止直接使用==或===来比较浮点数。
632```
633
634示例代码2:
635
636```javascript
637var sum1 = (0.1 + 0.2) + 0.3;
638console.log(sum1); // 输出:0.6000000000000001
639
640var sum2 = 0.1 + (0.2 + 0.3);
641console.log(sum2); // 输出:0.6。所以对于二进制浮点数,(a + b) + c 不能保证产生于a + (b + c)相同的结果。
642```
643
644有效的解决方法有以下几种:
645
6461. 尽可能的采用整数值运算,因为整数在表示是不需要舍入;
647
6482. 使用JavaScript的原生方法`Number.prototype.toFixed(digits)`,`digist`参数表示小数点后数字的个数,不使用指数法,在必要时会进行四舍五入。使用该方法,在判断浮点数运算结果前对计算结果进行精度缩小。示例代码如下所示:
649
650   ```javascript
651   parseFloat(0.1 + 0.2).toFixed(1); //0.3
652   ```
653
6543. ES6 新增了一个极小的常量`Number.EPSILON =.220446049250313e-16 `,约等于`0.00000000000000022204`。`Number.EPSILON`的出现是用来判断浮点数的计算误差,如果浮点数计算得到的误差不超过`Number.EPSILON`的值,就表示可以接受这样的误差。示例代码如下所示:
655
656   ```javascript
657   function isNumberEquals(one, other) {
658     return Math.abs(one - other) < Number.EPSILON;
659   }
660   var one = 0.1 + 0.2;
661   var other = 0.3;
662   console.log(isNumberEquals(one, other)); // 输出:true
663   ```
664
6654. 使用一些支持精确运算的类库方法,如`math.js`
666
667   ```html
668   <!DOCTYPE html>
669   <html>
670     <head>
671       <script src="https://cdnjs.cloudflare.com/ajax/libs/mathjs/5.0.0/math.js"></script>
672       <script type="text/javascript">
673         function fn_click() {
674           math.config({
675             number: "BigNumber",
676           });
677           var result = math.add(math.bignumber(0.1), math.bignumber(0.2));
678           alert(result);
679         }
680       </script>
681     </head>
682     <body>
683       <input type="button" value="mathjs(0.1+0.2)" onclick="fn_click();" />
684     </body>
685   </html>
686
687   ```
688
689#### 建议3.15 不要使用可变参数的Array构造函数
690
691**说明:** 通常不鼓励使用构造函数`new Array`的方法来构造新数组,因为当构造函数只有一个参数的时候,可能会导致意外情况,另外Array的全局定义也可能被重新修改,所以提倡使用数组文字表示法来创建数组,也就是`[]`表示法。
692**反例:**
693
694```javascript
695const arr1 = new Array(x1, x2, x3);
696const arr2 = new Array(x1, x2);
697const arr3 = new Array(x1);
698const arr4 = new Array();
699```
700
701除了第三种情况之外,其他都可以正常工作,如果`x1`是个整数,那么`arr3`就是长度为`x1`,值都为`undefined`的数组。如果`x1`是其他任何数字,那么将抛出异常,如果它是其他任何东西,那么它将是单元数组。
702
703**正例:**
704
705```javascript
706const arr1 = [x1, x2, x3];
707const arr2 = [x1, x2];
708const arr3 = [x1];
709const arr4 = [];
710```
711
712这种写法,就会省去很多麻烦。
713
714同理,对于对象,同样不要使用`new Object()`,而是使用`{}`来创建。
715
716#### 规则3.16 构建字符串时,优先使用字符串模板而不是字符串链接。
717
718**说明:** 模板字符串表达更简洁,更具可读性。
719
720**反例:**
721
722```javascript
723function sayHi(name) {
724  console.log('hi, ' + name);
725}
726```
727
728**正例:**
729
730```javascript
731function sayHi(name) {
732  console.log(`hi, ${name}`);
733}
734```
735
736#### 规则3.17 数组遍历采用`for...of`,对象遍历采用`for...in`。
737
738**反例:**
739
740```javascript
741let numbers = [1, 2, 3, 4];
742let sum = 0;
743for (let number in numbers) {
744  sum += number;
745}
746// sum === 00123;
747```
748
749**正例:**
750
751```javascript
752let numbers = [1, 2, 3, 4];
753let sum = 0;
754for (let number of numbers) {
755  sum += number;
756}
757// sum === 10
758```
759
760