ECMAScript 2020新增特性

ECMAScript 2020新特性

Jordan Harband提出的String.prototype.matchAll

String.prototype上的match()方法仅返回完全匹配,但是没有返回关于特定正则组的任意信息。感谢Jordan Harband关于String.prototype.matchAll的提案,可以返回比match()多很多的信息。返回的迭代器除了精确匹配外还给了我们访问所有的正则匹配捕获组。你还记得Gorkem Yakin和Daniel Ehrenberg添加到ECMAScript 2018的具名捕获组吗?matchAll()方法和此能很好的协调。通过下面例子来解释一下。

1
2
3
4
5
6
const text = "From 2019.01.29 to 2019.01.30";
const regexp = /(?<year>\d{4}).(?<month>\d{2}).(?<day>\d{2})/gu;
const results = text.match(regexp);

console.log(results);
// [ '2019.01.29', '2019.01.30' ]
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
const text = "From 2019.01.29 to 2019.01.30";
const regexp = /(?<year>\d{4}).(?<month>\d{2}).(?<day>\d{2})/gu;
const results = Array.from(text.matchAll(regexp));

console.log(results);
// [
// [
// '2019.01.29',
// '2019',
// '01',
// '29',
// index: 5,
// input: 'From 2019.01.29 to 2019.01.30',
// groups: [Object: null prototype] { year: '2019', month: '01', day: '29' }
// ],
// [
// '2019.01.30',
// '2019',
// '01',
// '30',
// index: 19,
// input: 'From 2019.01.29 to 2019.01.30',
// groups: [Object: null prototype] { year: '2019', month: '01', day: '30' }
// ]
// ]

Domenic Denicola提出的import()

不同于ECMAScript 2015中介绍的静态模块,Domenic Denicola提案的动态导入可以实现按需加载。这个类似函数的格式(不是继承自Function .prototype)返回一个很强大的promise。使用场景比如: 按需导入,在一个脚本中计算模块名并加载执行变得可能。

1
2
3
4
5
const modulePage = 'page.js';
import(modulePage)
.then((module) => {
module.default();
});
1
2
3
4
5
(async () => {
const helpersModule = 'helpers.js';
const module = await import(helpersModule)
const total = module.sum(2, 2);
})();

Daniel Ehrenberg提出的BigInt-任意精度整数

感谢Daniel Ehrenberg, Number.MAX_SAFE_INTEGER不再是JavaScript中的一个限制。BigInt是一个能表示任意精度整数的新基础类型。你可以通过使用BigInt方法或者在一个数字后添加n后缀来把一个数字转换为一个新的bigint类型。

1
2
3
4
5
6
7
8
Number.MAX_SAFE_INTERGER
// 9007199254740991

Number.MAX_SAFE_INTEGER + 10 -10
// 9007199254740990 👎

BigInt(Number.MAX_SAFE_INTEGER) + 10n -10n
// 9007199254740991n 👍

Jason Williams, Robert Pamely and Mathias Bynens提出的Promise.allSettled

自从ECMAScript 2015以来,JavaScript仅支持两种promise组合: Promise.all()Promise.race()。感谢Jason Williams, Robert Pamely and Mathias Bynens,现在我们可以使用Promise.allSettled()。用这个方法来处理所有promise都解决时的场景(不管成功或失败)。看看下面的例子,并没有使用catch捕获异常!

1
2
3
4
5
Promise.allSettled([
fetch("https://api.github.com/users/pawelgrzybek").then(data => data.json()),
fetch("https://api.github.com/users/danjordan").then(data => data.json())
])
.then(result => console.log(`All profile settled`));

还有Promise.any()有潜力很快进入ECMAScript规范中,在文章“Promise组合解释”中介绍了相关内容。

Jordan Harband提出的globalThis

那么在JavaScript中什么是全局的this?是在浏览器中的window,在worker中的self,在Nodejs中的global或者其他… 这种混乱结束了!感谢Jordan Harband,我们现在可以使用globalThis关键字了。

