- 做者:陳大魚頭
- github: KRISACHAN
ECMAScript是一種由Ecma國際(前身爲歐洲計算機制造商協會)在標準ECMA-262中定義的腳本語言規範。這種語言在萬維網上應用普遍,它每每被稱爲JavaScript或JScript,但實際上後二者是ECMA-262標準的實現和擴展。javascript
至發稿日爲止有九個ECMA-262版本發表。其歷史版本以下:html
"use strict"
)。修改了前面版本模糊不清的概念。增長了getters,setters,JSON以及在對象屬性上更完整的反射。TC39(Technical Committee 39)是一個推進JavaScript發展的委員會,它的成語來自各個主流瀏覽器的表明成語。會議實行多數決,每一項決策只有大部分人贊成且沒有強烈反對才能去實現。java
TC39成員制定着ECMAScript的將來。git
每一項新特性最終要進入到ECMAScript規範裏,須要經歷5個階段,這5個階段以下:es6
Stage 0: Strawpersongithub
只要是TC39成員或者貢獻者,均可以提交想法web
Stage 1: Proposalajax
這個階段肯定一個正式的提案正則表達式
Stage 2: draft算法
規範的第一個版本,進入此階段的提案大機率會成爲標準
Stage 3: Candidate
進一步完善提案細則
Stage 4: Finished
表示已準備好將其添加到正式的ECMAScript標準中
因爲ES6之前的屬性誕生年末久遠,咱們使用也比較廣泛,遂不進行說明,ES6以後的語言風格跟ES5之前的差別比較大,因此單獨拎出來作個記錄。
ES6是一次重大的革新,比起過去的版本,改動比較大,本文僅對經常使用的API以及語法糖進行講解。
在ES6之前,JS
只有var
一種聲明方式,可是在ES6以後,就多了let
跟const
這兩種方式。用var
定義的變量沒有塊級做用域的概念,而let
跟const
則會有,由於這三個關鍵字建立是不同的。
區別以下:
{
var a = 10
let b = 20
const c = 30
}
a // 10
b // Uncaught ReferenceError: b is not defined
c // c is not defined
let d = 40
const e = 50
d = 60
d // 60
e = 70 // VM231:1 Uncaught TypeError: Assignment to constant variable.
複製代碼
var | let | const | |
---|---|---|---|
變量提高 | √ | × | × |
全局變量 | √ | × | × |
重複聲明 | √ | × | × |
從新賦值 | √ | √ | × |
暫時死區 | × | √ | √ |
塊做用域 | × | √ | √ |
只聲明不初始化 | √ | √ | × |
在ES6以前,若是咱們要生成一個實例對象,傳統的方法就是寫一個構造函數,例子以下:
function Person(name, age) {
this.name = name
this.age = age
}
Person.prototype.information = function () {
return 'My name is ' + this.name + ', I am ' + this.age
}
複製代碼
可是在ES6以後,咱們只須要寫成如下形式:
class Person {
constructor(name, age) {
this.name = name
this.age = age
}
information() {
return 'My name is ' + this.name + ', I am ' + this.age
}
}
複製代碼
箭頭函數表達式的語法比函數表達式更簡潔,而且沒有本身的this
,arguments
,super
或 new.target
。這些函數表達式更適用於那些原本須要匿名函數的地方,而且它們不能用做構造函數。
在ES6之前,咱們寫函數通常是:
var list = [1, 2, 3, 4, 5, 6, 7]
var newList = list.map(function (item) {
return item * item
})
複製代碼
可是在ES6裏,咱們能夠:
const list = [1, 2, 3, 4, 5, 6, 7]
const newList = list.map(item => item * item)
複製代碼
看,是否是簡潔了很多
在ES6以前,若是咱們寫函數須要定義初始值的時候,須要這麼寫:
function config (data) {
var data = data || 'data is empty'
}
複製代碼
這樣看起來也沒有問題,可是若是參數的布爾值爲falsy時就會出問題,例如咱們這樣調用config:
config(0)
config('')
複製代碼
那麼結果就永遠是後面的值
若是咱們用函數參數默認值就沒有這個問題,寫法以下:
const config = (data = 'data is empty') => {}
複製代碼
在ES6以前,若是咱們要拼接字符串,則須要像這樣:
var name = 'kris'
var age = 24
var info = 'My name is ' + this.name + ', I am ' + this.age
複製代碼
可是在ES6以後,咱們只須要寫成如下形式:
const name = 'kris'
const age = 24
const info = `My name is ${name}, I am ${age}`
複製代碼
咱們經過解構賦值, 能夠將屬性/值從對象/數組中取出,賦值給其餘變量。
好比咱們須要交換兩個變量的值,在ES6以前咱們可能須要:
var a = 10
var b = 20
var temp = a
a = b
b = temp
複製代碼
可是在ES6裏,咱們有:
let a = 10
let b = 20
[a, b] = [b, a]
複製代碼
是否是方便不少
在ES6以前,JS並無模塊化的概念,有的也只是社區定製的相似CommonJS和AMD之類的規則。例如基於CommonJS的NodeJS:
// circle.js
// 輸出
const { PI } = Math
exports.area = (r) => PI * r ** 2
exports.circumference = (r) => 2 * PI * r
// index.js
// 輸入
const circle = require('./circle.js')
console.log(`半徑爲 4 的圓的面積是 ${circle.area(4)}`)
複製代碼
在ES6以後咱們則能夠寫成如下形式:
// circle.js
// 輸出
const { PI } = Math
export const area = (r) => PI * r ** 2
export const circumference = (r) => 2 * PI * r
// index.js
// 輸入
import {
area
} = './circle.js'
console.log(`半徑爲 4 的圓的面積是: ${area(4)}`)
複製代碼
擴展操做符能夠在函數調用/數組構造時, 將數組表達式或者string在語法層面展開;還能夠在構造字面量對象時, 將對象表達式按key-value的方式展開。
好比在ES5的時候,咱們要對一個數組的元素進行相加,在不使用reduce
或者reduceRight
的場合,咱們須要:
function sum(x, y, z) {
return x + y + z;
}
var list = [5, 6, 7]
var total = sum.apply(null, list)
複製代碼
可是若是咱們使用擴展操做符,只須要以下:
const sum = (x, y, z) => x + y + z
const list = [5, 6, 7]
const total = sum(...list)
複製代碼
很是的簡單,可是要注意的是擴展操做符只能用於可迭代對象
若是是下面的狀況,是會報錯的:
var obj = {'key1': 'value1'}
var array = [...obj] // TypeError: obj is not iterable
複製代碼
在ES6以前,若是咱們要將某個變量賦值爲一樣名稱的對象元素,則須要:
var cat = 'Miaow'
var dog = 'Woof'
var bird = 'Peet peet'
var someObject = {
cat: cat,
dog: dog,
bird: bird
}
複製代碼
可是在ES6裏咱們就方便不少:
let cat = 'Miaow'
let dog = 'Woof'
let bird = 'Peet peet'
let someObject = {
cat,
dog,
bird
}
console.log(someObject)
//{
// cat: "Miaow",
// dog: "Woof",
// bird: "Peet peet"
//}
複製代碼
很是方便
Promise 是ES6提供的一種異步解決方案,比回調函數更加清晰明瞭。
Promise
翻譯過來就是承諾的意思,這個承諾會在將來有一個確切的答覆,而且該承諾有三種狀態,分別是:
這個承諾一旦從等待狀態變成爲其餘狀態就永遠不能更改狀態了,也就是說一旦狀態變爲 resolved 後,就不能再次改變
new Promise((resolve, reject) => {
resolve('success')
// 無效
reject('reject')
})
複製代碼
當咱們在構造 Promise
的時候,構造函數內部的代碼是當即執行的
new Promise((resolve, reject) => {
console.log('new Promise')
resolve('success')
})
console.log('finifsh')
// new Promise -> finifsh
複製代碼
Promise
實現了鏈式調用,也就是說每次調用 then
以後返回的都是一個 Promise
,而且是一個全新的 Promise
,緣由也是由於狀態不可變。若是你在 then
中 使用了 return
,那麼 return
的值會被 Promise.resolve()
包裝
Promise.resolve(1)
.then(res => {
console.log(res) // => 1
return 2 // 包裝成 Promise.resolve(2)
})
.then(res => {
console.log(res) // => 2
})
複製代碼
固然了,Promise
也很好地解決了回調地獄的問題,例如:
ajax(url, () => {
// 處理邏輯
ajax(url1, () => {
// 處理邏輯
ajax(url2, () => {
// 處理邏輯
})
})
})
複製代碼
能夠改寫成:
ajax(url)
.then(res => {
console.log(res)
return ajax(url1)
}).then(res => {
console.log(res)
return ajax(url2)
}).then(res => console.log(res))
複製代碼
for...of
語句在可迭代對象(包括 Array,Map,Set,String,TypedArray,arguments
對象等等)上建立一個迭代循環,調用自定義迭代鉤子,併爲每一個不一樣屬性的值執行語句。
例子以下:
const array1 = ['a', 'b', 'c'];
for (const element of array1) {
console.log(element)
}
// "a"
// "b"
// "c"
複製代碼
symbol 是一種基本數據類型,Symbol()
函數會返回symbol類型的值,該類型具備靜態屬性和靜態方法。它的靜態屬性會暴露幾個內建的成員對象;它的靜態方法會暴露全局的symbol註冊,且相似於內建對象類,但做爲構造函數來講它並不完整,由於它不支持語法:"new Symbol()
"。
每一個從Symbol()
返回的symbol值都是惟一的。一個symbol值能做爲對象屬性的標識符;這是該數據類型僅有的目的。
例子以下:
const symbol1 = Symbol();
const symbol2 = Symbol(42);
const symbol3 = Symbol('foo');
console.log(typeof symbol1); // "symbol"
console.log(symbol3.toString()); // "Symbol(foo)"
console.log(Symbol('foo') === Symbol('foo')); // false
複製代碼
迭代器(Iterator)是一種迭代的機制,爲各類不一樣的數據結構提供統一的訪問機制。任何數據結構只要內部有 Iterator 接口,就能夠完成依次迭代操做。
一旦建立,迭代器對象能夠經過重複調用next()顯式地迭代,從而獲取該對象每一級的值,直到迭代完,返回{ value: undefined, done: true }
雖然自定義的迭代器是一個有用的工具,但因爲須要顯式地維護其內部狀態,所以須要謹慎地建立。生成器函數提供了一個強大的選擇:它容許你定義一個包含自有迭代算法的函數, 同時它能夠自動維護本身的狀態。 生成器函數使用 function*
語法編寫。 最初調用時,生成器函數不執行任何代碼,而是返回一種稱爲Generator的迭代器。 經過調用生成器的下一個方法消耗值時,Generator函數將執行,直到遇到yield關鍵字。
能夠根據須要屢次調用該函數,而且每次都返回一個新的Generator,但每一個Generator只能迭代一次。
因此咱們能夠有如下例子:
function* makeRangeIterator(start = 0, end = Infinity, step = 1) {
for (let i = start; i < end; i += step) {
yield i;
}
}
var a = makeRangeIterator(1,10,2)
a.next() // {value: 1, done: false}
a.next() // {value: 3, done: false}
a.next() // {value: 5, done: false}
a.next() // {value: 7, done: false}
a.next() // {value: 9, done: false}
a.next() // {value: undefined, done: true}
複製代碼
Set
對象容許你存儲任何類型的惟一值,不管是原始值或者是對象引用。
因此咱們能夠經過Set
實現數組去重
const numbers = [2,3,4,4,2,3,3,4,4,5,5,6,6,7,5,32,3,4,5]
console.log([...new Set(numbers)])
// [2, 3, 4, 5, 6, 7, 32]
複製代碼
WeakSet
結構與 Set
相似,但區別有如下兩點:
WeakSet
對象中只能存放對象引用, 不能存放值, 而 Set
對象均可以。WeakSet
對象中存儲的對象值都是被弱引用的, 若是沒有其餘的變量或屬性引用這個對象值, 則這個對象值會被當成垃圾回收掉. 正由於這樣, WeakSet
對象是沒法被枚舉的, 沒有辦法拿到它包含的全部元素。因此代碼以下:
var ws = new WeakSet()
var obj = {}
var foo = {}
ws.add(window)
ws.add(obj)
ws.has(window) // true
ws.has(foo) // false, 對象 foo 並無被添加進 ws 中
ws.delete(window) // 從集合中刪除 window 對象
ws.has(window) // false, window 對象已經被刪除了
ws.clear() // 清空整個 WeakSet 對象
複製代碼
Map
對象保存鍵值對。任何值(對象或者原始值) 均可以做爲一個鍵或一個值。
例子以下,咱們甚至可使用NaN
來做爲鍵值:
var myMap = new Map();
myMap.set(NaN, "not a number");
myMap.get(NaN); // "not a number"
var otherNaN = Number("foo");
myMap.get(otherNaN); // "not a number"
複製代碼
WeakMap
對象是一組鍵/值對的集合,其中的鍵是弱引用的。其鍵必須是對象,而值能夠是任意的。
跟Map
的區別與Set
跟WeakSet
的區別類似,具體代碼以下:
var wm1 = new WeakMap(),
wm2 = new WeakMap(),
wm3 = new WeakMap();
var o1 = {},
o2 = function(){},
o3 = window;
wm1.set(o1, 37);
wm1.set(o2, "azerty");
wm2.set(o1, o2); // value能夠是任意值,包括一個對象
wm2.set(o3, undefined);
wm2.set(wm1, wm2); // 鍵和值能夠是任意對象,甚至另一個WeakMap對象
wm1.get(o2); // "azerty"
wm2.get(o2); // undefined,wm2中沒有o2這個鍵
wm2.get(o3); // undefined,值就是undefined
wm1.has(o2); // true
wm2.has(o2); // false
wm2.has(o3); // true (即便值是undefined)
wm3.set(o1, 37);
wm3.get(o1); // 37
wm3.clear();
wm3.get(o1); // undefined,wm3已被清空
wm1.has(o1); // true
wm1.delete(o1);
wm1.has(o1); // false
複製代碼
Proxy
對象用於定義基本操做的自定義行爲(如屬性查找,賦值,枚舉,函數調用等)。
Reflect
是一個內置的對象,它提供攔截 JavaScript 操做的方法。這些方法與 Proxy
的方法相同。Reflect
不是一個函數對象,所以它是不可構造的。
Proxy
跟Reflect
是很是完美的配合,例子以下:
const observe = (data, callback) => {
return new Proxy(data, {
get(target, key) {
return Reflect.get(target, key)
},
set(target, key, value, proxy) {
callback(key, value);
target[key] = value;
return Reflect.set(target, key, value, proxy)
}
})
}
const FooBar = { open: false };
const FooBarObserver = observe(FooBar, (property, value) => {
property === 'open' && value
? console.log('FooBar is open!!!')
: console.log('keep waiting');
});
console.log(FooBarObserver.open) // false
FooBarObserver.open = true // FooBar is open!!!
複製代碼
固然也不是什麼均可以被代理的,若是對象帶有configurable: false
跟writable: false
屬性,則代理失效。
i
修飾符
// i 修飾符
/[a-z]/i.test('\u212A') // false
/[a-z]/iu.test('\u212A') // true
複製代碼
y
修飾符
// y修飾符
var s = 'aaa_aa_a';
var r1 = /a+/g;
var r2 = /a+/y;
r1.exec(s) // ["aaa"]
r2.exec(s) // ["aaa"]
r1.exec(s) // ["aa"]
r2.exec(s) // null
複製代碼
String.prototype.flags
// 查看RegExp構造函數的修飾符
var regex = new RegExp('xyz', 'i')
regex.flags // 'i'
複製代碼
unicode模式
var s = '𠮷'
/^.$/.test(s) // false
/^.$/u.test(s) // true
複製代碼
u轉義
// u轉義
/\,/ // /\,/
/\,/u // 報錯 沒有u修飾符時,逗號前面的反斜槓是無效的,加了u修飾符就報錯。
複製代碼
引用
const RE_TWICE = /^(?<word>[a-z]+)!\k<word>$/;
RE_TWICE.test('abc!abc') // true
RE_TWICE.test('abc!ab') // false
const RE_TWICE = /^(?<word>[a-z]+)!\1$/;
RE_TWICE.test('abc!abc') // true
RE_TWICE.test('abc!ab') // false
複製代碼
RegExp
方法String.prototype.match
調用 RegExp.prototype[Symbol.match]
String.prototype.replace
調用 RegExp.prototype[Symbol.replace]
String.prototype.search
調用 RegExp.prototype[Symbol.search]
String.prototype.split
調用 RegExp.prototype[Symbol.split]
RegExp.prototype.sticky
表示是否有y
修飾符
/hello\d/y.sticky // true
複製代碼
RegExp.prototype.flags
獲取修飾符
/abc/ig.flags // 'gi'
複製代碼
二進制表示法
: 0b或0B開頭
表示二進制(0bXX
或0BXX
)
二進制表示法
: 0b或0B開頭
表示二進制(0bXX
或0BXX
)
八進制表示法
: 0o或0O開頭
表示二進制(0oXX
或0OXX
)
Number.EPSILON
: 數值最小精度
Number.MIN_SAFE_INTEGER
: 最小安全數值(-2^53
)
Number.MAX_SAFE_INTEGER
: 最大安全數值(2^53
)
Number.parseInt()
: 返回轉換值的整數部分
Number.parseFloat()
: 返回轉換值的浮點數部分
Number.isFinite()
: 是否爲有限數值
Number.isNaN()
: 是否爲NaN
Number.isInteger()
: 是否爲整數
Number.isSafeInteger()
: 是否在數值安全範圍內
Math.trunc()
: 返回數值整數部分
Math.sign()
: 返回數值類型(正數1
、負數-1
、零0
)
Math.cbrt()
: 返回數值立方根
Math.clz32()
: 返回數值的32位無符號整數形式
Math.imul()
: 返回兩個數值相乘
Math.fround()
: 返回數值的32位單精度浮點數形式
Math.hypot()
: 返回全部數值平方和的平方根
Math.expm1()
: 返回e^n - 1
Math.log1p()
: 返回1 + n
的天然對數(Math.log(1 + n)
)
Math.log10()
: 返回以10爲底的n的對數
Math.log2()
: 返回以2爲底的n的對數
Math.sinh()
: 返回n的雙曲正弦
Math.cosh()
: 返回n的雙曲餘弦
Math.tanh()
: 返回n的雙曲正切
Math.asinh()
: 返回n的反雙曲正弦
Math.acosh()
: 返回n的反雙曲餘弦
Math.atanh()
: 返回n的反雙曲正切
Array.prototype.from
:轉換具備Iterator接口
的數據結構爲真正數組,返回新數組。
console.log(Array.from('foo')) // ["f", "o", "o"]
console.log(Array.from([1, 2, 3], x => x + x)) // [2, 4, 6]
複製代碼
Array.prototype.of()
:轉換一組值爲真正數組,返回新數組。
Array.of(7) // [7]
Array.of(1, 2, 3) // [1, 2, 3]
Array(7) // [empty, empty, empty, empty, empty, empty]
Array(1, 2, 3) // [1, 2, 3]
複製代碼
Array.prototype.copyWithin()
:把指定位置的成員複製到其餘位置,返回原數組
const array1 = ['a', 'b', 'c', 'd', 'e']
console.log(array1.copyWithin(0, 3, 4)) // ["d", "b", "c", "d", "e"]
console.log(array1.copyWithin(1, 3)) // ["d", "d", "e", "d", "e"]
複製代碼
Array.prototype.find()
:返回第一個符合條件的成員
const array1 = [5, 12, 8, 130, 44]
const found = array1.find(element => element > 10)
console.log(found) // 12
複製代碼
Array.prototype.findIndex()
:返回第一個符合條件的成員索引值
const array1 = [5, 12, 8, 130, 44]
const isLargeNumber = (element) => element > 13
console.log(array1.findIndex(isLargeNumber)) // 3
複製代碼
Array.prototype.fill()
:根據指定值填充整個數組,返回原數組
const array1 = [1, 2, 3, 4]
console.log(array1.fill(0, 2, 4)) // [1, 2, 0, 0]
console.log(array1.fill(5, 1)) // [1, 5, 5, 5]
console.log(array1.fill(6)) // [6, 6, 6, 6]
複製代碼
Array.prototype.keys()
:返回以索引值爲遍歷器的對象
const array1 = ['a', 'b', 'c']
const iterator = array1.keys()
for (const key of iterator) {
console.log(key)
}
// 0
// 1
// 2
複製代碼
Array.prototype.values()
:返回以屬性值爲遍歷器的對象
const array1 = ['a', 'b', 'c']
const iterator = array1.values()
for (const key of iterator) {
console.log(key)
}
// a
// b
// c
複製代碼
Array.prototype.entries()
:返回以索引值和屬性值爲遍歷器的對象
const array1 = ['a', 'b', 'c']
const iterator = array1.entries()
console.log(iterator.next().value) // [0, "a"]
console.log(iterator.next().value) // [1, "b"]
複製代碼
數組空位
:ES6明確將數組空位轉爲undefined
或者empty
Array.from(['a',,'b']) // [ "a", undefined, "b" ]
[...['a',,'b']] // [ "a", undefined, "b" ]
Array(3) // [empty × 3]
[,'a'] // [empty, "a"]
複製代碼
includes()
方法用來判斷一個數組是否包含一個指定的值,根據狀況,若是包含則返回 true,不然返回false。
代碼以下:
const array1 = [1, 2, 3]
console.log(array1.includes(2)) // true
const pets = ['cat', 'dog', 'bat']
console.log(pets.includes('cat')) // true
console.log(pets.includes('at')) // false
複製代碼
冪運算符**,具備與Math.pow()同樣的功能,代碼以下:
console.log(2**10) // 1024
console.log(Math.pow(2, 10)) // 1024
複製代碼
自ES7起,帶標籤的模版字面量遵照如下轉義序列的規則:
\u00A9
\u{2F804}
\xA9
\251
這表示相似下面這種帶標籤的模版是有問題的,由於對於每個ECMAScript語法,解析器都會去查找有效的轉義序列,可是隻能獲得這是一個形式錯誤的語法:
latex`\unicode`
// 在較老的ECMAScript版本中報錯(ES2016及更早)
// SyntaxError: malformed Unicode character escape sequence
複製代碼
雖然Promise
能夠解決回調地獄的問題,可是鏈式調用太多,則會變成另外一種形式的回調地獄 —— 麪條地獄,因此在ES8裏則出現了Promise
的語法糖async/await
,專門解決這個問題。
咱們先看一下下面的Promise
代碼:
fetch('coffee.jpg')
.then(response => response.blob())
.then(myBlob => {
let objectURL = URL.createObjectURL(myBlob)
let image = document.createElement('img')
image.src = objectURL
document.body.appendChild(image)
})
.catch(e => {
console.log('There has been a problem with your fetch operation: ' + e.message)
})
複製代碼
而後再看看async/await
版的,這樣看起來是否是更清晰了。
async function myFetch() {
let response = await fetch('coffee.jpg')
let myBlob = await response.blob()
let objectURL = URL.createObjectURL(myBlob)
let image = document.createElement('img')
image.src = objectURL
document.body.appendChild(image)
}
myFetch()
複製代碼
固然,若是你喜歡,你甚至能夠二者混用
async function myFetch() {
let response = await fetch('coffee.jpg')
return await response.blob()
}
myFetch().then((blob) => {
let objectURL = URL.createObjectURL(blob)
let image = document.createElement('img')
image.src = objectURL
document.body.appendChild(image)
})
複製代碼
Object.values()
方法返回一個給定對象自身的全部可枚舉屬性值的數組,值的順序與使用for...in循環的順序相同 ( 區別在於 for-in 循環枚舉原型鏈中的屬性 )。
代碼以下:
const object1 = {
a: 'somestring',
b: 42,
c: false
}
console.log(Object.values(object1)) // ["somestring", 42, false]
複製代碼
Object.entries()
方法返回一個給定對象自身可枚舉屬性的鍵值對數組,其排列與使用 for...in 循環遍歷該對象時返回的順序一致(區別在於 for-in 循環還會枚舉原型鏈中的屬性)。
代碼以下:
const object1 = {
a: 'somestring',
b: 42
}
for (let [key, value] of Object.entries(object1)) {
console.log(`${key}: ${value}`)
}
// "a: somestring"
// "b: 42"
複製代碼
padStart()
方法用另外一個字符串填充當前字符串(重複,若是須要的話),以便產生的字符串達到給定的長度。填充從當前字符串的開始(左側)應用的。
代碼以下:
const str1 = '5'
console.log(str1.padStart(2, '0')) // "05"
const fullNumber = '2034399002125581'
const last4Digits = fullNumber.slice(-4)
const maskedNumber = last4Digits.padStart(fullNumber.length, '*')
console.log(maskedNumber) // "************5581"
複製代碼
padEnd()
方法會用一個字符串填充當前字符串(若是須要的話則重複填充),返回填充後達到指定長度的字符串。從當前字符串的末尾(右側)開始填充。
const str1 = 'Breaded Mushrooms'
console.log(str1.padEnd(25, '.')) // "Breaded Mushrooms........"
const str2 = '200'
console.log(str2.padEnd(5)) // "200 "
複製代碼
###函數參數結尾逗號(Function parameter lists and calls trailing commas)
在ES5裏就添加了對象的尾逗號,不過並不支持函數參數,可是在ES8以後,便開始支持這一特性,代碼以下:
// 參數定義
function f(p) {}
function f(p,) {}
(p) => {}
(p,) => {}
class C {
one(a,) {},
two(a, b,) {},
}
var obj = {
one(a,) {},
two(a, b,) {},
};
// 函數調用
f(p)
f(p,)
Math.max(10, 20)
Math.max(10, 20,)
複製代碼
可是如下的方式是不合法的:
僅僅包含逗號的函數參數定義或者函數調用會拋出 SyntaxError。 並且,當使用剩餘參數的時候,並不支持尾後逗號,例子以下:
function f(,) {} // SyntaxError: missing formal parameter
(,) => {} // SyntaxError: expected expression, got ','
f(,) // SyntaxError: expected expression, got ','
function f(...p,) {} // SyntaxError: parameter after rest parameter
(...p,) => {} // SyntaxError: expected closing parenthesis, got ','
複製代碼
在解構裏也可使用,代碼以下:
// 帶有尾後逗號的數組解構
[a, b,] = [1, 2]
// 帶有尾後逗號的對象解構
var o = {
p: 42,
q: true,
}
var {p, q,} = o
複製代碼
一樣地,在使用剩餘參數時,會拋出 SyntaxError,代碼以下:
var [a, ...b,] = [1, 2, 3] // SyntaxError: rest element may not have a trailing comma
複製代碼
SharedArrayBuffer 對象用來表示一個通用的,固定長度的原始二進制數據緩衝區,相似於 ArrayBuffer 對象,它們均可以用來在共享內存(shared memory)上建立視圖。與 ArrayBuffer 不一樣的是,SharedArrayBuffer 不能被分離。
代碼以下:
let sab = new SharedArrayBuffer(1024) // 必須實例化
worker.postMessage(sab)
複製代碼
Atomics對象 提供了一組靜態方法用來對 SharedArrayBuffer
對象進行原子操做。
方法以下:
Atomics.add() :將指定位置上的數組元素與給定的值相加,並返回相加前該元素的值。
Atomics.and():將指定位置上的數組元素與給定的值相與,並返回與操做前該元素的值。
Atomics.compareExchange():若是數組中指定的元素與給定的值相等,則將其更新爲新的值,並返回該元素原先的值。
Atomics.exchange():將數組中指定的元素更新爲給定的值,並返回該元素更新前的值。
Atomics.load():返回數組中指定元素的值。
Atomics.or():將指定位置上的數組元素與給定的值相或,並返回或操做前該元素的值。
Atomics.store():將數組中指定的元素設置爲給定的值,並返回該值。
Atomics.sub():將指定位置上的數組元素與給定的值相減,並返回相減前該元素的值。
Atomics.xor():將指定位置上的數組元素與給定的值相異或,並返回異或操做前該元素的值。
Atomics.wait():檢測數組中某個指定位置上的值是否仍然是給定值,是則保持掛起直到被喚醒或超時。返回值爲 "ok"、"not-equal" 或 "time-out"。調用時,若是當前線程不容許阻塞,則會拋出異常(大多數瀏覽器都不容許在主線程中調用 wait())。
Atomics.wake():喚醒等待隊列中正在數組指定位置的元素上等待的線程。返回值爲成功喚醒的線程數量。
Atomics.isLockFree(size):能夠用來檢測當前系統是否支持硬件級的原子操做。對於指定大小的數組,若是當前系統支持硬件級的原子操做,則返回 true;不然就意味着對於該數組,Atomics 對象中的各原子操做都只能用鎖來實現。此函數面向的是技術專家。
Object.getOwnPropertyDescriptors()
方法用來獲取一個對象的全部自身屬性的描述符。代碼以下:
const object1 = {
property1: 42
}
const descriptors1 = Object.getOwnPropertyDescriptors(object1)
console.log(descriptors1.property1.writable) // true
console.log(descriptors1.property1.value) // 42
// 淺拷貝一個對象
Object.create(
Object.getPrototypeOf(obj),
Object.getOwnPropertyDescriptors(obj)
)
// 建立子類
function superclass() {}
superclass.prototype = {
// 在這裏定義方法和屬性
}
function subclass() {}
subclass.prototype = Object.create(superclass.prototype, Object.getOwnPropertyDescriptors({
// 在這裏定義方法和屬性
}))
複製代碼
for await...of
語句會在異步或者同步可迭代對象上建立一個迭代循環,包括 String
,Array
,Array-like
對象(好比arguments
或者NodeList
),TypedArray
,Map
, Set
和自定義的異步或者同步可迭代對象。其會調用自定義迭代鉤子,併爲每一個不一樣屬性的值執行語句。
配合迭代異步生成器,例子以下:
async function* asyncGenerator() {
var i = 0
while (i < 3) {
yield i++
}
}
(async function() {
for await (num of asyncGenerator()) {
console.log(num)
}
})()
// 0
// 1
// 2
複製代碼
ES9開始,模板字符串容許嵌套支持常見轉義序列,移除對ECMAScript在帶標籤的模版字符串中轉義序列的語法限制。
不過,非法轉義序列在"cooked"當中仍然會體現出來。它們將以undefined
元素的形式存在於"cooked"之中,代碼以下:
function latex(str) {
return { "cooked": str[0], "raw": str.raw[0] }
}
latex`\unicode` // { cooked: undefined, raw: "\\unicode" }
複製代碼
首先咱們得先知道什麼是斷言(Assertion)。
**斷言(Assertion)**是一個對當前匹配位置以前或以後的字符的測試, 它不會實際消耗任何字符,因此斷言也被稱爲「非消耗性匹配」或「非獲取匹配」。
正則表達式的斷言一共有 4 種形式:
(?=pattern)
零寬正向確定斷言(zero-width positive lookahead assertion)(?!pattern)
零寬正向否認斷言(zero-width negative lookahead assertion)(?<=pattern)
零寬反向確定斷言(zero-width positive lookbehind assertion)(?<!pattern)
零寬反向否認斷言(zero-width negative lookbehind assertion)在ES9以前,JavaScript 正則表達式,只支持正向斷言。正向斷言的意思是:當前位置後面的字符串應該知足斷言,可是並不捕獲。例子以下:
'fishHeadfishTail'.match(/fish(?=Head)/g) // ["fish"]
複製代碼
反向斷言和正向斷言的行爲同樣,只是方向相反。例子以下:
'abc123'.match(/(?<=(\d+)(\d+))$/) // ["", "1", "23", index: 6, input: "abc123", groups: undefined]
複製代碼
正則表達式中的Unicode轉義符容許根據Unicode字符屬性匹配Unicode字符。 它容許區分字符類型,例如大寫和小寫字母,數學符號和標點符號。
部分例子代碼以下:
// 匹配全部數字
const regex = /^\p{Number}+$/u;
regex.test('²³¹¼½¾') // true
regex.test('㉛㉜㉝') // true
regex.test('ⅠⅡⅢⅣⅤⅥⅦⅧⅨⅩⅪⅫ') // true
// 匹配全部空格
\p{White_Space}
// 匹配各類文字的全部字母,等同於 Unicode 版的 \w
[\p{Alphabetic}\p{Mark}\p{Decimal_Number}\p{Connector_Punctuation}\p{Join_Control}]
// 匹配各類文字的全部非字母的字符,等同於 Unicode 版的 \W
[^\p{Alphabetic}\p{Mark}\p{Decimal_Number}\p{Connector_Punctuation}\p{Join_Control}]
// 匹配 Emoji
/\p{Emoji_Modifier_Base}\p{Emoji_Modifier}?|\p{Emoji_Presentation}|\p{Emoji}\uFE0F/gu
// 匹配全部的箭頭字符
const regexArrows = /^\p{Block=Arrows}+$/u;
regexArrows.test('←↑→↓↔↕↖↗↘↙⇏⇐⇑⇒⇓⇔⇕⇖⇗⇘⇙⇧⇩') // true
複製代碼
具體的屬性列表可查看:developer.mozilla.org/en-US/docs/…
在以往的版本里,JS的正則的.
只能匹配emoji跟行終結符之外的全部文本,例如:
let regex = /./;
regex.test('\n'); // false
regex.test('\r'); // false
regex.test('\u{2028}'); // false
regex.test('\u{2029}'); // false
regex.test('\v'); // true
regex.test('\f'); // true
regex.test('\u{0085}'); // true
/foo.bar/.test('foo\nbar'); // false
/foo[^]bar/.test('foo\nbar'); // true
/foo.bar/.test('foo\nbar'); // false
/foo[\s]bar/.test('foo\nbar'); // true
複製代碼
可是在ES9以後,JS正則增長了一個新的標誌 s
用來表示 dotAll,這能夠匹配任意字符。代碼以下:
/foo.bar/s.test('foo\nbar'); // true
const re = /foo.bar/s; // 等價於 const re = new RegExp('foo.bar', 's');
re.test('foo\nbar'); // true
re.dotAll; // true
re.flags; // "s"
複製代碼
在以往的版本里,JS的正則分組是沒法命名的,因此容易混淆。例以下面獲取年月日的例子,很容易讓人搞不清哪一個是月份,哪一個是年份:
const matched = /(\d{4})-(\d{2})-(\d{2})/.exec('2019-01-01')
console.log(matched[0]); // 2019-01-01
console.log(matched[1]); // 2019
console.log(matched[2]); // 01
console.log(matched[3]); // 01
複製代碼
ES9引入了命名捕獲組,容許爲每個組匹配指定一個名字,既便於閱讀代碼,又便於引用。代碼以下:
const RE_DATE = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/;
const matchObj = RE_DATE.exec('1999-12-31');
const year = matchObj.groups.year; // 1999
const month = matchObj.groups.month; // 12
const day = matchObj.groups.day; // 31
const RE_OPT_A = /^(?<as>a+)?$/;
const matchObj = RE_OPT_A.exec('');
matchObj.groups.as // undefined
'as' in matchObj.groups // true
複製代碼
ES6中添加了數組的擴展操做符,讓咱們在操做數組時更加簡便,美中不足的是並不支持對象擴展操做符,可是在ES9開始,這一功能也獲得了支持,例如:
var obj1 = { foo: 'bar', x: 42 };
var obj2 = { foo: 'baz', y: 13 };
var clonedObj = { ...obj1 };
// 克隆後的對象: { foo: "bar", x: 42 }
var mergedObj = { ...obj1, ...obj2 };
// 合併後的對象: { foo: "baz", x: 42, y: 13 }
複製代碼
上面即是一個簡便的淺拷貝。這裏有一點小提示,就是Object.assign()
函數會觸發 setters
,而展開語法則不會。因此不能替換也不能模擬Object.assign()
。
若是存在相同的屬性名,只有最後一個會生效。
finally()
方法會返回一個Promise
,當promise的狀態變動,無論是變成rejected
或者fulfilled
,最終都會執行finally()
的回調。
例子以下:
fetch(url)
.then((res) => {
console.log(res)
})
.catch((error) => {
console.log(error)
})
.finally(() => {
console.log('結束')
})
複製代碼
flat()
方法會按照一個可指定的深度遞歸遍歷數組,並將全部元素與遍歷到的子數組中的元素合併爲一個新數組返回。
flatMap()
與 map()
方法和深度爲1的 flat()
幾乎相同.,不過它會首先使用映射函數映射每一個元素,而後將結果壓縮成一個新數組,這樣效率會更高。
例子以下:
var arr1 = [1, 2, 3, 4]
arr1.map(x => [x * 2]) // [[2], [4], [6], [8]]
arr1.flatMap(x => [x * 2]) // [2, 4, 6, 8]
// 深度爲1
arr1.flatMap(x => [[x * 2]]) // [[2], [4], [6], [8]]
複製代碼
flatMap()
能夠代替reduce()
與 concat()
,例子以下:
var arr = [1, 2, 3, 4]
arr.flatMap(x => [x, x * 2]) // [1, 2, 2, 4, 3, 6, 4, 8]
// 等價於
arr.reduce((acc, x) => acc.concat([x, x * 2]), []) // [1, 2, 2, 4, 3, 6, 4, 8]
複製代碼
但這是很是低效的,在每次迭代中,它建立一個必須被垃圾收集的新臨時數組,而且它將元素從當前的累加器數組複製到一個新的數組中,而不是將新的元素添加到現有的數組中。
在ES5中,咱們能夠經過trim()
來去掉字符首尾的空格,可是卻沒法只去掉單邊的,可是在ES10以後,咱們能夠實現這個功能。
若是咱們要去掉開頭的空格,可使用trimStart()
或者它的別名trimLeft()
,
一樣的,若是咱們要去掉結尾的空格,咱們可使用trimEnd()
或者它的別名trimRight()
。
例子以下:
const Str = ' Hello world! '
console.log(Str) // ' Hello world! '
console.log(Str.trimStart()) // 'Hello world! '
console.log(Str.trimLeft()) // 'Hello world! '
console.log(Str.trimEnd()) // ' Hello world!'
console.log(Str.trimRight()) // ' Hello world!'
複製代碼
不過這裏有一點要注意的是,trimStart()
跟trimEnd()
纔是標準方法,trimLeft()
跟trimRight()
只是別名。
在某些引擎裏(例如Chrome),有如下的等式:
String.prototype.trimLeft.name === "trimStart"
String.prototype.trimRight.name === "trimEnd"
複製代碼
Object.fromEntries()
方法把鍵值對列表轉換爲一個對象,它是Object.entries()
的反函數。
例子以下:
const entries = new Map([
['foo', 'bar'],
['baz', 42]
])
const obj = Object.fromEntries(entries)
console.log(obj) // Object { foo: "bar", baz: 42 }
複製代碼
description
是一個只讀屬性,它會返回Symbol
對象的可選描述的字符串。與 Symbol.prototype.toString()
不一樣的是它不會包含Symbol()
的字符串。例子以下:
Symbol('desc').toString(); // "Symbol(desc)"
Symbol('desc').description; // "desc"
Symbol('').description; // ""
Symbol().description; // undefined
// 具名 symbols
Symbol.iterator.toString(); // "Symbol(Symbol.iterator)"
Symbol.iterator.description; // "Symbol.iterator"
//全局 symbols
Symbol.for('foo').toString(); // "Symbol(foo)"
Symbol.for('foo').description; // "foo"
複製代碼
matchAll()
方法返回一個包含全部匹配正則表達式的結果及分組捕獲組的迭代器。而且返回一個不可重啓的迭代器。例子以下:
var regexp = /t(e)(st(\d?))/g
var str = 'test1test2'
str.match(regexp) // ['test1', 'test2']
str.matchAll(regexp) // RegExpStringIterator {}
[...str.matchAll(regexp)] // [['test1', 'e', 'st1', '1', index: 0, input: 'test1test2', length: 4], ['test2', 'e', 'st2', '2', index: 5, input: 'test1test2', length: 4]]
複製代碼
在以往的版本中,Function.prototype.toString()
獲得的字符串是去掉空白符號的,可是從ES10開始會保留這些空格,若是是原生函數則返回你控制檯看到的效果,例子以下:
function sum(a, b) {
return a + b;
}
console.log(sum.toString())
// "function sum(a, b) {
// return a + b;
// }"
console.log(Math.abs.toString()) // "function abs() { [native code] }"
複製代碼
在以往的版本中,try-catch
裏catch
後面必須帶異常參數,例如:
// ES10以前
try {
// tryCode
} catch (err) {
// catchCode
}
複製代碼
可是在ES10以後,這個參數卻不是必須的,若是用不到,咱們能夠不用傳,例如:
try {
console.log('Foobar')
} catch {
console.error('Bar')
}
複製代碼
BigInt 是一種內置對象,它提供了一種方法來表示大於 253 - 1
的整數。這本來是 Javascript中能夠用 Number
表示的最大數字。BigInt 能夠表示任意大的整數。
能夠用在一個整數字面量後面加 n
的方式定義一個 BigInt
,如:10n
,或者調用函數BigInt()
。
在以往的版本中,咱們有如下的弊端:
// 大於2的53次方的整數,沒法保持精度
2 ** 53 === (2 ** 53 + 1)
// 超過2的1024次方的數值,沒法表示
2 ** 1024 // Infinity
複製代碼
可是在ES10引入BigInt
以後,這個問題便獲得瞭解決。
如下操做符能夠和 BigInt
一塊兒使用: +
、*
、-
、**
、%
。除 >>>
(無符號右移)以外的位操做也能夠支持。由於 BigInt
都是有符號的, >>>
(無符號右移)不能用於 BigInt
。BigInt
不支持單目 (+
) 運算符。
/
操做符對於整數的運算也沒問題。但是由於這些變量是 BigInt
而不是 BigDecimal
,該操做符結果會向零取整,也就是說不會返回小數部分。
BigInt
和 Number
不是嚴格相等的,可是寬鬆相等的。
因此在BigInt
出來之後,JS的原始類型便增長到了7個,以下:
globalThis
屬性包含相似於全局對象 this
值。因此在全局環境下,咱們有:
globalThis === this // true
複製代碼
靜態的import
語句用於導入由另外一個模塊導出的綁定。不管是否聲明瞭 嚴格模式,導入的模塊都運行在嚴格模式下。在瀏覽器中,import
語句只能在聲明瞭 type="module"
的 script
的標籤中使用。
可是在ES10以後,咱們有動態 import()
,它不須要依賴 type="module"
的script標籤。
因此咱們有如下例子:
const main = document.querySelector("main")
for (const link of document.querySelectorAll("nav > a")) {
link.addEventListener("click", e => {
e.preventDefault()
import('/modules/my-module.js')
.then(module => {
module.loadPageInto(main);
})
.catch(err => {
main.textContent = err.message;
})
})
}
複製代碼
在ES10以前,若是咱們要實現一個簡單的計數器組件,咱們可能會這麼寫:
// web component 寫法
class Counter extends HTMLElement {
get x() {
return this.xValue
}
set x(value) {
this.xValue = value
window.requestAnimationFrame(this.render.bind(this))
}
clicked() {
this.x++
}
constructor() {
super()
this.onclick = this.clicked.bind(this)
this.xValue = 0
}
connectedCallback() {
this.render()
}
render() {
this.textContent = this.x.toString()
}
}
window.customElements.define('num-counter', Counter)
複製代碼
可是在ES10以後咱們可使用私有變量進行組件封裝,以下:
class Counter extends HTMLElement {
#xValue = 0
get #x() {
return #xValue
}
set #x(value) {
this.#xValue = value
window.requestAnimationFrame(this.#render.bind(this))
}
#clicked() {
this.#x++
}
constructor() {
super();
this.onclick = this.#clicked.bind(this)
}
connectedCallback() {
this.#render()
}
#render() {
this.textContent = this.#x.toString()
}
}
window.customElements.define('num-counter', Counter)
複製代碼
若是你喜歡探討技術,或者對本文有任何的意見或建議,很是歡迎加魚頭微信好友一塊兒探討,固然,魚頭也很是但願能跟你一塊兒聊生活,聊愛好,談天說地。 魚頭的微信號是:krisChans95 也能夠掃碼關注公衆號,訂閱更多精彩內容。