阮一峰es6电子书_ES6理解进阶【大前端高薪训练营】
一:面向對象:類class
面向對象三大特性之封裝
封裝是面向對象的重要原則,它在代碼中的體現主要是以下兩點:
- 封裝整體:把對象的屬性和行為封裝為一個整體,其中內部成員可以分為靜態成員(也叫類成員)和實例成員,成員之間又可細分為屬性和方法。
- 訪問控制:外部對對象內部屬性和行為的訪問權限,簡單來分時就是私有和公有兩種權限。
以下是基本封裝示例:
面向對象三大特性之繼承
繼承是面向對象最顯著的一個特性,它在代碼中的體現主要是以下兩點:
- 子類對象具有父類對象的屬性和行為
- 子類對象可以有它自己的屬性和行為
以下是定義一個Cat類并對上述Animal類的繼承示例:
面向對象三大特性之多態
多態指允許不同的對象對同一消息做出不同響應,在Java中,實現多態有以下三個條件:
- 繼承
- 重寫
- 父類引用指向子類對象
由于JavaScript是弱類型語言,所以JavaScript實現多態,不存在父類引用指向子類對象的問題。
以下再定義一個Dog類,實現Animal實例對象、Cat實例對象和Dog實例對象對同樣的cry調用做出不同的響應示例:
二:數據類型Symbol
Symbol是一種新的原始數據類型,用來表示獨一無二的值。此外,它也是對象屬性名的第二種數據類型(另一種是字符串)。
接下來列舉幾個在日常開發中可能會用到Symbol數據類型的場景:
1):消除魔法字符串
魔術字符串指的是,在代碼之中多次出現、與代碼形成強耦合的某一個具體的字符串或者數值。風格良好的代碼,應該盡量消除魔術字符串,改由含義清晰的變量代替。 —阮一峰
如下含有魔法字符串的代碼示例:
在上述代碼中,大量出現的type1與type2字符串就是魔法字符串。我們分析這樣大量使用魔法字符串可能會出現的問題:
- 在添加邏輯時,我們每次判斷obj的類型都需要輸入該魔法字符串,這時不但沒有輸入提示需要一個一個字符輸入,而且一旦字符少輸、多輸或者輸入錯誤,都會導致代碼運行錯誤。
- 在修改邏輯時,如果type1變成了type3,那么就需要把代碼里所有的type1找到并替換成type3。
接下來使用Symbol對上述代碼改造:
2):實現對象的保護成員 / 私有成員
假設我們對一個對象需要做如下的訪問控制:
- attr1和attr2公有成員:外部可以訪問
- attr3和attr4保護成員:外部受限訪問,需要引入鍵attr3和attr4才能訪問
- attr5和attr6私有成員:外部不能訪問,僅支持當前模塊文件內部訪問
以下是沒有實現訪問控制的代碼:
接下來使用Symbol對上述代碼改造:
如上代碼就實現了對我們所需要的訪問控制,外部對不能訪問的成員是無法感知的,因為外部對這些不能訪問的成員不但不支持讀寫,甚至也不能遍歷和序列號操作。
在我們以往的日常開發中,我們基本上對對象的訪問控制都是設置為公有的,很少設置為私有,設置為保護的就更是沒見過。但少歸少,至少說明了ES6引入的Symbol能幫助我們實現類似Java中保護和私有成員的訪問控制。
3):實現類的保護成員、私有成員
如下示例,封裝一個集合Collection,它對模塊外部具有私有屬性size與私有方法logAdd:
三:數據結構Set
Set對于JavaScript而言是一種新的數據結構,相對于數組用于存儲有序、可重復的元素集合,Set用于存儲有序、不可重復的元素集合。
接下來列舉幾個在日常開發中可能會用到Set數據結構的場景:
1):數組去重、字符串去重等任何可迭代類型的去重
2):集合間操作:交集、并集、差集
下面截取阮一峰ES6對Set的說明案例:
四:數據結構Map
Map對于JavaScript而言也是一種新的數據結構,用于存儲鍵值對形式的字典 / 雙列集合。在Map對象出現之前,我們通常使用Object對象來做鍵值對的存儲,下面對比一下Map對象實現鍵值對存儲與普通對象存儲鍵值對的區別:
- 功能角度:Object對象只能使用字符串或者Symbol類型作為鍵,而Map對象可以使用任何數據類型作為鍵。Map對象使用引用類型作為鍵時,以內存地址是否一致來作為判斷兩個鍵是否相同的標準。
- 構造與讀寫角度:Object對象字面量構造并存儲鍵值對的方式比Map方便,其讀寫操作也比Map需要調用get、set方法而言性能更好(性能分析工具初步對比分析)。
- 常用Api角度:Object對象的原型為Object.protoype,而Map對象的原型為Map.prototype,兩者對常用的鍵值對操作都有相應的api可以調用,不過Map原型上定義的Api更加純粹一些。
- 序列化角度:Object對象存儲鍵值時支持序列化,而Map對象不支持。
經過上面的對比分析可以得出結論,不到必須使用引用類型作為鍵的情況下,我們都用Object對象字面量的方式來定義并存儲鍵值對會更好一些。
接下來敘述在日常開發中可能會用到Map數據結構的場景:
1):實現對象之間的一對一、一對多、多對多(橋Map方式)的關系
經驗尚淺,日常開發示例暫時沒想到,有機會補上。但是Map結構的出現告訴了我們這些JavaScript開發者,此后在JavaScript中我們也可以很簡單的實現對象之間的映射關系。
五:迭代器Iterator和for of
遍歷器(Iterator)就是這樣一種機制。它是一種接口,為各種不同的數據結構提供統一的訪問機制。而for…of循環是ES6 創造出的一種新的遍歷命令,它可以配合迭代器使用,只要實現了Iterator接口的任意對象就可以使用for…of循環遍歷。
在JavaScript常見的數據結構如Array、Set、Map、偽數組arguments等等一系列對象的原型上都有Symbol.iterator標識,并且有默認的Iterator實現。普通對象是沒有這個接口標識以及iterator的實現的,但是我們可以手動為普通對象添加這個標識以及對應的iterator實現,示例代碼如下:
上述代碼的優點是封裝者在對外界遍歷沒有影響的情況下,對數據進行了更細粒度的管理。是一種解耦合的代碼優化操作!
六:promise、generator和Async
這三者都與異步編程有關,之后會單獨拎出來寫在另一篇博客當中,在此文中就不做贅述了。
七:模板字符串和標簽函數
模板字符串就不做介紹了,標簽函數在定義時和普通函數沒什么區別。區別在調用上,標簽函數以模板字符串作為參數輸入,并且有獨特的規則完成形實參匹配。接下來看一個簡單的例子:
上述示例運行結果:
經過上述例子我們可以大概得知標簽函數的形實參匹配規則:
- 模板中字面量數組的形實參匹配:模板字符串以類似/${[^}]+}/g 的正則規則進行split 得到其內所有字面量組成的數組,而后作為實參匹配標簽函數的第一個形參literals
- 模板中所有變量的形實參匹配:模板字符串以 /${[^}]+}/g 的正則規則進行match找到所有的JS變量數組,解析得到其值后,按順序作為實參匹配標簽函數剩下的形參,上例代碼中用rest剩余參數作為形參接收所有實參。
通過上面的例子和解析,我們認識了標簽函數調用的執行規則。根據標簽函數和模板字符串的配合機制,我們很容易就想到這種機制可以實現模板引擎甚至是定義內部語言的功能。
接下來敘述在日常開發中我們可能會用到標簽函數的場景:
1):把可能作為innerHtml的string中的特殊字符轉義,使它不被解析為HTML標簽
在日常開發中,我們很可能會碰到這么一個需求:
- 一個input輸入框接收用戶的輸入
- 另一個p標簽用來展示這個用戶的輸入
先分析一下這樣做的風險:由于用戶的輸入直接作為了p標簽的內容,當用戶輸入一個<script>標簽等任意HTML標簽時,如果我們直接把它交給p標簽,那么瀏覽器就會把它當成inneHTML進行解析后執行其中的腳本或者渲染HTML,這肯定是不被期望且有風險的。所以我們在把用戶的輸入交給p標簽展示之前,應該對其中的一些特殊字符進行轉義,防止被瀏覽器解析為標簽,接下來示例中我們用標簽函數實現這個轉義過程:
2):i18n 國際化
在我們的項目中支持國際化(i18n)的邏輯本身非常簡單,只需要界面中的所有字符串變量化,而后這些變量自動根據項目的當前語音渲染出該語言下的字符串。使用函數式編程的思想來實現的基本思路如下:
- 輸入:需要翻譯的字符串鍵
- 映射關系:根據輸入獲得輸出,具體映射邏輯與當前語言與語言包有關
- 輸出:翻譯后的字符串
3):定義語言,如 jsx
jsx標簽函數,實現了將一個含有html、css、js的模板字符串解析為一個React 對象的功能。它的模板解析功能很強大,以至于我們把它稱之為一門語言。思想和原理大概如此,由于博主暫未看過jsx源碼,下文對此不再贅述。
八:內置對象Refelect
Refelect是JavaScript的一個新內置對象(非函數類型對象),與Math對象上掛載了很多用于數學處理方面的方法一樣,Refelect對象身上掛在了一套用于操作對象的方法。
下表總結列舉了Refelect對象上的13個操作對象的靜態方法的作用,以及在Reflect出現之前的實現方案:
由上面剛剛總結出的表格內容可以得知,Reflect在對象層面以及屬性層面的Api都有相應的實現,并且比單獨的Object原型更加全面。那么我們在日常開發中如何選擇呢,出于代碼的運行性能、可讀性以及統一操作思想考慮,個人是這么選擇的,,日常簡潔的屬性讀寫、函數對象調用操作不用Reflect,其它都統一使用Reflect對象操作(也就是不用操作符delete、in以及重疊的Object原型上的方法)。
九:內置對象Proxy
Proxy是JavaScript的一個新內置對象(函數類型對象),它的實例對象用于定義對象基本操作的自定義行為(如屬性查找、賦值、枚舉、函數調用等)。
在上述Reflect的介紹中,我們發現在日常開發中,我們可以也經常對對象進行對象層面和屬性層面的很多操作,既然是操作,那么我們就希望能夠具備對這些操作進行AOP處理的能力,也即實現代理操作,那么應該怎么做呢?ES5提供了存取器屬性get、set,這讓我們具備了代理一個對象的屬性讀寫操作以進行AOP處理的能力。但是這時候對于其它對對象操作行為的代理方案仍然沒有官方的實現方案。直到ES6的Proxy出現,這才讓我們具備了對這些各種類型的對象操作進行代理以進行AOP處理的能力(上述Reflect的13個靜態方法對應的對象操作全部都可以AOP處理)。
既然Object.defineProperty和Reflect都可以代理對象操作,那么我們對比一下兩者的代理原理和優缺點以備往后甄選方案:
- 代理原理:Object.defineProperty的原理是通過將數據屬性轉變為存取器屬性的方式實現的屬性讀寫代理。而Proxy方式的原理則是這個內置Proxy對象內部有一套監聽機制,在傳入handler對象作為參數構造代理對象后,一旦代理對象的操作觸發后,就會進入handler中對應注冊的處理函數 而后可以 有選擇的使用Reflect將操作轉發被代理對象上。
- 代理局限性:Object.defineProperty始終還是局限于屬性層面的讀寫代理,對于對象層面以及屬性的其它操作代理它都無法實現。鑒于此,由于數組對象push、pop等方法的存在,它對于數組元素的讀寫代理并不方便。而使用Proxy則可以很方便的監視數組操作。
- 自我代理:Object.defineProperty方式可以代理到自身(代理之后使用對象本身即可),也可以代理到別的對象身上(代理之后需要使用代理對象)。Proxy方式只能代理到Proxy實例對象上。這一點在其它說法中是Proxy對象不需要侵入對象就可以實現代理,實際上Object.defineProperty方式也可以不侵入。
接下來敘述在日常開發中我們可能會見到 / 用到Proxy代理的場景:
1):實現屬性讀寫AOP
2):實現數組操作的監視
本文結束,謝謝觀看。
如若認可,一鍵三連。
原出處:CSDN
原文鏈接:ES6理解進階【大前端高薪訓練營】_清心-CSDN博客
總結
以上是生活随笔為你收集整理的阮一峰es6电子书_ES6理解进阶【大前端高薪训练营】的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: node开启子线程_多进程 amp; N
- 下一篇: 网络安全模型_基于数据驱动的网络安全流量