Kevin Gibbons提出的for-in机制

ECMAScript遗留了一个关于for-in循环顺序的详细描述。感谢Kevin Gibbons所付出的努力,为for-in机制定义了一系列规则。(原文: Thanks to Kevin Gibbons who finally put some TLC and defined a set in stone set of rules for for-in mechanics.)

Gabriel Isenberg, Claude Pache and Dustin Savery提出的optional chaining

读取层次很深的对象属性时通常是容易出错并且对应代码也不易阅读。感谢Gabriel Isenberg, Claude Pache and Dustin Savery,这件事情现在变得简单了。如果你是一个TypeScript用户,那么你不会发现什么新的特性,因为在3.7版本中TypeScript已经实现了这个特性。喜欢!

1
2
3
4
5
// 之前
const title = data && data.article && data.article.title

// 现在
const title = data?.article?.title

Gabriel Isenberg 提出的空值联合

空值联合添加了一个新的短路原则操作符来处理默认值。Gabriel Isenberg做了很棒的工作。这个特性结合optional chanining特性使用。不同于||操作符,空值联合操作符??仅在左边的值为严格的nullundefined时起左右。

1
2
3
4
5
"" || "default value"
// default value

"" ?? "default value"
// ""
1
const title = data?.article?.title ?? "What's new in ECMAScript 2020"

Domenic Denicola提出的import.meta

Domenic Denicola提出的import.meta提案添加一个host相关的元数据对象到当前执行的模块中。

1
2
console.log(import.meta.url)
// file:///Users/pawelgrzybek/main.js

EXPORT * AS NS FROM “MOD”

这是一个添加到规范中的有用特性,可以让开发者导出其他模块命名空间下的对象到一个新的名称下。

1
export * as ns from "mod"

参考

项目开发中团队规范的那些事

项目开发中团队规范的那些事,记录一下项目中可以通过工具来约定和处理的规范

欢迎访问个人博客-项目开发中团队规范的那些事

  • editorconfig的使用
  • jsdoc的使用
  • .gitignore的使用
  • eslint的使用
  • prettier的使用

editorconfig的使用

你是否有遇到过多人协作的项目中,大家的缩进风格不一样,有人用两个空格,有人用4个空格,也有人用tab缩进,而感到烦恼的。editorconfig这个开源项目就是为此而生的。

官方的介绍如下:

EditorConfig helps maintain consistent coding styles for multiple developers working on the same project across various editors and IDEs. The EditorConfig project consists of a file format for defining coding styles and a collection of text editor plugins that enable editors to read the file format and adhere to defined styles. EditorConfig files are easily readable and they work nicely with version control systems.

使用思路:

通过一个配置文件,让不同的编辑器或者IDE自动识别缩进、字符编码格式等风格并保持统一。目前市面上绝大部分的编辑器和IDE都已经支持了,你使用的编辑器是否原生支持还是要安装插件,可以去官网上看一眼。

.edittorconfig文件配置如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 表明是最顶层的配置文件,发现设为true时,才会停止查找.editorconfig文件,否则会往上一层目录继续查找
root = true

# 对所有文件生效
[*]
# 编码格式,支持latin1、utf-8、utf-8-bom、utf-16be和utf-16le,通常使用utf-8
charset = utf-8
# 缩进类型, 支持space 空格, tab 制表符
indent_style = space
# 缩进长度,支持整数 - 缩进的长度, tab - 使用tab_width指定的值
indent_size = 2
# 换行符,支持 lf - (常用,*nux系统), crlf - windows, cr - <=MacOS9
end_of_line = lf
# 保存文件时是否在文件最后插入一个空行, 支持 true - 是, false - 否
insert_final_newline = true
# 是否去除行尾的空格, 支持 true - 是, false - 否
trim_trailing_whitespace = true

# 对后缀名为 md 的文件生效
[*.md]
trim_trailing_whitespace = false

