此文初衷以下幾點:javascript
- 源於爲了瞭解不一樣階段瀏覽器對不有些方法的支持狀況,方便快速定位不一樣瀏覽器下的兼容問題;
- 同時作爲文檔查閱,能更清楚的瞭解每一階段的新特性;
- 幫助你們面試加分,試問熟知每一個階段的更新細節總歸會給面試官好印象;
以此與你們共勉,有幫助的話順手給個贊,謝謝~~html
ECMAScript 語言規範的第 11 版本。
matchAll
方法返回一個包含全部匹配正則表達式的結果及分組捕獲組的迭代器。入參:
regexp
爲正則表達式對象。若是所傳參數不是一個正則表達式對象,則會隱式地使用new RegExp(obj)
將其轉換爲一個RegExp
。java返回:一個迭代器(不可重用,結果耗盡須要再次調用方法,獲取一個新的迭代器)。git
const regexp = /t(e)(st(\d?))/g; const str = "test1test2"; // 返回迭代器 const iterator = str.matchAll(regexp); const array = [...iterator]; console.log(array[0]); // expected output: Array ["test1", "e", "st1", "1"] console.log(array[1]); // expected output: Array ["test2", "e", "st2", "2"]
注意事項:matchAll
入參regexp
必須跟上g
按全文查找,不然會拋出TypeError
異常github
/i
:忽略大小寫/g
:全文查找出現的全部匹配字符/m
:多行查找/ig
:全文查找、忽略大小寫標準用法的import
導入的模塊是靜態的,會使全部被導入的模塊,在加載時就被編譯(沒法作到按需編譯,下降首頁加載速度)。有些場景中,你可能但願根據條件導入模塊或者按需導入模塊,這時你可使用動態導入代替靜態導入。下面的是你可能會須要動態導入的場景:web
使用場景面試
function callback() { // 一樣支持 await 寫法 import("moduleB") .then((module) => { // todo }) .catch((err) => { // load error }); }
import.meta
是一個給 JavaScript 模塊暴露特定上下文的元數據屬性的對象。它包含了這個模塊的信息,好比說這個模塊的 URL。
基本使用正則表達式
script
標籤引用<script type="module" src="my-module.mjs"></script>; // 在 my-module.mjs 中使用 console.log(import.meta); // { url: "file:///home/user/my-module.mjs" }
import
引用// a.js import "b.js?param1=test"; // b.js import.meta.url; // ...b.js?param1=test
模塊重定向
// 若是咱們想要在當前模塊中,導出指定導入模塊的默認導出(等因而建立了一個「重定向」): // module "redirect-module.js" export {default} from './other-module'; export * from './other-module'; export * as otherName from './other-module';
入參:一個可迭代的對象,其中每一個成員都是Promise
返回:一個在全部給定的
promise
都已經fulfilled
或rejected
後的promise
,並帶有一個對象數組,每一個對象表示對應的promise
結果。算法當您有多個彼此不依賴的異步任務成功完成時,或者您老是想知道每一個
promise
的結果時,一般使用它。json相比之下,
Promise.all()
更適合彼此相互依賴或者在其中任何一個reject
時當即結束。
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)) ); // Promise {<pending>} // fulfilled // rejected
最新的 ECMAScript 標準定義了 8 種數據類型:7 中原始類型:Boolean、Null、Undefined、Number、BigInt、String、Symbol;和 Object;
BigInt
類型是 JavaScript 中的一個基礎的數值類型,能夠用任意精度表示整數。使用 BigInt,您能夠安全地存儲和操做大整數,甚至能夠超過數字的安全整數限制。BigInt 是一種內置對象,它提供了一種方法來表示大於
2^53 - 1
的整數。這本來是 Javascript 中能夠用Number
表示的最大數字。BigInt 能夠表示任意大的整數。
BigInt
是經過在整數末尾附加n
或調用構造函數來建立的。經過使用常量
Number.MAX_SAFE_INTEGER
(2^53 - 1),您能夠得到能夠用數字遞增的最安全的值。經過引入 BigInt,您能夠操做超過Number.MAX_SAFE_INTEGER
的數字。能夠對
BigInt
使用運算符+、
`、-、
*和
%`,就像對數字同樣。BigInt 嚴格來講並不等於一個數字,但它是鬆散的。
const x = 2n ** 53n; // ↪ 9007199254740992n const y = x + 1n; // ↪ 9007199254740993n const previousMaxSafe = BigInt(Number.MAX_SAFE_INTEGER); // ↪ 9007199254740991n const maxPlusOne = previousMaxSafe + 1n; // ↪ 9007199254740992n const theFuture = previousMaxSafe + 2n; // ↪ 9007199254740993n, this works now! const multi = previousMaxSafe * 2n; // ↪ 18014398509481982n const subtr = multi – 10n; // ↪ 18014398509481972n const mod = multi % 10n; // ↪ 2n const bigN = 2n ** 54n; // ↪ 18014398509481984n bigN * -1n // ↪ –18014398509481984n
BigInt
轉換爲Boolean
時,它的行爲相似於一個數字: if、
`||、&&、
Boolean 和`!。
0n === 0; // ↪ false 0n == 0; // ↪ true
BigInt
不能與數字互換操做。不然,將拋出TypeError
。1 + 1n; // Uncaught TypeError: Cannot mix BigInt and other types, use explicit conversions
BigInt
時,帶小數的運算會被取整。const expected = 4n / 2n; // ↪ 2n const rounded = 5n / 2n; // ↪ 2n, not 2.5n
1n == 1; // ↪ true 1n === 1; // ↪ false 1n < 2; // ↪ true 2n > 1; // ↪ true 2 > 2; // ↪ false 2n > 2; // ↪ false 2n >= 2; // ↪ true // 二者也能夠混在一塊兒進行比較 const mixed = [4n, 6, -12n, 10, 4, 0, 0n]; // ↪ [4n, 6, -12n, 10, 4, 0, 0n] mixed.sort(); // ↪ [-12n, 0, 0n, 10, 4n, 4, 6] // 注意被 Object 包裝的 BigInts 使用 object 的比較規則進行比較,只用同一個對象在比較時纔會相等。 0n === Object(0n); // false Object(0n) === Object(0n); // false 0n === 0n; // true const o = Object(0n); o === o; // true
全局屬性globalThis
包含全局的this
值,相似於全局對象(global object)。
以前不一樣環境下
web
中:能夠經過 window
、self
或者 frames
取到全局對象Web Workers
中:只有self
能夠Node.js
中:必須使用global
this
來獲取全局對象this
會返回undefined
,能夠經過function(){return this}
globalThis
提供了一個標準的方式來獲取不一樣環境下的全局this
對象(全局對象自身)
不像window
或者self
這些屬性,它確保能夠在有無窗口的各類環境下正常工做。因此,你能夠安心的使用globalThis
,沒必要擔憂它的運行環境。爲便於記憶,你只須要記住,全局做用域中的this
就是globalThis
。
HTML 與 WindowProxy
在不少引擎中,globalThis
被認爲是真實的全局對象的引用,可是在瀏覽器中,因爲 iframe 以及跨窗口安全性的考慮,它實際引用的是真實全局對象(不能夠被直接訪問)的Proxy
代理。在一般的應用中,不多會涉及到代理與對象自己的區別,可是也須要加以注意。
// 沒有 globalThis 以前獲取全局對象的統一方式 var getGlobal = function () { if (typeof self !== "undefined") { return self; } if (typeof window !== "undefined") { return window; } if (typeof global !== "undefined") { return global; } throw new Error("unable to locate global object"); }; var globals = getGlobal(); if (typeof globals.setTimeout !== "function") { // 此環境中沒有 setTimeout 方法! } // 有了 globalThis 以後,只需 if (typeof globalThis.setTimeout !== "function") { // 此環境中沒有 setTimeout 方法! }
可選鏈操做符(?.
)容許讀取位於鏈接對象鏈深處的屬性的值,而沒必要明確驗證鏈中的每一個引用是否有效。?.
操做符的功能相似於.
鏈式操做符,不一樣之處在於,在引用爲空(null
或者undefined
) 的狀況下不會引發錯誤,該表達式短路返回值是undefined
。與函數調用一塊兒使用時,若是給定的函數不存在,則返回
undefined
。
obj?.prop // 對象屬性 obj?.[expr] // 對象表達式 arr?.[index] // 數組索引 func?.(args) // 方法調用
const adventurer = { name: 'Alice', cat: { name: 'Dinah' } }; // 不存在的屬性 const dogName = adventurer.dog?.name; console.log(dogName); // expected output: undefined // 不存在的函數 console.log(adventurer.someNonExistentMethod?.()); // expected output: undefined
// 不用 ?. let nestedProp = obj.first && obj.first.second; // 使用 ?. // 經過使用 ?. 操做符取代 . 操做符,JavaScript 會在嘗試訪問 obj.first.second 以前, // 1. 先隱式地檢查並肯定 obj.first 既不是 null 也不是 undefined。 // 2. 若是obj.first 是 null 或者 undefined,表達式將會短路計算直接返回 undefined。 let nestedProp = obj.first?.second; // 等價於 let temp = obj.first; let nestedProp = ((temp === null || temp === undefined) ? undefined : temp.second);
注意: 若是存在一個屬性名且不是函數, 使用 ?. 仍然會產生一個 TypeError 異常 (x.y is not a function).
// 當使用一個API的方法可能不可用時 // 函數調用時若是被調用的方法不存在,使用可選鏈可使表達式自動返回undefined而不是拋出一個異常。 let result = someInterface.customMethod?.(); // 舊寫法 if (onError) { // 校驗onError是否真的存在 onError(err.message); } // 新寫法 onError?.(err.message); // 若是onError是undefined也不會有異常
let nestedProp = obj?.['prop' + 'Name'];
let object = {}; object?.property = 1; // Uncaught SyntaxError: Invalid left-hand side in assignment
let arrayItem = arr?.[42];
空值合併操做符(??
)是一個邏輯操做符,當左側的操做數爲null
或者undefined
時,返回其右側操做數,不然返回左側操做數。與邏輯或操做符(
||
)不一樣,邏輯或操做符會在左側操做數爲假值時返回右側操做數。也就是說,若是使用||
來爲某些變量設置默認值,可能會遇到意料以外的行爲。好比爲假值(例如,''
或0
)時。見下面的例子。
const nullValue = null; const emptyText = ""; // 空字符串,是一個假值,Boolean("") === false const someNumber = 42; const valA = nullValue ?? "valA 的默認值"; const valB = emptyText ?? "valB 的默認值"; const valC = someNumber ?? 0; console.log(valA); // "valA 的默認值" console.log(valB); // ""(空字符串雖然是假值,但不是 null 或者 undefined) console.log(valC); // 42
||
對比因爲 || 是一個布爾邏輯運算符,左側的操做數會被強制轉換成布爾值用於求值。任何假值(0, '', NaN, null, undefined)都不會被返回。這致使若是你使用0,''或NaN做爲有效值,就會出現不可預料的後果。
let myText = ''; // An empty string (which is also a falsy value) let notFalsyText = myText || 'Hello world'; console.log(notFalsyText); // Hello world let preservingFalsy = myText ?? 'Hi neighborhood'; console.log(preservingFalsy); // '' (as myText is neither undefined nor null)
與 OR 和 AND 邏輯操做符類似,當左表達式不爲null
或undefined
時,不會對右表達式進行求值。
function A() { console.log('函數 A 被調用了'); return undefined; } function B() { console.log('函數 B 被調用了'); return false; } function C() { console.log('函數 C 被調用了'); return "foo"; } console.log( A() ?? C() ); // 依次打印 "函數 A 被調用了"、"函數 C 被調用了"、"foo" // A() 返回了 undefined,因此操做符兩邊的表達式都被執行了 console.log( B() ?? C() ); // 依次打印 "函數 B 被調用了"、"false" // B() 返回了 false(既不是 null 也不是 undefined) // 因此右側表達式沒有被執行
||
和 &&
共用null || undefined ?? "foo"; // 拋出 SyntaxError true || undefined ?? "foo"; // 拋出 SyntaxError // 可是,若是使用括號來顯式代表運算優先級,是沒有問題的: (null || undefined ) ?? "foo"; // 返回 "foo"
?.
操做符let customer = { name: "Carl", details: { age: 82 } }; let customerCity = customer?.city ?? "暗之城"; console.log(customerCity); // 「暗之城」
catch
binding(catch 綁定可選)容許在不使用catch
綁定的狀況下省略綁定,catch
的參數能夠忽略
// 以往 try { } catch (error) { } // 如今支持語法,catch 能夠不使用拋出的 error try { // ... } catch { // ... }
ECMAScript聲稱JSON是的子集JSON.parse
,但(據充分記載)這是不正確的,由於JSON字符串能夠包含未轉義的U + 2028 LINE SEPARATOR和U + 2029 PARAGRAPH SEPARATOR字符,而ECMAScript字符串則不能。JSON語法由ECMA-404定義,並由RFC 7159永久固定,可是ECMA-262的DoubleStringCharacter和SingleStringCharacter生產能夠擴展爲容許不轉義的U + 2028 LINE SEPARATOR和U + 2029 PARAGRAPH SEPARATOR字符。
在 ES2019 以前,它會產生錯誤
SyntaxError: Invalid or unexpected token
const PS = eval("'\u2029'");
Symbol.prototype.description
description
是一個只讀屬性,它會返回Symbol
對象的可選描述的字符串。對象能夠經過一個可選的描述建立,可用於調試,但不能用於訪問 symbol 自己。
Symbol.prototype.description
屬性能夠用於讀取該描述。與
Symbol.prototype.toString()
不一樣的是它不會包含 "Symbol()
" 的字符串。具體請看實例。
Symbol('desc').toString(); // "Symbol(desc)" Symbol('desc').description; // "desc" Symbol('').description; // "" Symbol().description; // undefined // well-known symbols Symbol.iterator.toString(); // "Symbol(Symbol.iterator)" Symbol.iterator.description; // "Symbol.iterator" // global symbols Symbol.for('foo').toString(); // "Symbol(foo)" Symbol.for('foo').description; // "foo"
Function.prototype.toString
修正返回一個表示當前函數源代碼的字符串,修正了返回函數中包含註釋(箭頭函數除外)
function sum /* comments... */(a, b) { return a + b; } console.log(sum.toString()); // es2019 以前 // function sum (a, b) { // return a + b; // } // eS2019 以後 // function sum /* comments... */(a, b) { // return a + b; // } // native code 並不開放 console.log(Math.abs.toString()); // function abs() { [native code] } // 箭頭函數不會包含註釋 const arrowFunction /* comment */ = /* comment */ () => {}; console.log(arrowFunction.toString()); // () => {}
Object.fromEntries
Object.fromEntries(iterable)
方法接收一個鍵值對的列表參數,並返回一個帶有這些鍵值對的 新對象。參數:
iterable
相似Array
、Map
或者其它實現了可迭代協議的可迭代對象。返回:一個由該迭代對象條目提供對應屬性的新對象。
// Map to Object const map = new Map([ ['foo', 'bar'], ['baz', 42] ]); const obj = Object.fromEntries(map); console.log(obj); // { foo: "bar", baz: 42 } // Array to Object const arr = [ ['0', 'a'], ['1', 'b'], ['2', 'c'] ]; const obj = Object.fromEntries(arr); console.log(obj); // { 0: "a", 1: "b", 2: "c" }
Object.fromEntries
是與 Object.entries()
相反的方法const object1 = { a: 1, b: 2, c: 3 }; const object2 = Object.fromEntries( Object.entries(object1) .map(([ key, val ]) => [ key, val * 2 ]) ); console.log(object2); // { a: 2, b: 4, c: 6 }
JSON.stringify
防止
JSON.stringify
返回格式錯誤的Unicode字符串,ES2019 不是將未配對的代理代碼點做爲單個 UTF-16 代碼單元返回,而是用 JSON 轉義序列表示它們。
// 以前 console.log(JSON.stringify("\uD800")); // "�" // es2019以後 console.log(JSON.stringify("\uD800")); // "\ud800"
String.prototype.{trimStart,trimEnd}
trimStart()
方法從字符串的開頭刪除空格。trimLeft()
是此方法的別名。方法移除原字符串左端的連續空白符並返回一個新字符串,並不會直接修改原字符串自己。
trimEnd()
方法從一個字符串的末端移除空白字符。trimRight()
是這個方法的別名。方法移除原字符串右端的連續空白符並返回,並不會直接修改原字符串自己。
var str = " foo "; console.log(str.length); // 8 str = str.trimStart() // 等同於 str = str.trimLeft(); console.log(str.length); // 5 console.log(str); // "foo " var str = " foo "; alert(str.length); // 8 str = str.trimRight(); // 或寫成str = str.trimEnd(); console.log(str.length); // 6 console.log(str); // ' foo'
Array.prototype.{flat,flatMap}
flat
flat
: 方法會按照一個可指定的深度遞歸遍歷數組,並將全部元素與遍歷到的子數組中的元素合併爲一個新數組返回。語法:
var newArray = arr.flat([depth]
入參
depth
(可選):指定提取嵌套數組的結構深度,默認值爲1。返回:一個包含將數組與子數組中全部元素的新數組。
flat
基本使用var arr1 = [1, 2, [3, 4]]; arr1.flat(); // [1, 2, 3, 4] var arr2 = [1, 2, [3, 4, [5, 6]]]; arr2.flat(); // [1, 2, 3, 4, [5, 6]] var arr3 = [1, 2, [3, 4, [5, 6]]]; arr3.flat(2); // [1, 2, 3, 4, 5, 6] //使用 Infinity,可展開任意深度的嵌套數組 var arr4 = [1, 2, [3, 4, [5, 6, [7, 8, [9, 10]]]]]; arr4.flat(Infinity); // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] // 移除數組中的空項 var arr5 = [1, 2, , 4, 5]; arr5.flat(); // [1, 2, 4, 5]
flatMap
方法首先使用映射函數映射每一個元素,而後將結果壓縮成一個新數組。它與
map 連着深度值爲1的
flat 幾乎相同,但
flatMap
一般在合併成一種方法的效率稍微高一些。
/** 參數: callback 能夠生成一個新數組中的元素的函數,能夠傳入三個參數: currentValue 當前正在數組中處理的元素 index可選 可選的。數組中正在處理的當前元素的索引。 array可選 可選的。被調用的 map 數組 thisArg可選 可選的。執行 callback 函數時 使用的this 值。 返回: 一個新的數組,其中每一個元素都是回調函數的結果,而且結構深度 depth 值爲1。 */ var new_array = arr.flatMap(function callback(currentValue[, index[, array]]) { // return element for new_array }[, thisArg])
var arr1 = [1, 2, 3, 4]; arr1.map(x => [x * 2]); // [[2], [4], [6], [8]] arr1.flatMap(x => [x * 2]); // [2, 4, 6, 8] // 只有一層 flattened arr1.flatMap(x => [[x * 2]]); // [[2], [4], [6], [8]]
ES2018關於非法轉義序列的修訂:
帶標籤的模版字符串應該容許嵌套支持常見轉義序列的語言(例如DSLs、LaTeX)。ECMAScript提議模版字面量修訂(第4階段,將要集成到ECMAScript 2018標準) 移除對ECMAScript在帶標籤的模版字符串中轉義序列的語法限制。
function latex(str) { return { "cooked": str[0], "raw": str.raw[0] } } latex`\unicode` // 較老版本 es2016或更早 // SyntaxError: malformed Unicode character escape sequence // es2018 // { cooked: undefined, raw: "\\unicode" }
let bad = `bad escape sequence: \unicode`; // 報錯 Uncaught SyntaxError: Invalid Unicode escape sequence
\s
or (dotAll
) 正則表達式中點.
匹配除回車外的任何單字符,標記s
改變這種行爲,容許行終止符的出現.
/foo.bar/.test('foo\nbar'); // → false /foo.bar/s.test('foo\nbar'); // → true
dotAll
:dotAll
屬性代表是否在正則表達式中一塊兒使用"s
"修飾符(引入/s修飾符,使得.能夠匹配任意單個字符)。dotAll
是一個只讀的屬性,屬於單個正則表達式實例。const re = /foo.bar/s; // Or, `const re = new RegExp('foo.bar', 's');`. re.test('foo\nbar'); // → true re.dotAll // → true re.flags // → 's'
ES2018容許命名捕獲組使用符號?<name>
,在打開捕獲括號(
後當即命名,示例以下:任何匹配失敗的命名組都將返回
undefined
。
exec
let re = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/u; let result = re.exec('2015-01-02'); // result.groups.year === '2015'; // result.groups.month === '01'; // result.groups.day === '02'; // result[0] === '2015-01-02'; // result[1] === '2015'; // result[2] === '01'; // result[3] === '02';
replace
let re = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/u; let result = '2015-01-02'.replace(re, '$<day>/$<month>/$<year>'); // result === '02/01/2015'
ES2015引入了 Rest參數和 擴展運算符。三個點(...)僅用於數組。Rest參數語法容許咱們將一個不定數量的參數表示爲一個數組。
// es2015 restFunc(1, 2, 3, 4, 5); function restFunc(arg1, arg2, ...arg3) { // arg1 = 1 // arg2 = 2 // arg3 = [3, 4, 5] } // 展開屬性 const arr = [1, 4, -1, 5, 9]; console.log(Math.max(...values)); // 9
ES2018爲對象解構提供了和數組同樣的Rest參數和(...)展開操做符
let { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 }; x; // 1 y; // 2 z; // { a: 3, b: 4 }
let n = { x, y, ...z }; n; // { x: 1, y: 2, a: 3, b: 4 }
?<=...
),它們確保包含在其中的模式位於聲明以後的模式以前// 匹配金額 /(?<=\$)\d+(\.\d*)?/.exec('$10.53'); // [10.53 ...] /(?<=\$)\d+(\.\d*)?/.exec('¥10.53'); // null
?<!...
),另外一方面,請確保其中的模式不在該斷言以後的模式以前/(?<!\$)\d+(?:\.\d*)/.exec('$10.53') // [0.53 ...] /(?<!\$)\d+(?:\.\d*)/.exec('¥10.53') // [10.53 ...]
es2018以前,在正則表達式中本地訪問 Unicode 字符屬性是不被容許的。ES2018添加了 Unicode 屬性轉義——形式爲
\p{...}
和\P{...}
,在正則表達式中使用標記u
(unicode) 設置,在\p
塊兒內,能夠以鍵值對的方式設置須要匹配的屬性而非具體內容。
const regex = /^\p{Decimal_Number}+$/u; regex.test('𝟏𝟐𝟑𝟜𝟝𝟞𝟩𝟪𝟫𝟬𝟭𝟮𝟯𝟺𝟻𝟼'); // → true const regex = /^\P{Decimal_Number}+$/u; regex.test('Իմ օդաթիռը լի է օձաձկերով'); // → true const regex = /^\p{Number}+$/u; regex.test('²³¹¼½¾𝟏𝟐𝟑𝟜𝟝𝟞𝟩𝟪𝟫𝟬𝟭𝟮𝟯𝟺𝟻𝟼㉛㉜㉝ⅠⅡⅢⅣⅤⅥⅦⅧⅨⅩⅪⅫⅬⅭⅮⅯⅰⅱⅲⅳⅴⅵⅶⅷⅸⅹⅺⅻⅼⅽⅾⅿ'); // → true
Promise.prototype.finally
finally()
方法返回一個Promise
。在promise結束時,不管結果是fulfilled或者是rejected,都會執行指定的回調函數。這爲在Promise
是否成功完成後都須要執行的代碼提供了一種方式。這避免了一樣的語句須要在
then()
和catch()
中各寫一次的狀況。注意: 在
finally
回調中throw
(或返回被拒絕的promise)將以throw()
指定的緣由拒絕新的promise.
finally()
雖然與 .then(onFinally, onFinally)
相似,它們不一樣的是:
promise
的最終狀態,因此finally
的回調函數中不接收任何參數,它僅用於不管最終結果如何都要執行的狀況。Promise.resolve(2).then(() => {}, () => {})
(resolved的結果爲undefined
)不一樣,Promise.resolve(2).finally(() => {})
resolved的結果爲 2
。Promise.reject(3).then(() => {}, () => {})
(resolved 的結果爲undefined
), Promise.reject(3).finally(() => {})
rejected 的結果爲 3
。let isLoading = true; fetch(myRequest).then(function(response) { var contentType = response.headers.get("content-type"); if(contentType && contentType.includes("application/json")) { return response.json(); } throw new TypeError("Oops, we haven't got JSON!"); }) .then(function(json) { /* process your JSON further */ }) .catch(function(error) { console.log(error); }) .finally(function() { isLoading = false; });
es2018引入異步迭代器for-await-of
,使得await
能夠和for...of
循環一塊兒使用,以串行的方式運行異步操做。
for await...of
語句會在異步或者同步可迭代對象上建立一個迭代循環,包括String
,Array
,Array
-like 對象(好比arguments
或者NodeList
),TypedArray
,Map
,Set
和自定義的異步或者同步可迭代對象。其會調用自定義迭代鉤子,併爲每一個不一樣屬性的值執行語句。像await
表達式同樣,這個語句只能在 async function內使用。
在
async/await
的某些時刻,你可能嘗試在同步循環中調用異步函數。
// 如下方法循環自己依舊同步,而且會在內部異步函數完成以前所有調用完成 async function process(array) { for (let i of array) { await doSomething(i); } } async function process(array) { array.forEach(async i => { await doSomething(i); }); }
async function process(array) { for await (let i of array) { doSomething(i); } }
var asyncIterable = { [Symbol.asyncIterator]() { return { i: 0, next() { if (this.i < 3) { return Promise.resolve({ value: this.i++, done: false }); } return Promise.resolve({ done: true }); } }; } }; (async function() { for await (num of asyncIterable) { console.log(num); } })(); // 0 // 1 // 2
async function* asyncGenerator() { var i = 0; while (i < 3) { yield i++; } } (async function() { for await (num of asyncGenerator()) { console.log(num); } })(); // 0 // 1 // 2
Object.values
/Object.entries
Object.values
Object.values()
方法返回一個給定對象自身的全部可枚舉屬性值的數組,值的順序與使用for...in
循環的順序相同 ( 區別在於 for-in 循環枚舉原型鏈中的屬性 )。
Object.entries
Object.entries()方法返回一個給定對象自身可枚舉屬性的鍵值對數組,其排列與使用 [
for...in`](
https://developer.mozilla.org... 循環遍歷該對象時返回的順序一致(區別在於 for-in 循環還會枚舉原型鏈中的屬性)。
var obj = { foo: 'bar', baz: 42 }; console.log(Object.values(obj)); // ['bar', 42] console.log(Object.entries(obj)); // [ ['foo', 'bar'], ['baz', 42] ]
String.prototype.padStart
padStart()
方法用另外一個字符串填充當前字符串(若是須要的話,會重複屢次),以便產生的字符串達到給定的長度。從當前字符串的左側開始填充。
String.prototype.padEnd
padEnd()
方法會用一個字符串填充當前字符串(若是須要的話則重複填充),返回填充後達到指定長度的字符串。從當前字符串的末尾(右側)開始填充。
/** * 參數 * * targetLength:當前字符串須要填充到的目標長度。若是這個數值小於當前字符串的長度,則返回當前字符串自己。 * padString(可選):填充字符串。若是字符串太長,使填充後的字符串長度超過了目標長度,則只保留最左側的部分,其餘部分會被截斷。此參數的缺省值爲 " "(U+0020)。 */ str.padStart(targetLength [, padString]) str.padEnd(targetLength [, padString])
'abc'.padStart(10); // " abc" 'abc'.padStart(10, "foo"); // "foofoofabc" 'abc'.padStart(6,"123465"); // "123abc" 'abc'.padStart(8, "0"); // "00000abc" 'abc'.padStart(1); // "abc" 'abc'.padEnd(10); // "abc " 'abc'.padEnd(10, "foo"); // "abcfoofoof" 'abc'.padEnd(6, "123456"); // "abc123" 'abc'.padEnd(1); // "abc"
Object.getOwnPropertyDescriptors
Object.getOwnPropertyDescriptors()
方法用來獲取一個對象的全部自身屬性的描述符。入參:任意對象
返回:所指定對象的全部自身屬性的描述符,若是沒有任何自身屬性,則返回空對象。
// Object.assign() 方法只能拷貝源對象的可枚舉的自身屬性,同時拷貝時沒法拷貝屬性的特性們,並且訪問器屬性會被轉換成數據屬性,也沒法拷貝源對象的原型,該方法配合 Object.create() 方法能夠實現上面說的這些。 Object.create( Object.getPrototypeOf(obj), Object.getOwnPropertyDescriptors(obj) );
在對由版本控制系統管理的代碼(git,subversion,mercurial等)進行此更改的過程當中,第3行和第9行的很是規/註釋代碼歷史記錄信息將更新爲指向添加逗號的人(而是而不是最初添加參數的人)。
1: function clownPuppiesEverywhere( 2: param1, 3: param2, // updated to add a comma 4: param3 // updated to add new parameter 5: ) { /* ... */ } 6: 7: clownPuppiesEverywhere( 8: 'foo', 9: 'bar', // updated to add a comma 10: 'baz' // updated to add new parameter 11: );
爲了幫助緩解此問題,某些其餘語言(Python,D,Hack等……可能還有其餘……)添加了語法支持,以容許在這些參數列表中使用逗號結尾。這使代碼提供者能夠始終在這些每行參數列表之一中以尾隨逗號結束參數添加,而沒必要擔憂代碼歸因問題
1: function clownPuppiesEverywhere( 2: param1, 3: param2, // Next parameter that's added only has to add a new line, not modify this line 5: ) { /* ... */ } 6: 7: clownPuppiesEverywhere( 8: 'foo', 9: 'bar', // Next parameter that's added only has to add a new line, not modify this line 11: );
注意,該建議僅與語法有關,而且不對語義進行任何更改,所以尾隨逗號的存在對諸如之類的東西沒有影響
<<function>>.length
。
async函數是使用async
關鍵字聲明的函數。 async函數是AsyncFunction
構造函數的實例, 而且其中容許使用await
關鍵字。async
和await
關鍵字讓咱們能夠用一種更簡潔的方式寫出基於Promise
的異步行爲,而無需刻意地鏈式調用promise
。async函數可能包含0個或者多個
await
表達式。await表達式會暫停整個async函數的執行進程並出讓其控制權,只有當其等待的基於promise的異步操做被兌現或被拒絕以後纔會恢復進程。promise的解決值會被看成該await表達式的返回值。使用async
/await
關鍵字就能夠在異步代碼中使用普通的try
/catch
代碼塊。
await
關鍵字只在async函數內有效。若是你在async函數體以外使用它,就會拋出語法錯誤SyntaxError
。
async
/await
的目的爲了簡化使用基於promise的API時所需的語法。async
/await
的行爲就好像搭配使用了生成器和promise。
async function foo() { return 1 } // 至關於 function foo() { return Promise.resolve(1) } // async函數的函數體能夠被看做是由0個或者多個await表達式分割開來的。 // 從第一行代碼直到(幷包括)第一個await表達式(若是有的話)都是同步運行的。 // 這樣的話,一個不含await表達式的async函數是會同步運行的。 // 然而,若是函數體內有一個await表達式,async函數就必定會異步執行。 async function foo() { await 1 } // 等價於 function foo() { return Promise.resolve(1).then(() => undefined) }
共享內存
和Atomics
(Shared memory and atomics)共享內存
和Atomics
:引入了一個新的構造函數SharedArrayBuffer
和 具備輔助函數的命名空間對象Atomics
SharedArrayBuffer
對象用來表示一個通用的,固定長度的原始二進制數據緩衝區,相似於 ArrayBuffer
對象,它們均可以用來在共享內存(shared memory)上建立視圖。與 ArrayBuffer
不一樣的是,SharedArrayBuffer
不能被分離。爲了將一個SharedArrayBuffer
對象從一個用戶代理共享到另外一個用戶代理(另外一個頁面的主進程或者當前頁面的一個worker
)從而實現共享內存,咱們須要運用postMessage
和結構化克隆算法( structured cloning )。
結構化克隆算法
接收被映射到一個新的SharedArrayBuffers
對象上的SharedArrayBuffers
對象與TypedArrays
對象。在這兩種映射下,這個新的SharedArrayBuffer
對象會被傳遞到目標用戶代理的接收函數上,致使在目標用戶代理產生了一個新的私有SharedArrayBuffer
對象(正如ArrayBuffer
同樣)。然而,這兩個SharedArrayBuffer
對象指向的共享數據塊實際上是同一個,而且在某一代理中的一個塊的反作用將最終致使另外一個代理具備可見性。
Atomics
對象提供了一組靜態方法對 SharedArrayBuffer
和 ArrayBuffer
對象進行原子操做。這些原子操做屬於Atomics
模塊。與通常的全局對象不一樣,Atomics
不是構造函數,所以不能使用 new 操做符調用,也不能將其看成函數直接調用。Atomics
的全部屬性和方法都是靜態的(與Math
對象同樣)。
Array.prototype.includes
includes()
方法用來判斷一個數組是否包含一個指定的值,根據狀況,若是包含則返回 true,不然返回false。
/** * valueToFind:須要查找的元素值。 * fromIndex(可選):從fromIndex 索引處開始查找 valueToFind。若是爲負值,則按升序從 array.length + fromIndex 的索引開始搜 (即便從末尾開始往前跳 fromIndex 的絕對值個索引,而後日後搜尋)。默認爲 0。 * 返回:返回一個布爾值 Boolean ,若是在數組中找到了(若是傳入了 fromIndex ,表示在 fromIndex 指定的索引範圍中找到了)則返回 true 。 */ arr.includes(valueToFind[, fromIndex])
[1, 2, 3].includes(2); // true [1, 2, 3].includes(4); // false [1, 2, 3].includes(3, 3); // false [1, 2, 3].includes(3, -1); // true [1, 2, NaN].includes(NaN); // true
fromIndex
爲負值,計算出的索引將做爲開始搜索searchElement
的位置。若是計算出的索引小於 0,則整個數組都會被搜索。// 計算索引小於0 // array length is 3 // fromIndex is -100 // computed index is 3 + (-100) = -97 var arr = ['a', 'b', 'c']; arr.includes('a', -100); // true arr.includes('b', -100); // true arr.includes('c', -100); // true arr.includes('a', -2); // false
求冪運算符(**
)返回將第一個操做數加到第二個操做數的冪的結果。它等效於Math.pow
,不一樣之處在於它也接受BigInts做爲操做數。求冪運算符是是右結合的:
a ** b ** c
等於a ** (b ** c)
.
2 ** 3 // 8 3 ** 2 // 9 3 ** 2.5 // 15.588457268119896 10 ** -1 // 0.1 NaN ** 2 // NaN // 右結合性 2 ** 3 ** 2 // 512 2 ** (3 ** 2) // 512 (2 ** 3) ** 2 // 64 // 與一元運算符結合 -(2 ** 2) // -4 (-2) ** 2 // 4
+/-/~/!/delete/void/typeof
)放在基數前,這樣作只會致使一個語法錯誤。-2 ** 2; // 4 in Bash, -4 in other languages. // This is invalid in JavaScript, as the operation is ambiguous. -(2 ** 2); // -4 in JavaScript and the author's intention is unambiguous.