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

来源:https://www.cnblogs.com/zml1023/archive/2019/12/19/12067765.html
-Advertisement-
Play Games

介紹ECMAScript是一種由Ecma國際(前身為歐洲電腦製造商協會)在標準ECMA-262中定義的腳本語言規範。這種語言在萬維網上應用廣泛,它往往被稱為JavaScript或JScript,但實際上後兩者是ECMA-262標準的實現和擴展。 歷史版本至發稿日為止有九個ECMA-262版本發表。 ...


  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 = data || 'data is empty'
    }

    這樣看起來也沒有問題,但是如果參數的布爾值為falsy時就會出問題,例如我們這樣調用config:

    config(0)
    config('')

    那麼結果就永遠是後面的值

    如果我們用函數參數預設值就沒有這個問題,寫法如下:

    const config = (data = '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 = '

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 想要進階,想要提升自己一個更高檔次,想要擁有更高比格的iOS開發攻城獅們,請關註 ↓↓↓ 我的簡書:https://www.jianshu.com/u/3adf2f8593b8 我的掘金:https://juejin.im/user/5dd3f99c6fb9a01ffb355246 ♥ 歡迎大家來噴 ...
  • 轉載於https://blog.csdn.net/csdn924618338/article/details/51455595 實現效果 源碼 <!DOCTYPE HTML> <html lang="en-US"> <head> <meta charset="UTF-8"> <title>商品SKU ...
  • CSS類的操作 ~~~javascript 點擊按鈕以後修改box的樣式 點擊按鈕以後刪除box的樣式 ~~~ ...
  • 01、屏幕列印2000到3000之間的所有的數。 <script type="text/javascript"> for (var i = 2000; i < 3001; i++){ document.write(i+"<br />") } </script> 02、求450到550之間所有奇數的和 ...
  • 數組 1. 插入 2. 刪除 鏈表 插入 刪除 棧 入棧 出棧 隊列 進隊 出隊 二分搜索樹 插入 刪除 前序遍歷 中序遍歷 後序遍歷 層序遍歷 數據結構可視化:https://visualgo.net/en 國外數據結構與演算法可視化:https://www.cs.usfca.edu/~galles ...
  • 最近兩天寫了下老師課上留的作業:學生選課系統。感覺自己寫的特別麻煩,思路特別不清晰,平常自己總會偷懶,一些太麻煩細節的功能就不去實現了,用簡單的功能來替代,直到自己這回寫完這個系統(但自己寫的比較low,只有後臺功能,前臺幾乎沒有),發現一些功能雖然繁瑣,但多寫幾次就不會感到麻煩,反而自己的思維會變 ...
  • scss文件里 js里更改 ...
  • 今天在開發的時候,遇到一個問題,就是我們我在寫發送廣告的功能,然後我用了一個textare文本框,這個時候,發現了一個問題。這個文本框憑空消失了。不見了,我以為是自己的那個樣式不小心把這個隱藏掉了後來發現,還真的不是。經過仔細的查找發現是一個很特殊的樣式,請看截圖: 這個樣式肯定不是我自己寫的,然後 ...
一周排行
    -Advertisement-
    Play Games
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...