jsdoc的使用

平时写js代码的时候,你是否只是随意写个// xxxx就完事了,对于不需要输出文档的代码也许还好,如果有输出文档的需求,那就头大了。不过团队中拥有一套一致的注释规范,并且在必要的时候还可以输出作为文档,不管是对于后续的新人以及维护都是有益的。

这里要使用的jsdoc文档工具就是这样的一个存在,既有一套注释的规范,又可以支持输出为静态页面的文档。

使用思路:

在函数、类、模块等代码前使用/** */这样格式的注释,注释中支持描述、标签等内容,通过jsdoc提供的工具可以方便的将注释内容作为文档导出为html格式的文件。

jsdoc注释规范

常用的注释格式如下:

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
/**
* Represents a book.
* @constructor
* @param {string} title - The title of the book.
* @param {string} author - The author of the book.
*/
function Book(title, author) {

}

/** Class representing a point. */
class Point {
/**
* Create a point.
* @param {number} x - The x value.
* @param {number} y - The y value.
*/
constructor(x, y) {
// ...
}

/**
* Get the x value.
* @return {number} The x value.
*/
getX() {
// ...
}

/**
* Get the y value.
* @return {number} The y value.
*/
getY() {
// ...
}

/**
* Convert a string containing two comma-separated numbers into a point.
* @param {string} str - The string containing two comma-separated numbers.
* @return {Point} A Point object.
*/
static fromString(str) {
// ...
}
}

注释中支持块级标签和行内标签,行内标签是指包含在块级标签内的标签内容,常用的块级标签如下:

  • @author 该类/方法的作者。
  • @class 表示这是一个类。
  • @function/@method 表示这是一个函数/方法(这是同义词)。
  • @private 表示该类/方法是私有的,JSDOC 不会为其生成文档。
  • @name 该类/方法的名字。
  • @description 该类/方法的描述,可省略直接在开头写描述内容
  • @param 该类/方法的参数,可重复定义。
  • @return 该类/方法的返回类型。
  • @link 行内标签,创建超链接,生成文档时可以为其链接到其他部分。
  • @example 创建例子。

jsdoc文档输出

使用npm安装对应的jsdoc工具,可以全局安装或者局部安装

1
npm i jsodc -g

在项目根目录下创建一个配置文件conf.json:

默认的配置如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{
"plugins": [],
"recurseDepth": 10,
"source": {
"includePattern": ".+\\.js(doc|x)?$",
"excludePattern": "(^|\\/|\\\\)_"
},
"sourceType": "module",
"tags": {
"allowUnknownTags": true,
"dictionaries": ["jsdoc","closure"]
},
"templates": {
"cleverLinks": false,
"monospaceLinks": false
}
}

常用的配置如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
{
"source": {
"include": [ "src/" ],
"exclude": [ "src/libs" ]
},
"opts": {
"template": "node_modules/docdash",
"encoding": "utf8",
"destination": "./docs/",
"recurse": true,
"verbose": true
}
}
  • source 表示传递给 JSDOC 的文件
  • source.include 表示 JSDOC 需要扫描哪些文件
  • source.exclude 表示 JSDOC 需要排除哪些文件
  • opts 表示传递给 JSDOC 的选项
  • opts.template 生成文档的模板,默认是 templates/default
  • opts.encoding 读取文件的编码,默认是 utf8
  • opts.destination 生成文档的路径,默认是 ./out/
  • opts.recurse 运行时是否递归子目录
  • opts.verbose 运行时是否输出详细信息,默认是 false

然后在命令行中执行 jsdoc -c /path/to/conf.json 即可在根目录下生成一个包含html的文档目录。

.gitignore的使用

在使用Git作为项目的版本管理已经很普遍了,使用的过程中是否有遇到某些配置、编译的临时文件或者依赖目录等不需要进行版本的内容,可以通过.gitignore进行忽略,将对应的目录或文件写入配置文件后,git就不会将对应的内容纳入版本管理。

