本章节讨论使用ES6编码风格到代码中需要注意的点。通过记住这些关键点,可以让我们写出更优美的、可读性更强的JavaScript E6风格的代码。
ES6 提出了两个新的声明变量的命令: let 和 const。其中,let可以完全取代var,因为两者语义相同,而且let没有副作用。
var命令存在变量提升的特性,而let没有这个命令。
所谓变量提升,即指变量可以先使用,再声明,显然,这种编码规范非常不适合阅读。
在let和const之间,优先使用const。
let应出现在单线程模块代码内,而const则非常适合多线程。
// bad
var a = 1, b = 2, c = 3;
// good
const a = 1;
const b = 2;
const c = 3;
// best
const[a, b, c] = [1, 2, 3];
静态字符串一律使用单引号或者反引号,不推荐使用双引号。
动态字符串(字符串模板)使用反引号。
// bad
const a = "zhaoyi";
const b = a + ", hello.";
// little good
const c = `zhaoyi`;
// very good
const a = 'zhaoyi';
const b = `zhaoyi,${a}, I say your name twice`;
1、使用数组成员对变量进行赋值时,优先使用解构赋值。
const arr = ['I', 'love', 'you'];
// bad
const one = arr[0];
const two = arr[1];
const three = arr[2];
// good
const [first, second, third] = arr;
// test
console.log(first, second, third);// I love you
2、函数的参数如果是对象的成员,优先使用解构赋值。
// bad
function getUserInfo(user){
const name = user.name;
const age = user.age;
}
// good
function getUserInfo2(user){
const {name, age} = user;
console.log(name, age); //
}
// test
getUserInfo2({name: 'zhaoyi', age: 20}); // zhaoyi 20
3、如果函数返回多个值,优先使用对象的结构赋值,而不是数组的结构赋值。这样便于以后添加返回值,以及更改返回值的顺序。
// bad
function getInfo(input){
return [left, right];
}
// good
function getInfo2(input){
return {left, right};
}
// 此处使用对象的解构赋值方式接收返回结果
const {left, right} = getInfo2('test');
1、单行定义的对象,最后一个成员不以逗号结尾。多行定义的对象,最后一个成员以逗号结尾。
// bad
const a1 = {k1: v1, k2: v2, k3: v3,};
// good
const a2 = {k1: v1, k2: v2, k3: v3};
// bad
const b1 = {
k1: v1,
k2: v2
};
// good
const b2 = {
k1: v1,
k2: v2,
};
2、对象尽量静态化,一旦定义,就不得随意添加新的属性。如果添加属性不可避免,要使用assign方法。
// bad
const a = {};
a.attr = 3;
// if reshape anavoidable(若无可避免)
const b = {};
Object.assign(b, {atrr: 3});
// good
const c = {attr: null};
c.attr = 3;
// test
console.log(a); //{attr: 3}
console.log(b); //{attr: 3}
console.log(c); //{attr: 3}
3、如果对象的属性名是动态的(所谓动态是指,需要通过计算得到),可以在创建对象的时候使用属性表达式定义。(此种情况在开发时,并不多见。)
使用扩展运算符(...)复制数组。
// bad
function copyArr1(arr){
const itemCopy = [];
for (let index = 0; index < arr.length; index++) {
itemCopy[index] = arr[index];
}
return itemCopy;
}
// good
function copyArr2(arr){
return [...arr];
}
// test
const arr = ['z', 'y', 'z'];
console.log(copyArr1(arr)); // ["z", "y", "z"]
console.log(copyArr2(arr)); // ["z", "y", "z"]
使用Array.from 方法将类似数组的对象转为数组。
const obj = { "0": "a", "1": "b", length: 2};
const arr = Array.from(obj);
// test
console.log(arr); // ["a", "b"]
1、立即执行函数可以写成箭头函数的形式。
(() => {
console.log('this is a good night.');
})();
2、在需要使用函数表达式的场合,尽量用箭头函数代替。因为这样可以更简洁,而且绑定了this。
// bad
const sayHello = ['a', 'b', 'c'].map(function (w){
return 'Hello, ' + w;
})
// good
const sayHello2 = ['a', 'b', 'c'].map(w => {
return 'Hello, ' + w;
});
// test
console.log(sayHello2); // ["Hello, a", "Hello, b", "Hello, c"]
3、箭头函数取代Function.prototype.bind,不应再用self/_this/that绑定this.
// bad
const self = this;
const boundMethod = function(...params){
return method.apply(self, params);
}
// acceptable
const boundMethod2 = method.bind(this);
// best
const boundMehod3 = (...params) => method.apply(this, params);
4、单行简单、无需复用的函数,建议采用箭头函数。如果函数体较为复杂,行数较多,还是应采用传统的函数写法。
5、所有配置项都应该集中在一个对象,放在到最后一个参数,布尔值不可以直接作为参数。
// bad
function divide(a, b, option = false){
}
// good
function divide(a, b, {option = false} = {}){
}
6、不要在函数体内使用arguments变量,使用rest运算符(...)代替。因为rest运算符可以显示声明我们想要获取的参数,而且arguments是一个类似数组的对象,而rest元素安抚可以提供一个真正的数组。
// bad
function f1(){
const args = Array.prototype.slice.call(arguments);
return args.join('-');
}
// good
function f2(...args){
return args.join('-');
}
// test
console.log(f1(1, 2, 3)); // 1-2-3
console.log(f2(1, 2, 3)); // 1-2-3
扩展运算符用三个点号表示,功能是把数组或类数组对象展开成一系列用逗号隔开的值;而rest运算符也是三个点号,不过其功能与扩展运算符恰好相反,把逗号隔开的值序列组合成一个数组。rest是剩余的意思。
7、使用默认值语法设置函数参数的默认值。
// bad
function handleThings(opts){
opts = opts || {};
// ...
}
// good
function handleThings2(opts = {}){
// ...
}
Map和Object给人的感觉是同一个数据类型,但是在实际语义还需要更为准确的区分,原则如下:
Map拥有内建的遍历机制(实现了Iterator结构)
// Map拥有许多初始化方式,这里使用数组成员为两个长度的数组进行初始化(第一个元素为K,第二个元素为V)
let map = new Map([['k1', 'I'], ['k2', 'love'], ['k3', 'your']]);
// 遍历K
for(const key of map.keys()){
console.log(key);
// k1
// k2
// k3
}
// 遍历V
for (const value of map.values()) {
console.log(value);
// I
// love
// you
}
// 遍历K-V
for (const item of map.entries()) {
console.log(item);
// ['k1', 'I']
// ['k2', 'love']
// ['k3', 'your']
}
1、总是用Class取代需要prototype的操作。因为Class的写法更简洁,更易于理解。接触过Java、C#比较多的朋友想必更喜欢这样的类语法方式。
// bad
function Queue1(contents = []){
this._queue = [...contents];
}
Queue1.prototype.pop = function(){
const value = this._queue[0];
this._queue.splice(0, 1);
return value;
}
// good
class Queue {
constructor(contents = []){
// 这里为什么不用this._queue = contents;呢?
// 读过effective java的朋友想必知道一个规则:
// 那就是在设计构造函数时,若传入的参数中有可变类型(对象、数组),
// 则构造函数内部接收此参数时应使用这个对象的拷贝。
// 这样可以避免外部参数对象的更改影响到类本身的实例。
// 因此,此处的contents需要拷贝一个复制在进行赋值。
this._queue = [...contents];
}
pop() {
const value = this._queue[0];
this._queue.splice(0, 1);
return value;
}
}
// test
q = new Queue([1, 2, 3]);
console.log(q.pop()); // 1
console.log(q.pop()); // 2
console.log(q.pop()); // 3
console.log(q.pop()); // undefined
2、使用extends实现继承,因为这样可以更简单,不存在破坏instanceof运算的危险。
// Queue为上一个例子的类
class PeekQueue extends Queue{
// ...
}
1、Module语法是JS模块的标准写法,要坚持使用这种写法。使用import取代require。
// bad
const ma = require('moduleA');
const f1 = ma.f1;
const f2 = ma.f2;
// good
import {f1, f2} from 'moduleA';
2、使用export取代module.export
// bad
module.exports = SomeObj;
// good
export default SomeObj;
3、如果模块只有一个输出值,就使用 export default; 若有镀铬,就不要使用 export default, 不要同时使用 export default 和 普通的 export,虽然规则上允许此种编写代码的方式。
4、不要在模块中使用通配符,因为这样可以确保模块中有一个默认输出:export default。
// bad
import * as myObject './someModule';
// good
import myObject from './someModule';
5、如果模块默认输出一个函数,函数的首字母应该小写。
function someFunction(){
// ...
}
export default someFunction;
6、 如果模块默认输出一个对象,对象名的首字母应该大写。
const someObj = {
// ...
}
export default SomeObj;
前面说了那么多规则,其实只是规则范本的冰山一角,真正想要写出格式优美、符合主流厂商规范的代码,仅仅靠我们的自觉是不够的。
有没有什么类似软件编译工具检查代码正确性来检查代码编写规范的软件呢,答案是有的。
ESLint就是这样的一款检查工具。可以用于保证写出语法正确、风格统一的代码。
以下是安装ESLink的教程(确保您的环境已经安装了npm),当然,如果您使用一些脚手架工具(例如@vue-cli)等方式生成的项目,那么这样的项目都是提供了可选的eslint插件的。当前版本为: v6.6.0。该版本的eslint提供了更为简单的配置方式,可以参考 https://eslint.bootcss.com/docs/user-guide/getting-started/ 进行配置。以下是一个粗略的配置步骤
1、安装所需插件
$ npm install eslint -g
2、生成package.json文件
$ npm init
该方法会在当前目录生成package.json文件,该文件类似于环境的说明文件。
3、生成eslint配置文件
$ eslint --init
该命令会询问你使用哪种类型的配置(通过上下箭头选取)
其他的选项根据你的需要进行选取即可。完成选择之后,会自动下载所需要的依赖包。
生成的配置文件内容大致如下:
module.exports = {
env: {
browser: true,
es6: true,
},
extends: [
'airbnb-base',
],
globals: {
Atomics: 'readonly',
SharedArrayBuffer: 'readonly',
},
parserOptions: {
ecmaVersion: 2018,
sourceType: 'module',
},
rules: {
},
};
我们在该配置文件中可以修改验证规则,具体的内容同样参考上面给出的链接。
4、在当前目录下,创建一个js文件
// index.js
var unused = '灰与幻想的格林姆迦尔';
function hello(){
var message = "Hello, zhaoyi!";
alert(message);
}
hello();
5、通过eslint验证代码编写正确性
$ eslint index.js
1:12 error Expected linebreaks to be 'LF' but found 'CRLF' linebreak-style
2:1 error Unexpected var, use let or const instead no-var
2:5 error 'unused' is assigned a value but never used no-unused-vars
2:27 error Expected linebreaks to be 'LF' but found 'CRLF' linebreak-style
3:1 error Expected linebreaks to be 'LF' but found 'CRLF' linebreak-style
4:17 error Missing space before opening brace space-before-blocks
4:18 error Expected linebreaks to be 'LF' but found 'CRLF' linebreak-style
5:1 error Expected indentation of 2 spaces but found 4 indent
5:5 error Unexpected var, use let or const instead no-var
5:19 error Strings must use singlequote quotes
5:36 error Expected linebreaks to be 'LF' but found 'CRLF' linebreak-style
6:1 error Expected indentation of 2 spaces but found 4 indent
6:5 warning Unexpected alert no-alert
6:20 error Expected linebreaks to be 'LF' but found 'CRLF' linebreak-style
7:2 error Expected linebreaks to be 'LF' but found 'CRLF' linebreak-style
8:1 error Trailing spaces not allowed no-trailing-spaces
8:3 error Expected linebreaks to be 'LF' but found 'CRLF' linebreak-style
9:9 error Trailing spaces not allowed no-trailing-spaces
9:10 error Newline required at end of file but not found eol-last
其中,有一种错误其实是因为git文件格式转化的问题:
... linebreak-style
我们可以在配置文件中移除该检测:在rules下添加'linebreak-style': [0, 'error', 'windows'].
rules: {
'linebreak-style': [0, 'error', 'windows']
}
继续运行检测命令,可以看到如下的输出:
2:1 error Unexpected var, use let or const instead no-var
2:5 error 'unused' is assigned a value but never used no-unused-vars
5:1 error Expected indentation of 2 spaces but found 4 indent
5:5 error Unexpected var, use let or const instead no-var
5:19 error Strings must use singlequote quotes
6:1 error Expected indentation of 2 spaces but found 4 indent
6:5 warning Unexpected alert no-alert
8:1 error Trailing spaces not allowed no-trailing-spaces
9:9 error Trailing spaces not allowed no-trailing-spaces
可以看到,我们许多不规范的操作都会警告了。比如缩进不要用四空格(其实是我们的编译器自带,而且我们习惯了的),不要加多余的空格,以及删掉没有使用过的声明变量,不推荐使用var类型等等。
如果觉得合情合理,那么遵循之;如果觉得缩进这些不符合自己的习惯,也可以通过配置文件进行关闭/修改等操作达到预期的效果。
参考:ES6标准入门(第三版)——阮一峰
目前我们现在用的前后端分离模式属于第一阶段,下一阶段可以在前端工程化方面,对技术框架的选择、前端模块化重用方面,可多做考量。也就是要迎来“==前端为主的 MV* 时代==”。
当您必须使用匿名函数,请使用箭头函数表示法,它创建了一个在 this 上下文中执行的函数的版本,这通常是你想要的,而且这样的写法更为简洁。如果你有一个相当复杂的函数,你或许可以把逻辑部分转移到一个声明函数上。
standard是一个开源的JS代码规范库,制定了所谓standard(标准)的JS代码规范,配合编辑器插件可以实时检查代码规范以及语法错误,通过执行命令检查代码规范以及语法错误,自动修复(可以直接修复的)不合规范的代码,使其符合规范
对于一个多人团队来说,制定一个统一的规范是必要的,因为个性化的东西无法产生良好的聚合效果,规范化可以提高编码工作效率,使代码保持统一的风格,以便于代码整合和后期维护。
为了编写可维护的代码,我们把很多函数分组,分别放到不同的文件里,这样,每个文件包含的代码就相对较少,很多编程语言都采用这种组织代码的方式。在Node环境中,一个.js文件就称之为一个模块(module)
引号的使用,单引号优先(如果不是引号嵌套,不要使用双引号)、空格的使用问题:(关键字后 符号后 排版 函数 赋值符号= )等、不写没有使用过的变量,如果定义了一个变量,后来一直没有参与过运算,那么不应该定义这个变量...
嵌套的节点应该缩进;在属性上,使用双引号,不要使用单引号;属性名全小写,用中划线做分隔符;不要在自动闭合标签结尾处使用斜线(HTML5 规范 指出他们是可选的);不要忽略可选的关闭标签;
CommonJS规范规定,每个模块内部,module变量代表当前模块。这个变量是一个对象,它的exports属性(即module.exports)是对外的接口。加载某个模块,其实是加载该模块的module.exports属性。module.exports属性表示当前模块对外输出的接口
W3C通过设立领域(Domains)和标准计划(Activities)来组织W3C的标准活动,围绕每个标准计划,会设立相关的W3C工作组织(包括工作组、社区组、商务组等)。W3C会根据产业界的标准需求调整Domains和Activity的设置及相关的工作组设置。
不要使用 @import 与 <link> 标签相比,@import 指令要慢很多,不光增加了额外的请求次数,还会导致不可预料的问题。CSS有些属性是可以缩写的,比如padding,margin,font等等,这样精简代码同时又能提高用户的阅读体验。
内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!