主頁 > 企業開發 > 從ES6到ES10的新特性萬字大總結

從ES6到ES10的新特性萬字大總結

2020-10-07 16:26:42 企業開發

  1. 介紹
    ECMAScript是一種由Ecma國際(前身為歐洲計算機制造商協會)在標準ECMA-262中定義的腳本語言規范,這種語言在萬維網上應用廣泛,它往往被稱為JavaScript或JScript,但實際上后兩者是ECMA-262標準的實作和擴展,

    歷史版本
    至發稿日為止有九個ECMA-262版本發表,其歷史版本如下:

    1997年6月:第一版
    1998年6月:修改格式,使其與ISO/IEC16262國際標準一樣
    1999年12月:強大的正則運算式,更好的詞法作用域鏈處理,新的控制指令,例外處理,錯誤定義更加明確,資料輸出的格式化及其它改變
    2009年12月:添加嚴格模式("use strict"),修改了前面版本模糊不清的概念,增加了getters,setters,JSON以及在物件屬性上更完整的反射,
    2011年6月:ECMAScript標5.1版形式上完全一致于國際標準ISO/IEC 16262:2011,
    2015年6月:ECMAScript 2015(ES2015),第 6 版,最早被稱作是 ECMAScript 6(ES6),添加了類和模塊的語法,其他特性包括迭代器,Python風格的生成器和生成器運算式,箭頭函式,二進制資料,靜態型別陣列,集合(maps,sets 和 weak maps),promise,reflection 和 proxies,作為最早的 ECMAScript Harmony 版本,也被叫做ES6 Harmony,
    2016年6月:ECMAScript 2016(ES2016),第 7 版,多個新的概念和語言特性,
    2017年6月:ECMAScript 2017(ES2017),第 8 版,多個新的概念和語言特性,
    2018年6月:ECMAScript 2018 (ES2018),第 9 版,包含了異步回圈,生成器,新的正則運算式特性和 rest/spread 語法,
    2019年6月:ECMAScript 2019 (ES2019),第 10 版,
    發展標準
    TC39(Technical Committee 39)是一個推動JavaScript發展的委員會,它的成語來自各個主流瀏覽器的代表成語,會議實行多數決,每一項決策只有大部分人同意且沒有強烈反對才能去實作,

    TC39成員制定著ECMAScript的未來,

    每一項新特性最終要進入到ECMAScript規范里,需要經歷5個階段,這5個階段如下:

    Stage 0: Strawperson

    只要是TC39成員或者貢獻者,都可以提交想法

    Stage 1: Proposal

    這個階段確定一個正式的提案

    Stage 2: draft

    規范的第一個版本,進入此階段的提案大概率會成為標準

    Stage 3: Candidate

    進一步完善提案細則

    Stage 4: Finished

    表示已準備好將其添加到正式的ECMAScript標準中

    由于ES6以前的屬性誕生年底久遠,我們使用也比較普遍,遂不進行說明,ES6之后的語言風格跟ES5以前的差異比較大,所以單獨拎出來做個記錄,

    ES6(ES2015)
    ES6是一次重大的革新,比起過去的版本,改動比較大,本文僅對常用的API以及語法糖進行講解,

    Let 和 Const
    在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

     varletconst
    變數提升 × ×
    全域變數 × ×
    重復宣告 × ×
    重新賦值 ×
    暫時死區 ×
    塊作用域 ×
    只宣告不初始化 ×


    類(Class)
    在ES6之前,如果我們要生成一個實體物件,傳統的方法就是寫一個建構式,例子如下:

    1 function Person(name, age) {
    2 this.name = name
    3 this.age = age
    4 }
    5 Person.prototype.information = function () {
    6 return 'My name is ' + this.name + ', I am ' + this.age
    7 }

    但是在ES6之后,我們只需要寫成以下形式:

    class Person {
    constructor(name, age) {
    this.name = name
    this.age = age
    }
    information() {
    return 'My name is ' + this.name + ', I am ' + this.age
    }
    }

    箭頭函式(Arrow function)
    箭頭函式運算式的語法比函式運算式更簡潔,并且沒有自己的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)

    看,是不是簡潔了不少

    函式引數默認值(Function parameter defaults)
    在ES6之前,如果我們寫函式需要定義初始值的時候,需要這么寫:

    function config (data) {
    var data = https://www.cnblogs.com/zml1023/p/data ||'data is empty'
    }

    這樣看起來也沒有問題,但是如果引數的布林值為falsy時就會出問題,例如我們這樣呼叫config:

    config(0)
    config('')

    那么結果就永遠是后面的值

    如果我們用函式引數默認值就沒有這個問題,寫法如下:

    const config = (data = 'https://www.cnblogs.com/zml1023/p/data is empty') => {}

    模板字串(Template string)
    在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}`

    解構賦值(Destructuring assignment)
    我們通過解構賦值, 可以將屬性/值從物件/陣列中取出,賦值給其他變數,

    比如我們需要交換兩個變數的值,在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]

    是不是方便很多

    模塊化(Module)
    在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)}`)

    擴展運算子(Spread operator)
    擴展運算子可以在函式呼叫/陣列構造時, 將陣列運算式或者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


    物件屬性簡寫(Object attribute shorthand)
    在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
    Promise 是ES6提供的一種異步解決方案,比回呼函式更加清晰明了,

    Promise 翻譯過來就是承諾的意思,這個承諾會在未來有一個確切的答復,并且該承諾有三種狀態,分別是:

    等待中(pending)
    完成了 (resolved)
    拒絕了(rejected)
    這個承諾一旦從等待狀態變成為其他狀態就永遠不能更改狀態了,也就是說一旦狀態變為 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
    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型別的值,該型別具有靜態屬性和靜態方法,它的靜態屬性會暴露幾個內建的成員物件;它的靜態方法會暴露全域的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)/ 生成器(Generator)
    迭代器(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/WeakSet
    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/WeakMap
    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
    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 屬性,則代理失效,

    Regex物件的擴展
    正則新增符號
    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'
    Math物件的擴展
    二進制表示法 : 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物件的擴展
    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"]
    ES7(ES2016)
    Array.prototype.includes()
    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

    模板字串(Template string)
    自ES7起,帶標簽的模版字面量遵守以下轉義序列的規則:

    Unicode字符以"\u"開頭,例如\u00A9
    Unicode碼位用"\u{}"表示,例如\u{2F804}
    十六進制以"\x"開頭,例如\xA9
    八進制以""和數字開頭,例如\251
    這表示類似下面這種帶標簽的模版是有問題的,因為對于每一個ECMAScript語法,決議器都會去查找有效的轉義序列,但是只能得到這是一個形式錯誤的語法:

    latex`\unicode`
    // 在較老的ECMAScript版本中報錯(ES2016及更早)
    // SyntaxError: malformed Unicode character escape sequence

    ES8(ES2017)
    async/await
    雖然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 = https://www.cnblogs.com/zml1023/p/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 = https://www.cnblogs.com/zml1023/p/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 = https://www.cnblogs.com/zml1023/p/objectURL
    document.body.appendChild(image)
    })

    Object.values()
    Object.values()方法回傳一個給定物件自身的所有可列舉屬性值的陣列,值的順序與使用for...in回圈的順序相同 ( 區別在于 for-in 回圈列舉原型鏈中的屬性 ),

    代碼如下:

    const object1 = {
    a: 'somestring',
    b: 42,
    c: false
    }
    console.log(Object.values(object1)) // ["somestring", 42, false]


    Object.entries()
    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()
    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()
    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

    ShareArrayBuffer(因安全問題,暫時在Chrome跟FireFox中被禁用)
    SharedArrayBuffer 物件用來表示一個通用的,固定長度的原始二進制資料緩沖區,類似于 ArrayBuffer 物件,它們都可以用來在共享記憶體(shared memory)上創建視圖,與 ArrayBuffer 不同的是,SharedArrayBuffer 不能被分離,

    代碼如下:

    let sab = new SharedArrayBuffer(1024) // 必須實體化
    worker.postMessage(sab)

    Atomics物件
    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()
    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({
    // 在這里定義方法和屬性
    }))

    ES9(ES2018)
    for await...of
    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

    模板字串(Template string)
    ES9開始,模板字串允許嵌套支持常見轉義序列,移除對ECMAScript在帶標簽的模版字串中轉義序列的語法限制,

    不過,非法轉義序列在"cooked"當中仍然會體現出來,它們將以undefined元素的形式存在于"cooked"之中,代碼如下:

    function latex(str) {
    return { "cooked": str[0], "raw": str.raw[0] }
    }

    latex`\unicode` // { cooked: undefined, raw: "\\unicode" }

     

    正則運算式反向(lookbehind)斷言
    首先我們得先知道什么是斷言(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字符屬性匹配Unicode字符, 它允許區分字符型別,例如大寫和小寫字母,數學符號和標點符號,

    部分例子代碼如下:

    // 匹配所有數字
    const regex = /^\p{Number}+$/u;
    regex.test('231???') // 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/…

    正則運算式 s/dotAll 模式
    在以往的版本里,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() ,

    如果存在相同的屬性名,只有最后一個會生效,

    Promise.prototype.finally()
    finally()方法會回傳一個Promise,當promise的狀態變更,不管是變成rejected或者fulfilled,最終都會執行finally()的回呼,

    例子如下:

    fetch(url)
    .then((res) => {
    console.log(res)
    })
    .catch((error) => {
    console.log(error)
    })
    .finally(() => {
    console.log('結束')
    })

    ES10(ES2019)
    Array.prototype.flat() / flatMap()
    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]

    但這是非常低效的,在每次迭代中,它創建一個必須被垃圾收集的新臨時陣列,并且它將元素從當前的累加器陣列復制到一個新的陣列中,而不是將新的元素添加到現有的陣列中,

    String.prototype.trimStart() / trimLeft() / trimEnd() / trimRight()
    在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.fromEntries() 方法把鍵值對串列轉換為一個物件,它是Object.entries()的反函式,

    例子如下:

    const entries = new Map([
    ['foo', 'bar'],
    ['baz', 42]
    ])

    const obj = Object.fromEntries(entries)

    console.log(obj) // Object { foo: "bar", baz: 42 }

    Symbol.prototype.description
    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"

    String.prototype.matchAll
    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() 回傳注釋與空格
    在以往的版本中,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
    在以往的版本中,try-catch里catch后面必須帶例外引數,例如:

    // ES10之前
    try {
    // tryCode
    } catch (err) {
    // catchCode
    }

    但是在ES10之后,這個引數卻不是必須的,如果用不到,我們可以不用傳,例如:

    try {
    console.log('Foobar')
    } catch {
    console.error('Bar')
    }

    BigInt
    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個,如下:

    Boolean
    Null
    Undefined
    Number
    String
    Symbol (ES6)
    BigInt (ES10)
    globalThis
    globalThis屬性包含類似于全域物件 this值,所以在全域環境下,我們有:

    globalThis === this // true

    import()
    靜態的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 = https://www.cnblogs.com/zml1023/p/value
    window.requestAnimationFrame(this.render.bind(this))
    }

    clicked() {
    this.x++
    }

    constructor() {
    super()
    this.onclick = this.clicked.bind(this)
    this.xValue = https://www.cnblogs.com/zml1023/p/0
    }

    connectedCallback() {
    this.render()
    }

    render() {
    this.textContent = this.x.toString()
    }
    }
    window.customElements.define('num-counter', Counter)

    但是在ES10之后我們可以使用私有變數進行組件封裝,如下:

    class Counter extends HTMLElement {
    #xValue = https://www.cnblogs.com/zml1023/p/0

    get #x() {
    return #xValue
    }
    set #x(value) {
    this.#xValue = https://www.cnblogs.com/zml1023/p/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)

    參考資料
    ECMAScript 6 入門
    1.5萬字概括ES6全部特性
    MDN
    ES2018 新特征之:非轉義序列的模板字串
    正則運算式反向(lookbehind)斷言
    Unicode property escapes
    exnext提案
    ES7、ES8、ES9、ES10新特性大盤點
    Ecma TC39
    [ECMAScript] TC39 process
    The TC39 Process
    介紹
    ECMAScript是一種由Ecma國際(前身為歐洲計算機制造商協會)在標準ECMA-262中定義的腳本語言規范,這種語言在萬維網上應用廣泛,它往往被稱為JavaScript或JScript,但實際上后兩者是ECMA-262標準的實作和擴展,

    歷史版本
    至發稿日為止有九個ECMA-262版本發表,其歷史版本如下:

    1997年6月:第一版
    1998年6月:修改格式,使其與ISO/IEC16262國際標準一樣
    1999年12月:強大的正則運算式,更好的詞法作用域鏈處理,新的控制指令,例外處理,錯誤定義更加明確,資料輸出的格式化及其它改變
    2009年12月:添加嚴格模式("use strict"),修改了前面版本模糊不清的概念,增加了getters,setters,JSON以及在物件屬性上更完整的反射,
    2011年6月:ECMAScript標5.1版形式上完全一致于國際標準ISO/IEC 16262:2011,
    2015年6月:ECMAScript 2015(ES2015),第 6 版,最早被稱作是 ECMAScript 6(ES6),添加了類和模塊的語法,其他特性包括迭代器,Python風格的生成器和生成器運算式,箭頭函式,二進制資料,靜態型別陣列,集合(maps,sets 和 weak maps),promise,reflection 和 proxies,作為最早的 ECMAScript Harmony 版本,也被叫做ES6 Harmony,
    2016年6月:ECMAScript 2016(ES2016),第 7 版,多個新的概念和語言特性,
    2017年6月:ECMAScript 2017(ES2017),第 8 版,多個新的概念和語言特性,
    2018年6月:ECMAScript 2018 (ES2018),第 9 版,包含了異步回圈,生成器,新的正則運算式特性和 rest/spread 語法,
    2019年6月:ECMAScript 2019 (ES2019),第 10 版,
    發展標準
    TC39(Technical Committee 39)是一個推動JavaScript發展的委員會,它的成語來自各個主流瀏覽器的代表成語,會議實行多數決,每一項決策只有大部分人同意且沒有強烈反對才能去實作,

    TC39成員制定著ECMAScript的未來,

    每一項新特性最終要進入到ECMAScript規范里,需要經歷5個階段,這5個階段如下:

    Stage 0: Strawperson

    只要是TC39成員或者貢獻者,都可以提交想法

    Stage 1: Proposal

    這個階段確定一個正式的提案

    Stage 2: draft

    規范的第一個版本,進入此階段的提案大概率會成為標準

    Stage 3: Candidate

    進一步完善提案細則

    Stage 4: Finished

    表示已準備好將其添加到正式的ECMAScript標準中

    由于ES6以前的屬性誕生年底久遠,我們使用也比較普遍,遂不進行說明,ES6之后的語言風格跟ES5以前的差異比較大,所以單獨拎出來做個記錄,

    ES6(ES2015)
    ES6是一次重大的革新,比起過去的版本,改動比較大,本文僅對常用的API以及語法糖進行講解,

    Let 和 Const
    在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
    變數提升 √ × ×
    全域變數 √ × ×
    重復宣告 √ × ×
    重新賦值 √ √ ×
    暫時死區 × √ √
    塊作用域 × √ √
    只宣告不初始化 √ √ ×
    類(Class)
    在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
    }
    }

    箭頭函式(Arrow function)
    箭頭函式運算式的語法比函式運算式更簡潔,并且沒有自己的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)

    看,是不是簡潔了不少

    函式引數默認值(Function parameter defaults)
    在ES6之前,如果我們寫函式需要定義初始值的時候,需要這么寫:

    function config (data) {
    var data = https://www.cnblogs.com/zml1023/p/data ||'data is empty'
    }

    這樣看起來也沒有問題,但是如果引數的布林值為falsy時就會出問題,例如我們這樣呼叫config:

    config(0)
    config('')

    那么結果就永遠是后面的值

    如果我們用函式引數默認值就沒有這個問題,寫法如下:

    const config = (data = 'https://www.cnblogs.com/zml1023/p/data is empty') => {}

    模板字串(Template string)
    在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}`

    解構賦值(Destructuring assignment)
    我們通過解構賦值, 可以將屬性/值從物件/陣列中取出,賦值給其他變數,

    比如我們需要交換兩個變數的值,在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]

    是不是方便很多

    模塊化(Module)
    在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)}`)

    擴展運算子(Spread operator)
    擴展運算子可以在函式呼叫/陣列構造時, 將陣列運算式或者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

    物件屬性簡寫(Object attribute shorthand)
    在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
    Promise 是ES6提供的一種異步解決方案,比回呼函式更加清晰明了,

    Promise 翻譯過來就是承諾的意思,這個承諾會在未來有一個確切的答復,并且該承諾有三種狀態,分別是:

    等待中(pending)
    完成了 (resolved)
    拒絕了(rejected)
    這個承諾一旦從等待狀態變成為其他狀態就永遠不能更改狀態了,也就是說一旦狀態變為 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
    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型別的值,該型別具有靜態屬性和靜態方法,它的靜態屬性會暴露幾個內建的成員物件;它的靜態方法會暴露全域的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)/ 生成器(Generator)
    迭代器(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/WeakSet
    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/WeakMap
    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
    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 屬性,則代理失效,

    Regex物件的擴展
    正則新增符號
    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'

    Math物件的擴展
    二進制表示法 : 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物件的擴展
    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"]

    ES7(ES2016)
    Array.prototype.includes()
    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

    模板字串(Template string)
    自ES7起,帶標簽的模版字面量遵守以下轉義序列的規則:

    Unicode字符以"\u"開頭,例如\u00A9
    Unicode碼位用"\u{}"表示,例如\u{2F804}
    十六進制以"\x"開頭,例如\xA9
    八進制以""和數字開頭,例如\251
    這表示類似下面這種帶標簽的模版是有問題的,因為對于每一個ECMAScript語法,決議器都會去查找有效的轉義序列,但是只能得到這是一個形式錯誤的語法:

    latex`\unicode`
    // 在較老的ECMAScript版本中報錯(ES2016及更早)
    // SyntaxError: malformed Unicode character escape sequence

    ES8(ES2017)
    async/await
    雖然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 = https://www.cnblogs.com/zml1023/p/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 = https://www.cnblogs.com/zml1023/p/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 = https://www.cnblogs.com/zml1023/p/objectURL
    document.body.appendChild(image)
    })

    Object.values()
    Object.values()方法回傳一個給定物件自身的所有可列舉屬性值的陣列,值的順序與使用for...in回圈的順序相同 ( 區別在于 for-in 回圈列舉原型鏈中的屬性 ),

    代碼如下:

    const object1 = {
    a: 'somestring',
    b: 42,
    c: false
    }
    console.log(Object.values(object1)) // ["somestring", 42, false]


    Object.entries()
    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()
    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()
    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

    ShareArrayBuffer(因安全問題,暫時在Chrome跟FireFox中被禁用)
    SharedArrayBuffer 物件用來表示一個通用的,固定長度的原始二進制資料緩沖區,類似于 ArrayBuffer 物件,它們都可以用來在共享記憶體(shared memory)上創建視圖,與 ArrayBuffer 不同的是,SharedArrayBuffer 不能被分離,

    代碼如下:

    let sab = new SharedArrayBuffer(1024) // 必須實體化
    worker.postMessage(sab)

    Atomics物件
    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()
    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({
    // 在這里定義方法和屬性
    }))

    ES9(ES2018)
    for await...of
    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

    模板字串(Template string)
    ES9開始,模板字串允許嵌套支持常見轉義序列,移除對ECMAScript在帶標簽的模版字串中轉義序列的語法限制,

    不過,非法轉義序列在"cooked"當中仍然會體現出來,它們將以undefined元素的形式存在于"cooked"之中,代碼如下:

    function latex(str) {
    return { "cooked": str[0], "raw": str.raw[0] }
    }

    latex`\unicode` // { cooked: undefined, raw: "\\unicode" }

     

    正則運算式反向(lookbehind)斷言
    首先我們得先知道什么是斷言(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字符屬性匹配Unicode字符, 它允許區分字符型別,例如大寫和小寫字母,數學符號和標點符號,

    部分例子代碼如下:

    // 匹配所有數字
    const regex = /^\p{Number}+$/u;
    regex.test('231???') // 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/…

    正則運算式 s/dotAll 模式
    在以往的版本里,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() ,

    如果存在相同的屬性名,只有最后一個會生效,

    Promise.prototype.finally()
    finally()方法會回傳一個Promise,當promise的狀態變更,不管是變成rejected或者fulfilled,最終都會執行finally()的回呼,

    例子如下:

    fetch(url)
    .then((res) => {
    console.log(res)
    })
    .catch((error) => {
    console.log(error)
    })
    .finally(() => {
    console.log('結束')
    })

    ES10(ES2019)
    Array.prototype.flat() / flatMap()
    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]

    但這是非常低效的,在每次迭代中,它創建一個必須被垃圾收集的新臨時陣列,并且它將元素從當前的累加器陣列復制到一個新的陣列中,而不是將新的元素添加到現有的陣列中,

    String.prototype.trimStart() / trimLeft() / trimEnd() / trimRight()
    在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.fromEntries() 方法把鍵值對串列轉換為一個物件,它是Object.entries()的反函式,

    例子如下:

    const entries = new Map([
    ['foo', 'bar'],
    ['baz', 42]
    ])

    const obj = Object.fromEntries(entries)

    console.log(obj) // Object { foo: "bar", baz: 42 }

    Symbol.prototype.description
    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"

    String.prototype.matchAll
    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() 回傳注釋與空格
    在以往的版本中,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
    在以往的版本中,try-catch里catch后面必須帶例外引數,例如:

    // ES10之前
    try {
    // tryCode
    } catch (err) {
    // catchCode
    }

    但是在ES10之后,這個引數卻不是必須的,如果用不到,我們可以不用傳,例如:

    try {
    console.log('Foobar')
    } catch {
    console.error('Bar')
    }

    BigInt
    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個,如下:

    Boolean
    Null
    Undefined
    Number
    String
    Symbol (ES6)
    BigInt (ES10)
    globalThis
    globalThis屬性包含類似于全域物件 this值,所以在全域環境下,我們有:

    globalThis === this // true

    import()
    靜態的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 = https://www.cnblogs.com/zml1023/p/value
    window.requestAnimationFrame(this.render.bind(this))
    }

    clicked() {
    this.x++
    }

    constructor() {
    super()
    this.onclick = this.clicked.bind(this)
    this.xValue = https://www.cnblogs.com/zml1023/p/0
    }

    connectedCallback() {
    this.render()
    }

    render() {
    this.textContent = this.x.toString()
    }
    }
    window.customElements.define('num-counter', Counter)

    但是在ES10之后我們可以使用私有變數進行組件封裝,如下:

    class Counter extends HTMLElement {
    #xValue = https://www.cnblogs.com/zml1023/p/0

    get #x() {
    return #xValue
    }
    set #x(value) {
    this.#xValue = https://www.cnblogs.com/zml1023/p/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)

    參考資料

    • ECMAScript 6 入門
    • 1.5萬字概括ES6全部特性
    • MDN
    • ES2018 新特征之:非轉義序列的模板字串
    • 正則運算式反向(lookbehind)斷言
    • Unicode property escapes
    • exnext提案
    • ES7、ES8、ES9、ES10新特性大盤點
    • Ecma TC39
    • [ECMAScript] TC39 process
    • The TC39 Process

轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/161556.html

標籤:JavaScript

上一篇:關于Javascript

下一篇:資料結構影片演示

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • IEEE1588PTP在數字化變電站時鐘同步方面的應用

    IEEE1588ptp在數字化變電站時鐘同步方面的應用 京準電子科技官微——ahjzsz 一、電力系統時間同步基本概況 隨著對IEC 61850標準研究的不斷深入,國內外學者提出基于IEC61850通信標準體系建設數字化變電站的發展思路。數字化變電站與常規變電站的顯著區別在于程序層傳統的電流/電壓互 ......

    uj5u.com 2020-09-10 03:51:52 more
  • HTTP request smuggling CL.TE

    CL.TE 簡介 前端通過Content-Length處理請求,通過反向代理或者負載均衡將請求轉發到后端,后端Transfer-Encoding優先級較高,以TE處理請求造成安全問題。 檢測 發送如下資料包 POST / HTTP/1.1 Host: ac391f7e1e9af821806e890 ......

    uj5u.com 2020-09-10 03:52:11 more
  • 網路滲透資料大全單——漏洞庫篇

    網路滲透資料大全單——漏洞庫篇漏洞庫 NVD ——美國國家漏洞庫 →http://nvd.nist.gov/。 CERT ——美國國家應急回應中心 →https://www.us-cert.gov/ OSVDB ——開源漏洞庫 →http://osvdb.org Bugtraq ——賽門鐵克 →ht ......

    uj5u.com 2020-09-10 03:52:15 more
  • 京準講述NTP時鐘服務器應用及原理

    京準講述NTP時鐘服務器應用及原理京準講述NTP時鐘服務器應用及原理 安徽京準電子科技官微——ahjzsz 北斗授時原理 授時是指接識訓通過某種方式獲得本地時間與北斗標準時間的鐘差,然后調整本地時鐘使時差控制在一定的精度范圍內。 衛星導航系統通常由三部分組成:導航授時衛星、地面檢測校正維護系統和用戶 ......

    uj5u.com 2020-09-10 03:52:25 more
  • 利用北斗衛星系統設計NTP網路時間服務器

    利用北斗衛星系統設計NTP網路時間服務器 利用北斗衛星系統設計NTP網路時間服務器 安徽京準電子科技官微——ahjzsz 概述 NTP網路時間服務器是一款支持NTP和SNTP網路時間同步協議,高精度、大容量、高品質的高科技時鐘產品。 NTP網路時間服務器設備采用冗余架構設計,高精度時鐘直接來源于北斗 ......

    uj5u.com 2020-09-10 03:52:35 more
  • 詳細解讀電力系統各種對時方式

    詳細解讀電力系統各種對時方式 詳細解讀電力系統各種對時方式 安徽京準電子科技官微——ahjzsz,更多資料請添加VX 衛星同步時鐘是我京準公司開發研制的應用衛星授時時技術的標準時間顯示和發送的裝置,該裝置以M國全球定位系統(GLOBAL POSITIONING SYSTEM,縮寫為GPS)或者我國北 ......

    uj5u.com 2020-09-10 03:52:45 more
  • 如何保證外包團隊接入企業內網安全

    不管企業規模的大小,只要企業想省錢,那么企業的某些服務就一定會采用外包的形式,然而看似美好又經濟的策略,其實也有不好的一面。下面我通過安全的角度來聊聊使用外包團的安全隱患問題。 先看看什么服務會使用外包的,最常見的就是話務/客服這種需要大量重復性、無技術性的服務,或者是一些銷售外包、特殊的職能外包等 ......

    uj5u.com 2020-09-10 03:52:57 more
  • PHP漏洞之【整型數字型SQL注入】

    0x01 什么是SQL注入 SQL是一種注入攻擊,通過前端帶入后端資料庫進行惡意的SQL陳述句查詢。 0x02 SQL整型注入原理 SQL注入一般發生在動態網站URL地址里,當然也會發生在其它地發,如登錄框等等也會存在注入,只要是和資料庫打交道的地方都有可能存在。 如這里http://192.168. ......

    uj5u.com 2020-09-10 03:55:40 more
  • [GXYCTF2019]禁止套娃

    git泄露獲取原始碼 使用GET傳參,引數為exp 經過三層過濾執行 第一層過濾偽協議,第二層過濾帶引數的函式,第三層過濾一些函式 preg_replace('/[a-z,_]+\((?R)?\)/', NULL, $_GET['exp'] (?R)參考當前正則運算式,相當于匹配函式里的引數 因此傳遞 ......

    uj5u.com 2020-09-10 03:56:07 more
  • 等保2.0實施流程

    流程 結論 ......

    uj5u.com 2020-09-10 03:56:16 more
最新发布
  • 使用Django Rest framework搭建Blog

    在前面的Blog例子中我們使用的是GraphQL, 雖然GraphQL的使用處于上升趨勢,但是Rest API還是使用的更廣泛一些. 所以還是決定回到傳統的rest api framework上來, Django rest framework的官網上給了一個很好用的QuickStart, 我參考Qu ......

    uj5u.com 2023-04-20 08:17:54 more
  • 記錄-new Date() 我忍你很久了!

    這里給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 大家平時在開發的時候有沒被new Date()折磨過?就是它的諸多怪異的設定讓你每每用的時候,都可能不小心踩坑。造成程式意外出錯,卻一下子找不到問題出處,那叫一個煩透了…… 下面,我就列舉它的“四宗罪”及應用思考 可惡的四宗罪 1. Sa ......

    uj5u.com 2023-04-20 08:17:47 more
  • 使用Vue.js實作文字跑馬燈效果

    實作文字跑馬燈效果,首先用到 substring()截取 和 setInterval計時器 clearInterval()清除計時器 效果如下: 實作代碼如下: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta ......

    uj5u.com 2023-04-20 08:12:31 more
  • JavaScript 運算子

    JavaScript 運算子/運算子 在 JavaScript 中,有一些運算子可以使代碼更簡潔、易讀和高效。以下是一些常見的運算子: 1、可選鏈運算子(optional chaining operator) ?.是可選鏈運算子(optional chaining operator)。?. 可選鏈操 ......

    uj5u.com 2023-04-20 08:02:25 more
  • CSS—相對單位rem

    一、概述 rem是一個相對長度單位,它的單位長度取決于根標簽html的字體尺寸。rem即root em的意思,中文翻譯為根em。瀏覽器的文本尺寸一般默認為16px,即默認情況下: 1rem = 16px rem布局原理:根據CSS媒體查詢功能,更改根標簽的字體尺寸,實作rem單位隨螢屏尺寸的變化,如 ......

    uj5u.com 2023-04-20 08:02:21 more
  • 我的第一個NPM包:panghu-planebattle-esm(胖虎飛機大戰)使用說明

    好家伙,我的包終于開發完啦 歡迎使用胖虎的飛機大戰包!! 為你的主頁添加色彩 這是一個有趣的網頁小游戲包,使用canvas和js開發 使用ES6模塊化開發 效果圖如下: (覺得圖片太sb的可以自己改) 代碼已開源!! Git: https://gitee.com/tang-and-han-dynas ......

    uj5u.com 2023-04-20 08:01:50 more
  • 如何在 vue3 中使用 jsx/tsx?

    我們都知道,通常情況下我們使用 vue 大多都是用的 SFC(Signle File Component)單檔案組件模式,即一個組件就是一個檔案,但其實 Vue 也是支持使用 JSX 來撰寫組件的。這里不討論 SFC 和 JSX 的好壞,這個仁者見仁智者見智。本篇文章旨在帶領大家快速了解和使用 Vu ......

    uj5u.com 2023-04-20 08:01:37 more
  • 【Vue2.x原始碼系列06】計算屬性computed原理

    本章目標:計算屬性是如何實作的?計算屬性快取原理以及洋蔥模型的應用?在初始化Vue實體時,我們會給每個計算屬性都創建一個對應watcher,我們稱之為計算屬性watcher ......

    uj5u.com 2023-04-20 08:01:31 more
  • http1.1與http2.0

    一、http是什么 通俗來講,http就是計算機通過網路進行通信的規則,是一個基于請求與回應,無狀態的,應用層協議。常用于TCP/IP協議傳輸資料。目前任何終端之間任何一種通信方式都必須按Http協議進行,否則無法連接。tcp(三次握手,四次揮手)。 請求與回應:客戶端請求、服務端回應資料。 無狀態 ......

    uj5u.com 2023-04-20 08:01:10 more
  • http1.1與http2.0

    一、http是什么 通俗來講,http就是計算機通過網路進行通信的規則,是一個基于請求與回應,無狀態的,應用層協議。常用于TCP/IP協議傳輸資料。目前任何終端之間任何一種通信方式都必須按Http協議進行,否則無法連接。tcp(三次握手,四次揮手)。 請求與回應:客戶端請求、服務端回應資料。 無狀態 ......

    uj5u.com 2023-04-20 08:00:32 more