1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        ES6 你可能不知道的事 - 進(jìn)階篇

        共 9183字,需瀏覽 19分鐘

         ·

        2020-07-28 16:19


        前言

        這篇文章主要會(huì)針對(duì)上篇未涉及到的進(jìn)階特性展開;而與前一篇文章相同,本文主要介紹這些特性的一些容易忽略的部分,希望能對(duì)大家正確認(rèn)識(shí)和使用 ES6 有幫助。

        還是那句話,時(shí)間和能力有限,針對(duì)文章中的問題或不同意見,歡迎隨時(shí)拍磚、指正!

        正文

        Module

        模塊化是個(gè)進(jìn)行了很久的話題,發(fā)展歷程中出現(xiàn)過(guò)很多模式,例如 AMD, CommonJS 等等。

        Module 是 ES6 的新特性,是語(yǔ)言層面對(duì)模塊化的支持。

        與之前模塊加載機(jī)制不同,Module 是動(dòng)態(tài)的加載,導(dǎo)入的是變量的 只讀引用 ,而不是拷貝

        // 1. export default 可以做默認(rèn)導(dǎo)出// a.jsexport default 5; ? ? ?// 默認(rèn)導(dǎo)出// b.jsimport b, {a} from './a.js'; ? ?// 默認(rèn)導(dǎo)入,不需要加花括號(hào)// 2. 動(dòng)態(tài)的加載機(jī)制// a.jsexport let a = 10;
        export let b = 10;
        export function add() {
        ?a = 15;
        ?b = 20; ?return a+b;
        };// b.jsimport {a, b, add} from './a.js';
        a+b; ? ?// 20add(); ?// 35a+b; ? ?// 35

        Symbol

        symbol 是 ES6 的一個(gè)新特性,他有如下特點(diǎn):

        • symbol 是一個(gè) “新” 的 基礎(chǔ)數(shù)據(jù)類型 ;從 ES6 起,JavaScript 的 基礎(chǔ)數(shù)據(jù)類型 變?yōu)?6 個(gè):string, number, boolean, null, undefined, symbol

        • symbol 可以用作 Object 的 key

        • symbol 存在全局作用域,利用 Symbol.for(key) 方法,可以創(chuàng)建(全局作用域無(wú)指定鍵)或獲取全局作用域內(nèi)的 symbol ;利用 Symbol.keyFor(sym) 方法,可以獲取指定 symbol 的鍵

        • JavaScript 內(nèi)部使用了很多內(nèi)置 symbol ,作為特殊的鍵,來(lái)實(shí)現(xiàn)一些內(nèi)部功能;例如 Symbol.iterator 用于標(biāo)示對(duì)象的迭代器

        “新” 僅僅是針對(duì)前端開發(fā)人員來(lái)說(shuō)的,其實(shí) Symbol 概念本身已經(jīng)在 JavaScript 語(yǔ)言內(nèi)部長(zhǎng)時(shí)間使用了

        // 1. "Symbol(desc)" 方法用于創(chuàng)建一個(gè)新的 symbol,參數(shù) "desc" 僅用做 symbol 的描述,并不用于唯一標(biāo)示 symbolSymbol('abc') === Symbol('abc'); ? ?// false,'abc'僅作為兩個(gè) symbol 的描述信息// 2. "Symbol.for(key)" 方法,參數(shù) "key" 是用于在全局作用域中標(biāo)示 symbol 的唯一鍵,同時(shí)也作為該 symbol 的描述信息Symbol.for('abc') === Symbol.for('abc'); ? ?// true,左側(cè)為創(chuàng)建,右側(cè)為獲取Symbol.for('abc') === Symbol('abc'); ? ? ? ?// false// 3. symbol 無(wú)法被 for...in 遍歷到 (不可枚舉),可以利用 Object.getOwnPropertySymbols 獲取const obj = {
        ?[Symbol('abc')]: 'abc', ?'abc': 'abc'};for(var i in obj){ ?// abc
        ?console.log(i);
        }Object.getOwnPropertySymbols(obj); ? ?// [Symbol(abc)]

        Iterator + For..Of

        ES6 中除了新特性外,還有一個(gè)新的規(guī)范,那就是關(guān)于迭代的規(guī)范,他包括兩部分分別是 “可迭代規(guī)范(iterable protocol)” 和 “迭代器規(guī)范(iterator protocol)”。任何實(shí)現(xiàn)了前者的對(duì)象,都可以進(jìn)行 for…of 循環(huán)。

        String, Array, Map, Set等是原生可迭代對(duì)象,因?yàn)樗麄兌荚谠停╬rototype)對(duì)象中實(shí)現(xiàn)了 Symbol.iterator 鍵對(duì)應(yīng)的方法

        for…of 是對(duì)象迭代器的遍歷,而 for…in 是對(duì)象中 可枚舉 值的遍歷

        下面用代碼來(lái)解釋一下兩個(gè)規(guī)范:

        // 1. 迭代器規(guī)范const iter = {
        ?counter: 0,
        ?next(){ ? ?// 迭代器是實(shí)現(xiàn)了 "next()" 函數(shù)的對(duì)象
        ? ?if(++this.counter < 10){ ? ? ?return { ? ?// 返回一個(gè)含有兩個(gè)鍵值對(duì)的對(duì)象,Object {done => boolean, value => any}
        ? ? ? ?done: false,
        ? ? ? ?value: this.counter
        ? ? ?}
        ? ?}else{ ? ? ?this.counter = 0; ? ? ?return { ? ?// done = true 時(shí),value非必須
        ? ? ? ?done: true
        ? ? ?}
        ? ?}
        ?}
        };// 2. 可迭代規(guī)范,實(shí)現(xiàn) "Symbol.iterator => func()" 鍵值對(duì);而 "func()" 返回一個(gè) 迭代器對(duì)象const iterObj = {};for(var i of iterObj){}; ? ?// TypeError: iterObj is not iterableiterObj[Symbol.iterator] = function() { ?return iter;
        };for(var i of iterObj){ ?console.log(i); ? ?// 1,2,3,4,5,6,7,8,9};

        關(guān)于集合

        原來(lái)我們使用集合,多數(shù)情況下會(huì)直接用 Object 代替,ES6新增了兩個(gè)特性,MapSet,他們是對(duì) JavaScript 關(guān)于集合概念的補(bǔ)充。

        Map

        剛剛看到這個(gè)概念的同學(xué)會(huì)有幾個(gè)常見的疑問,為什么我們需要 Map 這種數(shù)據(jù)結(jié)構(gòu)?直接用 Object 不好么?是不是 Map 可以完全取代 Object 用于數(shù)據(jù)存???

        MapObject 的區(qū)別

        • MapObject 都可以存取數(shù)據(jù),Map 適用于存儲(chǔ)需要 常需要變化(增減鍵值對(duì))或遍歷 的數(shù)據(jù)集,而 Object 適用于存儲(chǔ) 靜態(tài) (例如配置信息)數(shù)據(jù)集

        • Object 的 key 必須是 StringSymbol 類型的,而 Map 無(wú)此限制,可以是任何值

        • Map 可以很方便的取到鍵值對(duì)數(shù)量,而 Object 需要用額外途徑

        // 1. Map 的構(gòu)造函數(shù)可以傳入一個(gè) “可迭代的對(duì)象(例如數(shù)組)”,其中包含鍵值對(duì)數(shù)組const first = new Map([['a', 1], [{'b': 1}, 2]]); ? ?// Map {"a" => 1, Object {b: 1} => 2}// 2. Map 的鍵可以是任何值,甚至是 undefined 或 nullfirst.set(null, 1).set(undefined, 0); ? ?// Map {"a" => 1, Object {b: 1} => 2, null => 1, undefined => 0}

        Set

        Set 作為最簡(jiǎn)單的集合,有著如下幾個(gè)特點(diǎn):

        • Set 可以存儲(chǔ)任何類型的值,遍歷順序與 插入順序相同

        • Set 內(nèi)無(wú)重復(fù)的值

        // 1. Set 的構(gòu)造函數(shù)可以傳入一個(gè) “可迭代的對(duì)象(例如數(shù)組)”,其中包含任意值const first = new Set(['a', 1, {'b': 1}, null]); ? ?// Set {"a", 1, Object {b: 1}, null}// 2. Set 無(wú)法插入重復(fù)的值first.add(1); ? ?// Set {"a", 1, Object {b: 1}, null}

        WeakMap + WeakSet

        WeakMapWeakSet 作為一個(gè)比較新穎的概念,其主要特點(diǎn)在于弱引用。

        相比于 MapSet 的強(qiáng)引用,弱引用可以令對(duì)象在 “適當(dāng)” 情況下正確被 GC 回收,減少內(nèi)存資源浪費(fèi)。

        但由于不是強(qiáng)引用,所以無(wú)法進(jìn)行遍歷或取得值數(shù)量,只能用于值的存?。╓eakMap)或是否存在值得判斷(WeakSet)

        在弱引用的情況下,GC 回收時(shí),不會(huì)把其視作一個(gè)引用;如果沒有其他強(qiáng)引用存在,那這個(gè)對(duì)象將被回收

        // 1. WeakMap 鍵必須是對(duì)象const err = new WeakMap([['a',1]]); ? ?// TypeError: Invalid value used as weak map key// 2. WeakMap/WeakSet 的弱引用const wm = new WeakMap([[{'a':1},1]]); ? ?// Object {'a': 1} 會(huì)正常被 GC 回收const ws = new WeakSet(); 
        ws.add({'a':1}); ? ?// Object {'a': 1} 會(huì)正常被 GC 回收const obj = {'b': 1};
        ws.add(obj); ? ? ? ?// Object {'b': 1} 不會(huì)被正常 GC 回收,因?yàn)榇嬖谝粋€(gè)強(qiáng)引用obj = undefined; ? ?// Object {'b': 1} 會(huì)正常被 GC 回收

        異步編程

        在 ES6 之前,JavaScript 的異步編程都跳不出回調(diào)函數(shù)這個(gè)方式?;卣{(diào)函數(shù)方式使用非常簡(jiǎn)單,在簡(jiǎn)單異步任務(wù)調(diào)用時(shí)候沒有任何問題,但如果出現(xiàn)復(fù)雜的異步任務(wù)場(chǎng)景時(shí),就顯得力不從心了,最主要的問題就是多層回調(diào)函數(shù)的嵌套會(huì)導(dǎo)致代碼的橫向發(fā)展,難以維護(hù);ES6 帶來(lái)了兩個(gè)新特性來(lái)解決異步編程的難題。

        // 一個(gè)簡(jiǎn)單的多層嵌套回調(diào)函數(shù)的例子 (Node.js)const git = require('shell').git;const commitMsg = '...';

        git.add('pattern/for/some/files/*', (err) => { ?if(!err){
        ? ?git.commit(commitMsg, (err) => { ? ? ?if(!err){
        ? ? ? ?git.push(pushOption);
        ? ? ?}else{ ? ? ? ?console.log(err);
        ? ? ?}
        ? ?});
        ?}else{ ? ?console.log(err);
        ?}
        });

        Promise

        Promise 是 ES6 的一個(gè)新特性,同為異步編程方式,它主要有如下幾個(gè)特點(diǎn):

        • 本質(zhì)還是回調(diào)函數(shù)

        • 區(qū)分成功和失敗的回調(diào),省去嵌套在內(nèi)層的判斷邏輯

        • 可以很輕松的完成回調(diào)函數(shù)模式到 Promise 模式的轉(zhuǎn)化

        • 代碼由回調(diào)函數(shù)嵌套的橫向擴(kuò)展,變?yōu)殒準(zhǔn)秸{(diào)用的縱向擴(kuò)展,易于理解和維護(hù)

        Promise 雖然優(yōu)勢(shì)頗多,但是代碼結(jié)構(gòu)仍與同步代碼區(qū)別較大

        // 上例用 Promise 實(shí)現(xiàn)// 假定 git.add, git.commit, git.push 均做了 Promise 封裝,返回一個(gè) Promise 對(duì)象const git = require('shell').git;const commitMsg = '...';

        git.add('pattern/for/some/files/*')
        ?.then(() => git.commit(commitMsg))
        ?.then(git.push)
        ?.catch((err) => { ? ?console.log(err);
        ?});

        Generator

        Generator 作為 ES6 的新特性,是一個(gè)語(yǔ)言層面的升級(jí)。它有以下幾個(gè)特點(diǎn):

        • 可以通過(guò) yield 關(guān)鍵字,終止執(zhí)行并返回(內(nèi)到外)

        • 可以通過(guò) next(val) 方法調(diào)用重新喚醒,繼續(xù)執(zhí)行(外回內(nèi))

        • 運(yùn)行時(shí)(包括掛起態(tài)),共享局部變量

        • Generator 執(zhí)行會(huì)返回一個(gè)結(jié)果對(duì)象,結(jié)果對(duì)象本身既是迭代器,同時(shí)也是可迭代對(duì)象(同時(shí)滿足兩個(gè)迭代規(guī)范),所以 Generator 可以直接用于 自定義對(duì)象迭代器

        由于具備以上特點(diǎn)(第四點(diǎn)除外),Generator 也是 JavaScript 對(duì) 協(xié)程(coroutine)的實(shí)現(xiàn),協(xié)程可以理解為 “可由開發(fā)人員控制調(diào)度的多線程”

        協(xié)程按照調(diào)度機(jī)制來(lái)區(qū)分,可以分為對(duì)稱式和非對(duì)稱式

        非對(duì)稱式:被調(diào)用者(協(xié)程)掛起時(shí),必須將控制權(quán)返還調(diào)用者(協(xié)程)

        對(duì)稱式:被調(diào)用者(協(xié)程)掛起時(shí),可將控制權(quán)轉(zhuǎn)給 “任意” 其他協(xié)程

        JavaScript 實(shí)現(xiàn)的是 非對(duì)稱式協(xié)程(semi-coroutine);非對(duì)稱式協(xié)程相比于對(duì)稱式協(xié)程,代碼邏輯更清晰,易于理解和維護(hù)

        協(xié)程給 JavaScript 提供了一個(gè)新的方式去完成異步編程;由于 Generator 的執(zhí)行會(huì)返回一個(gè)迭代器,需要手動(dòng)去遍歷,所以如果要達(dá)到自動(dòng)執(zhí)行的目的,除了本身語(yǔ)法外,還需要實(shí)現(xiàn)一個(gè)執(zhí)行器,例如 TJ 大神的 co 框架。

        // 上例用 Generator 實(shí)現(xiàn)// 假定 git.add, git.commit, git.push 均做了 Promise 封裝,返回一個(gè) Promise 對(duì)象const co = require('co');const git = require('shell').git;

        co(function* (){ ?const commitMsg = '...'; ? ? ?// 共享的局部變量

        ?yield git.add('pattern/for/some/files/*'); ?yield git.commit(commitMsg); ?yield git.push();
        }).catch((err) => { ?console.log(err);
        });

        Generator 是一個(gè) ES6 最佳的異步編程選擇么?顯然不是,因?yàn)槌嘶菊Z(yǔ)法外,我們還要額外去實(shí)現(xiàn)執(zhí)行器來(lái)達(dá)到執(zhí)行的目的,但是它整體的代碼結(jié)構(gòu)是優(yōu)于回調(diào)函數(shù)嵌套和 Promise 模式的。

        Async-Await

        這并不是一個(gè) ES6 新特性,而是 ES7 的語(yǔ)法,放在這里是因?yàn)樗鼘⑹?JavaScript 目前支持異步編程最好的方式

        // 上例用 async-await 實(shí)現(xiàn)// 假定 git.add, git.commit, git.push 均做了 Promise 封裝,返回一個(gè) Promise 對(duì)象const git = require('shell').git;

        (async function(){ ?const commitMsg = '...'; ? ? ?// 共享的局部變量

        ?try{
        ? ?await git.add('pattern/for/some/files/*');
        ? ?await git.commit(commitMsg);
        ? ?await git.push();
        ?}catch(err){ ? ?console.log(err);
        ?}
        })();

        元編程

        元編程是指的是開發(fā)人員對(duì) “語(yǔ)言本身進(jìn)行編程”。一般是編程語(yǔ)言暴露了一些 API,供開發(fā)人員來(lái)操作語(yǔ)言本身的某些特性。ES6 兩個(gè)新特性 ProxyReflect 是 JavaScript 關(guān)于對(duì)象元編程能力的擴(kuò)展。

        Proxy

        Proxy 是 ES6 加入的一個(gè)新特性,它可以 “代理” 對(duì)象的原生行為,替換為執(zhí)行自定義行為。

        這樣的元編程能力使得我們可以更輕松的擴(kuò)展出一些特殊對(duì)象。

        • 任何對(duì)象都可以被 “代理”

        • 利用 Proxy.revocable(target, handler) 可以創(chuàng)建出一個(gè)可逆的 “被代理” 對(duì)象

        // 簡(jiǎn)單 element 選擇控制工具的實(shí)現(xiàn)const cacheElement = function(target, prop) { ?if(target.hasOwnProperty(prop)){ ? ? ?return target[prop];
        ? ?}else{ ? ? ?return target[prop] = document.getElementById(prop);
        ? ?}
        }const elControl = new Proxy(cacheElement, {
        ?get: (target, prop) => { ? ?return cacheElement(target, prop);
        ?},
        ?set: (target, prop, val) => {
        ? ?cacheElement(target, prop).textContent = val;
        ?},
        ?apply: (target, thisArg, args) => { ? ?return Reflect.ownKeys(target);
        ?}
        });

        elControl.first; ? ? // div#firstelControl.second; ? ?// div#secondelControl.first = 5; ? ?// div#first => 5elControl.second = 10; ?// div#second => 10elControl(); ? ?// ['first', 'second']

        Reflect

        ES6 中引入的 Reflect 是另一個(gè)元編程的特性,它使得我們可以直接操縱對(duì)象的原生行為。Reflect 可操縱的行為與 Proxy 可代理的行為是一一對(duì)應(yīng)的,這使得可以在 Proxy 的自定義方法中方便的使用 Reflect 調(diào)起原生行為。

        // 1. Proxy 的自定義方法中,通過(guò) Reflect 調(diào)用原生行為const customProxy = new Proxy({ ?'custom': 1}, {
        ?get: (target, prop) => { ? ?console.log(`get ${prop} !`); ? ?return Reflect.get(target, undefined, prop);
        ?}
        });

        customProxy.custom; ?// get custom, 1// 2. 與 Object 對(duì)象上已經(jīng)開放的操作原生行為方法相比,語(yǔ)法更加清晰易用(例如:Object.hasOwnProperty 與 Reflect.has)const symb = Symbol('b');const a = {
        ?[symb]: 1, ?'b': 2};if(Reflect.has(a, symb) && Reflect.has(a, 'b')){ ?// good
        ?console.log('good');
        }
        Reflect.ownKeys(a); ?// ["b", Symbol(b)]

        進(jìn)階閱讀

        篇幅有限,無(wú)法面面俱到,還想再最后推薦給大家一些想進(jìn)階了解 ES6 的必看內(nèi)容

        • 如果你關(guān)注兼容性,推薦看:https://kangax.github.io/compat-table/es6/,這里介紹了從 ES5 到 ES2016+ 的所有特性(包括仍未定稿的特性)及其在各環(huán)境的兼容性

        • 如果你關(guān)注性能,推薦看:http://kpdecker.github.io/six-speed/,這里通過(guò)性能測(cè)試,將 ES6 特性的原生實(shí)現(xiàn)與 ES5 polyfill 版本進(jìn)行對(duì)比,覆蓋了各主流環(huán)境;同時(shí)也可以側(cè)面對(duì)比出各環(huán)境在原生實(shí)現(xiàn)上的性能優(yōu)劣

        • 如果你想全面了解特性,推薦看:https://developer.mozilla.org/en-US/docs/Web/JavaScript,覆蓋特性的各方面,包括全面的 API(包括不推薦和廢棄的)和基礎(chǔ)用法

        • 如果你想看特性更多的使用示例和對(duì)應(yīng)的 polyfill 實(shí)現(xiàn),推薦看:http://es6-features.org/#Constants,這里對(duì)各個(gè)特性都給出了使用豐富的例子和一個(gè) polyfill 實(shí)現(xiàn),簡(jiǎn)單明了

        • 如果想了解 ECMA Script 最多最全面的細(xì)節(jié),英語(yǔ)又比較過(guò)硬,推薦在需要時(shí)看:http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf,(或者直接看最新的:https://tc39.github.io/ecma262/)

        結(jié)語(yǔ)

        基礎(chǔ)篇+進(jìn)階篇基本介紹完了 ES6 的主要特性,但 ES6 僅僅是現(xiàn)在時(shí),后續(xù)如果大家覺得這個(gè)系列有意思,可以再寫一寫 ES 2016+ 的相關(guān)內(nèi)容,來(lái)?yè)肀б幌赂碌淖兓?/p>

        最后,希望文章中的部分內(nèi)容可以對(duì)大家理解和使用 ES6 有所幫助,感謝支持~

        參考資料

        • https://developer.mozilla.org/en-US/docs/Web/JavaScript

        • https://babeljs.io/docs/learn-es2015/

        • https://www.stackoverflow.com

        • http://es6.ruanyifeng.com/

        • https://kangax.github.io/compat-table/esnext/

        • http://www.ecma-international.org/ecma-262/6.0/

        推薦閱讀


        1、你不知道的前端異常處理(萬(wàn)字長(zhǎng)文,建議收藏)

        2、你不知道的 TypeScript 泛型(萬(wàn)字長(zhǎng)文,建議收藏)

        3、你不知道的 Web Workers (萬(wàn)字長(zhǎng)文,建議收藏)

        4、immutablejs 是如何優(yōu)化我們的代碼的?

        5、或許是一本可以徹底改變你刷 LeetCode 效率的題解書

        6、想去力扣當(dāng)前端,TypeScript 需要掌握到什么程度?

        7、距離弄懂正則的環(huán)視,你只差這一篇文章


        ?

        關(guān)注加加,星標(biāo)加加~

        ?

        如果覺得文章不錯(cuò),幫忙點(diǎn)個(gè)在看唄




        瀏覽 29
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評(píng)論
        圖片
        表情
        推薦
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            精品一区二区三区四区五区六区七区八区九区 | jizz成熟丰满韩国女人剧情 | 亚洲图片视频小说 | 亚洲三级视频在线 | 青青草在线观看免费 | 人少妇精品123在线观看 | 国产精品久久久免费视频 | 男女无遮挡啪啪 | 91免费处女 | 色伊人网 |