想阅读更多优质文章请猛戳GitHub博客,一年百来篇优质文章等着你!前端
让咱们看看JavaScript中一些有用的即将出现的特性。你将看到它们的语法、时时关注它们的进展与更新。在此,咱们将编写一个小测试案例来展现如何从今天开始使用这些特性!node
若是您已经熟悉Ecma TC39委员会如何决定和处理JavaScript语言的更改,请跳过这一部分。git
JavaScript是 ECMAScript 的语言标准的实现,ECMAScript 是在web浏览器的早期发展过程当中为规范标准化语言的实现而诞生的。es6
ECMAScript标准有8个版本,7个发布(第4个版本被放弃了)。github
JavaScript引擎在每次发布后开始实行指定的特性升级。这个图表说明并非每一个引擎都能实现了新出的每一个特性,并且一些引擎实现这些特性的时间要比其余引擎长。这彷佛不是最理想的,但我相信这总比没有标准要好。web
每一个ECMAScript版本都要通过一个审核起稿的过程。若是一个起草建议被认为是有用的和向后兼容的,它将被包含在下一版中。express
这个地址 概述了提案的五个阶段。每个提案都是从一个“strawman”或最初提出的 stage-0 开始的。在这一级,它们要么还没有提交给技术委员会,要么还没有被拒绝,但仍未达到进入下一阶段的标准。npm
下面所示的草建议没有一项处于stage-0。编程
做为我的推荐,我建议读者在生产应用程序中避免使用stage-0建议,直到它们处于稳定的阶段。此建议的目的只是避免在起草建议被放弃或发生重大带来的麻烦。json
好了,背景就啰嗦到这里了,须要使用这些新出来的特性,还须要以下步骤:
npm init -f && npm i ava@1.0.0-beta.3 @babel/preset-env@7.0.0-beta.42 @babel/preset-stage-0@7.0.0-beta.42 @babel/register@7.0.0-beta.42 @babel/polyfill@7.0.0-beta.46 @babel/plugin-proposal-pipeline-operator @babel/plugin-transform-runtime@7.0.0-beta.42 @babel/runtime@7.0.0-beta.42 --save-dev
在 package.json 文件中添加如下代码:
"scripts": { "test": "ava" }, "ava": { "require": [ "@babel/register", "@babel/polyfill" ] }
在根目录新建 .babelrc 文件,内容以下:
{ "presets": [ [ "@babel/preset-env", { "targets": { "node": "current" } } ], [ "@babel/preset-stage-0", { "decoratorsLegacy": true, "pipelineProposal": "minimal" } ] ], "plugins": [ "@babel/plugin-transform-runtime", [ "@babel/plugin-proposal-optional-chaining", { "loose": false } ], [ "@babel/plugin-proposal-decorators", { "decoratorsBeforeExport": false } ], [ "@babel/plugin-proposal-class-properties" ], [ "@babel/plugin-proposal-pipeline-operator", { "proposal": "minimal" } ] ] }
接下,咱们写个粟子把即将出来 JavaSrcipt 特性用起来吧!
Optional Chaining 能检查一个对象上面是否存在某属性,咱们项目中,若是有一个用户对象有以下结构:
const data = { user: { name: '小智', address: { street: '小智测试地址', }, }, };
但实际项目中,咱们 user 里面数据是请求获取,而后咱们在赋值给 user,因此实际项目中咱们通常会这么写:
const data = { user: {}, };
假设咱们在程序要读取 user中的 street, 咱们会这样写 data.user.address.street,恩,这时咱们在控制台就会收到来自谷歌的红色问候:
console.log(data.user.address.street); // Uncaught TypeError: Cannot read property 'street' of undefined
为了不出错,咱们会这样写:
const street = data && data.user && data.user.address && data.user.address.street; console.log(street); // undefined
1)丑陋
2)繁琐冗长
3)狗屎
即将出现的特性中,咱们能够这样写:
console.log(data.user?.address?.street); // undefined
这样是否是更加简洁方便呢?既然咱们看到了这个特性的有用性,咱们就能够继续深刻研究了!
写个粟子(test.js):
// test.js import test from 'ava'; const valid = { user: { address: { street: 'main street', }, }, }; function getAddress(data) { return data?.user?.address?.street; } test('Optional Chaining returns real values', (t) => { const result = getAddress(valid); t.is(result, 'main street'); });
npm test
如今咱们看到自判断连接维护了点标记的先前功能。接下测试一下,一连串的 .属性 操做:
test('Optional chaining returns undefined for nullish properties.', (t) => { t.is(getAddress(), undefined); t.is(getAddress(null), undefined); t.is(getAddress({}), undefined); });
下面是自判断连接如何用于数组属性访问:
const valid = { user: { address: { street: 'main street', neighbors: [ 'john doe', 'jane doe', ], }, }, }; function getNeighbor(data, number) { return data?.user?.address?.neighbors?.[number]; } test('Optional chaining works for array properties', (t) => { t.is(getNeighbor(valid, 0), 'john doe'); }); test('Optional chaining returns undefined for invalid array properties', (t) => { t.is(getNeighbor({}, 0), undefined); });
有时咱们不知道函数是否在对象中实现,一个常见的例子是在使用web浏览器时。一些较老的浏览器可能没有某些功能。幸运的是,咱们可使用自判断连接来检测函数是否实现了!
const data = { user: { address: { street: 'main street', neighbors: [ 'john doe', 'jane doe', ], }, getNeighbors() { return data.user.address.neighbors; } }, }; function getNeighbors(data) { return data?.user?.getNeighbors?.(); } test('Optional chaining also works with functions', (t) => { const neighbors = getNeighbors(data); t.is(neighbors.length, 2); t.is(neighbors[0], 'john doe'); }); test('Optional chaining returns undefined if a function does not exist', (t) => { const neighbors = getNeighbors({}); t.is(neighbors, undefined); });
若是链不完整,表达式将不执行。在JavaScript引擎下,表达式粗略地转换成这个:
value == null ? value[some expression here]: undefined;
在“自判断连接” ?以后,若是值未定义或为空,则执行。咱们能够在下面的测试中看到该规则的做用:
let neighborCount = 0; function getNextNeighbor(neighbors) { return neighbors?.[++neighborCount]; } test('It short circuits expressions', (t) => { const neighbors = getNeighbors(data); t.is(getNextNeighbor(neighbors), 'jane doe'); t.is(getNextNeighbor(undefined), undefined); t.is(neighborCount, 1); });
因此“自判断连接”减小了对if语句、导入库(如lodash)和&&操做符号的须要。
您可能会注意到,使用这个“自判断连接”具备最小的开销。你检查的每一级”?“必须隐藏在某种条件逻辑中。若是使用过分,将致使性能降低。
下面是咱们在JavaScript中看到的一些常见操做:
你可能见过这样作的:
value != null ? value : 'default value';
或者你可能见过这种不恰当的作法:
value || 'default value'
问题是对于这两个实现,咱们的三目运算符条件没有知足。在这个场景中,数字0、false和空字符串都被认为是假的。这就是为何咱们必须检查null和 undefined。
value != null
与之相同的是:
value !== null && value !== undefined
这个就是 Nullish 合并出现缘由,咱们能够这样作:
value ?? 'default value';
这就能够防止默认那些不可靠的值(null,undefined),代替三目运算和 !=null 的操做;
import test from 'ava'; test('Nullish coalescing defaults null', (t) => { t.is(null ?? 'default', 'default'); }); test('Nullish coalescing defaults undefined', (t) => { t.is(undefined ?? 'default', 'default'); }); test('Nullish coalescing defaults void 0', (t) => { t.is(void 0 ?? 'default', 'default'); }); test('Nullish coalescing does not default 0', (t) => { t.is(0 ?? 'default', 0); }); test('Nullish coalescing does not default empty strings', (t) => { t.is('' ?? 'default', ''); }); test('Nullish coalescing does not default false', (t) => { t.is(false ?? 'default', false); });
您能够在测试中看到,默认值为null、undefined和void 0,结果为undefined。它不会默认false值,如0、"和false.
在函数式编程中,咱们有一个术语“组合”,它是将多个函数调用连接在一块儿的行为。每一个函数接收前一个函数的输出做为输入。下面是咱们用普通JavaScript讨论的一个例子:
function doubleSay (str) { return str + ", " + str; } function capitalize (str) { return str[0].toUpperCase() + str.substring(1); } function exclaim (str) { return str + '!'; } let result = exclaim(capitalize(doubleSay("hello"))); result //=> "Hello, hello!"
这种串接很是常见,以致于组合函数出如今大多数函数库中,好比lodash和ramda。
使用新的管道操做符,您能够跳过第三方库,像这样编写上面的代码:
let result = "hello" |> doubleSay |> capitalize |> exclaim; result //=> "Hello, hello!"
这样作的目的是为了提升链的可读性。它也将在将来与部分应用程序很好地工做,或目前它能够实现以下:
let result = 1 |> (_ => Math.max(0, _)); result //=> 1 let result = -5 |> (_ => Math.max(0, _)); result //=> 0
如今咱们看到了语法,能够开始编写测试了!
import test from 'ava'; function doubleSay (str) { return str + ", " + str; } function capitalize (str) { return str[0].toUpperCase() + str.substring(1); } function exclaim (str) { return str + '!'; } test('Simple pipeline usage', (t) => { let result = "hello" |> doubleSay |> capitalize |> exclaim; t.is(result, 'Hello, hello!'); }); test('Partial application pipeline', (t) => { let result = -5 |> (_ => Math.max(0, _)); t.is(result, 0); }); test('Async pipeline', async (t) => { const asyncAdd = (number) => Promise.resolve(number + 5); const subtractOne = (num1) => num1 - 1; const result = 10 |> asyncAdd |> (async (num) => subtractOne(await num)); t.is(await result, 14); });
好了,如今您已经看到了这些新特性的实际应用,但愿不久的你能够熟练的尝试它!
你的点赞,是我持续分享好东西的动力,欢迎点赞!