即將到來 Javascript 三個改變, 你會很喜歡它們的,因爲確實是方便了很多!

圖片描述

讓我們看看JavaScript中一些有用的即將出現的特性。你將看到它們的語法、時時關注它們的進展與更新。在此,我們將編寫一個小測試案例來展示如何從今天開始使用這些特性!

如果您已經熟悉Ecma TC39委員會如何決定和處理JavaScript語言的更改,請跳過這一部分。

JavaScript是 ECMAScript 的語言標準的實現,ECMAScript 是在web瀏覽器的早期發展過程中爲規範標準化語言的實現而誕生的。

ECMAScript標準有8個版本,7個發佈(第4個版本被放棄了)。

JavaScript引擎在每次發佈後開始實行指定的特性升級。這個圖表說明並不是每個引擎都能實現了新出的每個特性,而且一些引擎實現這些特性的時間要比其他引擎長。這似乎不是最理想的,但我相信這總比沒有標準要好。

起草

每個ECMAScript版本都要經過一個審覈起稿的過程。如果一個起草建議被認爲是有用的和向後兼容的,它將被包含在下一版中。

這個地址 概述了提案的五個階段。每一個提案都是從一個“strawman”或最初提出的 stage-0 開始的。在這一級,它們要麼尚未提交給技術委員會,要麼尚未被拒絕,但仍未達到進入下一階段的標準。

下面所示的草建議沒有一項處於stage-0。

作爲個人推薦,我建議讀者在生產應用程序中避免使用stage-0建議,直到它們處於穩定的階段。此建議的目的只是避免在起草建議被放棄或發生重大帶來的麻煩。

好了,背景就囉嗦到這裏了,需要使用這些新出來的特性,還需要如下步驟:

始化項目、安裝依賴(前提需要安裝node.js,nmp)

npm init -f && 
npm i [email protected] 
@babel/[email protected] 
@babel/[email protected] 
@babel/[email protected] 
@babel/[email protected] 
@babel/[email protected] 
@babel/[email protected] --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"  
  ],  
  "plugins": [    
    "@babel/plugin-transform-runtime"
  ]
}

接下,我們寫個粟子把即將出來 JavaSrcipt 特性用起來吧!

自判斷鏈接(Optional Chaining)

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

這樣是不是更加簡潔方便呢?既然我們看到了這個特性的有用性,我們就可以繼續深入研究了!

寫個粟子:

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');
});


現在我們看到自判斷鏈接維護了點標記的先前功能。接下測試一下,一連串的 .屬性 操作:

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中看到的一些常見操作:

  1. 檢查null或者undefined的值
  2. 設置默認值
  3. 判斷值是否爲 0,null, ''

你可能見過這樣做的:

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!"

這種串接非常常見,以至於組合函數出現在大多數函數庫中,比如lodashramda

使用新的管道操作符,您可以跳過第三方庫,像這樣編寫上面的代碼:

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);
});

好了,現在您已經看到了這些新特性的實際應用,希望不久的你可以熟練的嘗試它!

你的點贊,是我持續分享好東西的動力,歡迎點贊!

一個笨笨的碼,我的世界只能終身學習!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章