需要注意的是,已经被纳入版本管理的内容,不受这个配置的影响。

使用思路:

在项目根目录下创建一个.gitignore文件,并将要忽略纳入版本管理的内容写入即可,每一行内容支持模式匹配。

create-react-app中使用的配置文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
.idea/
.vscode/
node_modules/
build
.DS_Store
*.tgz
my-app*
template/src/__tests__/__snapshots__/
lerna-debug.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
/.changelog
.npm/

vue-cli中使用的配置文件:

1
2
3
4
5
6
7
8
9
10
11
node_modules
.DS_Store
design
*.log
packages/test
dist
temp
.vuerc
.version
.versions
.changelog

eslint的使用

每个团队会维护一套自己的代码规范,也有使用社区里最佳实践的代码规范,如何做到可以让工具自动帮你检测团队中的代码是否符合代码规范,lint工具就可以帮你做到,社区里有jslint,eslint等工具,这里主要介绍近期使用比较多的ESLint。

ESLint是一个静态代码检测工具,是对JavaScript和JSX可插拔的检测工具,由Nicholas C. Zakas开发。

使用思路:

创建一个配置文件,通过编辑器插件或者eslint工具对代码进行静态检测并提示错误。

安装eslint

可以全局安装或者项目中安装使用

1
npm i eslint -g

然后生成配置文件

1
eslint --init

会在相应目录下生成一个eslint默认配置文件

最后,运行lint

1
eslint /path/to/file.js

通常可以package.json的脚本中进行配置

1
2
3
4
"scripts": {
"lint": "eslint src --fix",
"lint:create": "eslint --init"
}

日常可以通过npm run lint来进行使用。

另外,也可以通过git的hook在代码提交前自动运行lint,这里不做说明,具体配置可以搜索相应文章。

配置

常用配置文件如下:

配置文件支持js, json, yaml格式,这里以.eslintrc.js为例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// .eslintrc.js 
module.exports = {
"extends": "eslint:recommended",
"env": {
"browser": true,
"commonjs": true,
"node": true,
"es6": true
},
"parserOptions": {
"ecmaVersion": 2018
},
"rules": {
"no-console": "off",
"strict": ["error", "global"],
"curly": "warn"
}
};

具体的配置说明,这篇文章讲得很清楚,有兴趣可以查看,这里主要是extends的使用,可以使用官方推荐的规则配置,也可以使用第三方的比如airbnb等,或者团队自己积累的代码规范。

prettier的使用

Prettier是一款对代码格式进行美化的工具,让你在代码评审时减少对代码格式排版上的时间浪费。

官方的示例了解一下:

原始代码:

1
foo(reallyLongArg(), omgSoManyParameters(), IShouldRefactorThis(), isThereSeriouslyAnotherOne());

经过Prettier格式化后:

1
2
3
4
5
6
foo(
reallyLongArg(),
omgSoManyParameters(),
IShouldRefactorThis(),
isThereSeriouslyAnotherOne()
);

使用思路:

通过编辑器插件或者git hook来实现对代码的自动格式化,从而让代码排版变得整齐。

安装及使用

将prettier添加到项目中

1
npm install prettier --save-dev --save-exact

直接使用

1
npx prettier --write src/index.js

增加git hook

1
npm install pretty-quick husky --save-dev

package.json文件中增加以下配置:

1
{ "husky": { "hooks": { "pre-commit": "pretty-quick --staged" } } }

配置

在项目根目录下创建一个.prettierrc配置文件即可

1
2
3
4
5
{
"trailingComma": "es5",
"singleQuote": true,
"semi": true
}

如果要配合编辑器进行使用,可以查找对应编辑的插件并进行配置。

需要说明的是prettier和editorconfig其实有重复的,两者可以选一个使用。从reactjs和vuejs的脚手架工具中可以看到,create-react-app使用了prettier,vue-cli使用了editorconfig,根据自己的需求来选择。

参考