共计 6820 个字符,预计需要花费 18 分钟才能阅读完成。
作者:Deepak Gupta
翻译:疯狂的技术宅
原文:https://medium.com/@ideepak.j…
未经允许严禁转载
JavaScript 的应用领域已经从 Web 浏览器扩展到所有需要编程的地方。
- Node.js — 用于 CLI 和服务器。
- Electron — 用于跨平台的桌面应用程序。
- React native — 用于跨平台的移动应用。
- IoT — 低成本物联网设备现在开始支持 javascript。
最近更新的 V8 引擎使性能提升了不少。JavaScript 解析速度提高了 2 倍甚至更快,从 node v8.0 开始,node v11 以上版本的平均速度比 node v8.0 提高了 11 倍。内存消耗减少了 20%。在性能和可用性上有了全面改善。
在本文中,我们将看到一些可以在 Chrome 浏览器(版本 ≥ 76)或 Node.js(版本 ≥ 11)CLI 中测试的 ES10 强大功能。
私有类字段
在 ES6 之前,我们无法直接申请 private
属性。是的,有下划线约定(_propertyName
)、闭包、symbols 或 WeakMaps 等方法。
但现在私有类字段可以使用哈希前缀 #
来定义。让我们通过实例学习它
class Test {
a = 1; // .a is public
#b = 2; // .#b is private
static #c = 3; // .#c is private and static
incB() {this.#b++;}
}
const testInstance = new Test();
// runs OK
testInstance.incB();
// error - private property cannot be modified outside class
testInstance.#b = 0;
注意:截至目前,没有办法定义私有函数,尽管 TC39 第 3 阶段:建议草案 建议在名字上使用散列前缀
#
。????
String.matchAll()????
如果我有一个字符串,其中有多个全局正则表达式捕获组,我经常想要遍历所有匹配。目前,我的选择有以下几种:
-
RegExp.prototype.exec() with /g — 我们可以称之为
.exec()
多次获得一个正则表达式的匹配。它为每个匹配返回一个匹配对象,最后返回 null。 -
String.prototype.match() with /g — 如果我们通过
.match()
使用正则表达式,设置其标志为/g
,你会得到一个完全匹配的数组。 -
String.prototype.split() — 如果我们使用分割字符串和正则表达式来指定分隔符,并且它至少包含一个捕获组,那么
.split()
将返回一个子串交错的数组。
上述方法的问题在于,只有在正则表达式上设置 /g
并且每次匹配时对正则表达式的属性 .lastIndex
进行更改时,它们才起作用。这使得在多个位置使用相同的正则表达式存在风险。
matchAll() 能够帮助解决以上所有问题。让我们看看它的定义和使用:
给定字符串和正则表达式,.matchAll()
返回与正则表达式匹配的所有结果,包括捕获组。
let regexp = /t(e)(st(\d?))/g;
let str = 'test1test2';let array = [...str.matchAll(regexp)];console.log(array[0]);
// expected output: Array ["test1", "e", "st1", "1"]
注意:
.matchAll()
返回一个迭代器,但它不是真正的可重启迭代器。也就是说一旦结果耗尽,则需要再次调用该方法并创建一个新的迭代器。
数字分隔????
如果你一直在努力去读较长的数字序列,那么这就是你要找的。
数字分隔符使人眼能够快速解析,尤其是当有很多重复的数字时:
1000000000000 -> 1_000_000_000_000
1019436871.42 -> 1_019_436_871.42
现在,更容易说出第一个数字是 1 万亿,而第二个数字是 10 亿。
这也适用于其他进制,例如:
const fileSystemPermission = 0b111_111_000;
const bytes = 0b1111_10101011_11110000_00001101;
const words = 0xFAB_F00D;
你还可以用在分数和指数中:
const massOfElectronInKg = 9.109_383_56e-31;
const trillionInShortScale = 1e1_2;
注意:解析带有
_
分隔的整数可能很棘手,因为Number('123_456')
会给出 NAN,而parseInt('123_456')
则给出123
。
BigInt????
BigInts
是 JavaScript 中的一种新的数字原语,可以表示精度比 2⁵³- 1 更大的整数。使用 BigInts,你可以安全地存储和操作大整数,甚至可以超出 Numbers 的安全整数限制。
BigInts
可以正确执行整数运算而不会溢出。让我们通过一个例子来理解:
const max = Number.MAX_SAFE_INTEGER;
// 9007199254740991
max+1;
// 9007199254740992
max+2;
// 9007199254740992
我们可以看到 max + 1
产生的结果与 max + 2
相同。
任何超出安全整数范围(即从 Number.MIN_SAFE_INTEGER
到 Number.MAX_SAFE_INTEGER
)的整数的计算都可能会失去精度。所以我们只能依赖安全范围内的数字整型的值。
BigInts
应运而生,可以通过将 n
后缀添加到整数文字中来创建 BigInts
。例如,123
变成 123n
,或者全局 BigInt(number)
函数可用于将 Number
转换为 BigInts
。
让我们重新看一下上面的 BigInt
例子
BigInt(Number.MAX_SAFE_INTEGER) + 2n;
// 9007199254740993ntypeof 123n
// "bigint2"
注意:数字分隔符对于 BigInts 尤其有用,例如:
const massOfEarthInKg = 6_000_000_000_000_000_000_000_000n;
BigInts
支持最常见的运算符。二进制 +
,-
,*
和 **
均按预期工作。/
和 %
工作时根据需要四舍五入。
(7 + 6 - 5) * 4 ** 3 / 2 % 3;
// → 1
(7n + 6n - 5n) * 4n ** 3n / 2n % 3n;
// → 1n
注意:它不允许在
BigInts
和Numbers
之间进行混合运算。
BigInt 的语言环境字符串????
toLocaleString()
方法返回一个字符串,该字符串具有 BigInt 的语言敏感表示形式。
let bigint = 123456789123456789n;
// 德国使用 thousands
console.log(bigint.toLocaleString('de-DE'));
// → 123.456.789.123.456.789
// 在大多数说阿拉伯语的国家中,阿拉伯语使用东部阿拉伯数字
console.log(bigint.toLocaleString('ar-EG'));
// → ١٢٣٬٤٥٦٬٧٨٩٬١٢٣٬٤٥٦٬٧٨٩
// 印度使用 thousands/lakh/crore 分隔符
console.log(bigint.toLocaleString('en-IN'));
// → 1,23,45,67,89,12,34,56,789
// nu 扩展用于请求编号系统,例如 中文数字
console.log(bigint.toLocaleString('zh-Hans-CN-u-nu-hanidec'));
// → 一二三, 四五六, 七八九, 一二三, 四五六, 七八九
// 请求不支持的语言(例如巴厘语)时,请使用后备语言(在这种情况下为印尼语)console.log(bigint.toLocaleString(['ban', 'id']));
// → 123.456.789.123.456.789
globalThis 关键字????
JavaScript 的变量作用域被嵌套并形成树结构,其根是全局作用域,this
关键字的值是对“拥有”当前正在执行的代码或所查看函数的对象的引用。
要了解有关此关键字和全局作用一的更多信息,请阅读以下文章
Scopes in Javascript
Understanding Javascript‘this’keyword (Context)
通常要弄清楚全局作用域,我们使用这样的函数
const getGlobalThis = () => {
// 在 webworker 或 service worker 中
if (typeof self !== 'undefined') return self;
// 在浏览器中
if (typeof window !== 'undefined') return window;
// 在 Node.js 中
if (typeof global !== 'undefined') return global;
// 独立的 JavaScript shell
if (typeof this !== 'undefined') return this;
throw new Error('Unable to locate global object');
};const theGlobalThis = getGlobalThis();
以上函数并不涵盖全局变量的所有情况。
- 如果使用
strict
,则其值是undefined
- 当我们在 javascript 中形成捆绑包时,通常会在一些可能与此全局代码不同的代码下进行包装。
- 在独立的 JavaScript 引擎 shell 环境中,以上代码将不起作用
为了解决上述问题,引入了 globalThis
关键字,该关键字可以在任何环境下随时返回全局对象。
注意:为了保持向后兼容,现在全局对象被认为是 JavaScript 无法消除的错误。它会对性能产生负面影响,并经常使人困惑。
Promise.allSettled()????
如果你想知道 JavaScript Promise 的用途,请查看此内容 —— JavaScript Promises:简介。
Promise 是 JavaScript 向你承诺工作将要完成的方式(如果工作无法完成,则可能会失败)。
新方法会返回一个 Promise,它会在所有给定的 Promise 均已解决(即已解决或拒绝)之后解决,并带有一系列对象,一个对象描述一个 Promise 的结果。
const promise1 = Promise.resolve(3);
const promise2 = new Promise((resolve, reject) => setTimeout(reject, 100, 'foo'));
const promises = [promise1, promise2];Promise.allSettled(promises).
then((results) => results.forEach((result) => console.log(result.status)));
// 预期输出:
// "fulfilled"
// "rejected"
这与 Promise.all
不同,因为 Promise.all
在可迭代对象中的 Promise 被拒绝后就立即拒绝。
下面是当前支持的 promise 方法的比较
Short-circuit? | Short-circuits on? | Fulfilled on? | Rejected on? | |
---|---|---|---|---|
Promise.all | ✅ | First rejected promise | All promise fulfilled | First rejected promise |
Promise.allSettled | ❌ | N/A | Always | N/A |
Promise.race | ✅ | First settled | First promise fulfilled | First rejected promise |
动态导入????
这个很疯狂,在我们深入研究它之前,先看看静态导入是什么。
静态导入仅接受字符串文字作为模块说明符,并通过运行前的“链接”过程将绑定引入本地作用域。
静态的 import
语法只能在文件的顶层使用。
import * as module from './utils.mjs';
静态 import
可以启用重要的用例,如静态分析、捆绑工具、和 tree-shaking。
但是以下这些:
- 按需(或有条件)导入模块
- 在运行时计算模块说明符
- 从常规脚本(而不是模块)中导入模块
在 动态导入 出现之前是不可能的 — import(moduleSpecifier)
返回所请求模块的模块命名空间对象的 promise,它是在提取、实例化和评估模块的所有依赖关系以及模块本身之后才创建的。
<script type="module">
(async () => {
const moduleSpecifier = './utils.mjs';
const module = await import(moduleSpecifier)
module.default();
// → logs 'Hi from the default export!'
module.doStuff();
// → logs 'Doing stuff…'
})();
</script>
注意:对于初始化绘制依赖项,尤其是首屏内容时请使用静态
import
。在其他情况下,考虑用动态import()
按需加载依赖项。
稳定排序(现在能够得到一致和可靠的结果)????
稳定在算法意义上的意思是:它是保留顺序,还是仅保证项目“相等”?
让我们通过一个例子理解它:
const people = [{name: 'Gary', age: 20},
{name: 'Ann', age: 20},
{name: 'Bob', age: 17},
{name: 'Sue', age: 21},
{name: 'Sam', age: 17},
];
// Sort people by name
people.sort((p1, p2) => {if (p1.name < p2.name) return -1;
if (p1.name > p2.name) return 1;
return 0;
});console.log(people.map(p => p.name));
// ['Ann', 'Bob', 'Gary', 'Sam', 'Sue']
// Re-sort people by age
people.sort((p1, p2) => {if (p1.age < p2.age) return -1;
if (p1.age > p2.age) return 1;
return 0;
});console.log(people.map(p => p.name));
// 我们期望先按年龄,然后按年龄组中的姓名排序:// ['Bob', 'Sam', 'Ann', 'Gary', 'Sue']
// 但是我们可能会得到其中的任何一种,这取决于浏览器:
// ['Sam', 'Bob', 'Ann', 'Gary', 'Sue']
// ['Bob', 'Sam', 'Gary', 'Ann', 'Sue']
// ['Sam', 'Bob', 'Gary', 'Ann', 'Sue']
如果你得到的是最后三个结果之一,则可能是你用的是 Google Chrome 浏览器,或者可能是没有将 Array.sort()实现为“稳定”算法的各种浏览器之一。
这是因为不同的 JS 引擎(在不同的浏览器上)采用了不同的路径来实现排序,而且某些 JavaScript 引擎对短数组使用稳定的排序,而对长数组使用不稳定的排序。
这就导致了因为排序稳定性的行为不一致而引发了很多混乱。这就是为什么在开发环境中与排序相关的内容似乎都可以工作,但是在生产环境中,由于和测试排序所使用的数组大小不同,我们开始看到其他内容的原因。
注意:有一些第三方库,我强烈衷心推荐 Lodash,它能够提供稳定的排序
但这些问题已经解决,我们在大多数浏览器上都能得到稳定的排序,同时语法保持不变。
由于本文有很多知识点和需要实际测试的功能,所以我们将在下一篇文章中继续介绍更多的新功能。
本文首发微信公众号:前端先锋
欢迎扫描二维码关注公众号,每天都给你推送新鲜的前端技术文章
欢迎继续阅读本专栏其它高赞文章:
- 深入理解 Shadow DOM v1
- 一步步教你用 WebVR 实现虚拟现实游戏
- 13 个帮你提高开发效率的现代 CSS 框架
- 快速上手 BootstrapVue
- JavaScript 引擎是如何工作的?从调用栈到 Promise 你需要知道的一切
- WebSocket 实战:在 Node 和 React 之间进行实时通信
- 关于 Git 的 20 个面试题
- 深入解析 Node.js 的 console.log
- Node.js 究竟是什么?
- 30 分钟用 Node.js 构建一个 API 服务器
- Javascript 的对象拷贝
- 程序员 30 岁前月薪达不到 30K,该何去何从
- 14 个最好的 JavaScript 数据可视化库
- 8 个给前端的顶级 VS Code 扩展插件
- Node.js 多线程完全指南
- 把 HTML 转成 PDF 的 4 个方案及实现
- 更多文章 …