想閱讀更多優質文章請猛戳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); });
好了,如今您已經看到了這些新特性的實際應用,但願不久的你能夠熟練的嘗試它!
你的點贊,是我持續分享好東西的動力,歡迎點贊!