一個合格的中級前端工程師需要掌握的 28 個 JavaScript 技巧


一個合格的中級前端工程師需要掌握的 28 個 JavaScript 技巧


前言

文中代碼對應的詳細註釋和具體使用方法都放在我的 github 上,源代碼在底部連接

1.判斷對象的數據類型


一個合格的中級前端工程師需要掌握的 28 個 JavaScript 技巧


使用 Object.prototype.toString 配合閉包,通過傳入不同的判斷類型來返回不同的判斷函數,一行代碼,簡潔優雅靈活(注意傳入 type 參數時首字母大寫)

不推薦將這個函數用來檢測可能會產生包裝類型的基本數據類型上,因為 call 始終會將第一個參數進行裝箱操作,導致基本類型和包裝類型無法區分

2. 循環實現數組 map 方法


一個合格的中級前端工程師需要掌握的 28 個 JavaScript 技巧


使用方法:將 selfMap 注入到 Array.prototype 上(下面數組的迭代方法同理)


一個合格的中級前端工程師需要掌握的 28 個 JavaScript 技巧


值得一提的是,map 的第二個參數為第一個參數回調中的 this 指向,如果第一個參數為箭頭函數,那設置第二個 this 會因為箭頭函數的詞法綁定而失效

另外就是對稀疏數組的處理,通過 hasOwnProperty 來判斷當前下標的元素是否存在與數組中(感謝評論區的朋友)

3. 使用 reduce 實現數組 map 方法


一個合格的中級前端工程師需要掌握的 28 個 JavaScript 技巧


4. 循環實現數組 filter 方法


一個合格的中級前端工程師需要掌握的 28 個 JavaScript 技巧


5. 使用 reduce 實現數組 filter 方法


一個合格的中級前端工程師需要掌握的 28 個 JavaScript 技巧


6. 循環實現數組的 some 方法


一個合格的中級前端工程師需要掌握的 28 個 JavaScript 技巧


執行 some 方法的數組如果是一個空數組,最終始終會返回 false,而另一個數組的 every 方法中的數組如果是一個空數組,會始終返回 true

7. 循環實現數組的 reduce 方法


一個合格的中級前端工程師需要掌握的 28 個 JavaScript 技巧


因為可能存在稀疏數組的關係,所以 reduce 需要保證跳過稀疏元素,遍歷正確的元素和下標(感謝@神三元的提供的代碼)

8. 使用 reduce 實現數組的 flat 方法


一個合格的中級前端工程師需要掌握的 28 個 JavaScript 技巧


因為 selfFlat 是依賴 this 指向的,所以在 reduce 遍歷時需要指定 selfFlat 的 this 指向,否則會默認指向 window 從而發生錯誤

原理通過 reduce 遍歷數組,遇到數組的某個元素仍是數組時,通過 ES6 的擴展運算符對其進行降維(ES5 可以使用 concat 方法),而這個數組元素可能內部還嵌套數組,所以需要遞歸調用 selfFlat

同時原生的 flat 方法支持一個 depth 參數表示降維的深度,默認為 1 即給數組降一層維度


一個合格的中級前端工程師需要掌握的 28 個 JavaScript 技巧


傳入 Inifity 會將傳入的數組變成一個一維數組


一個合格的中級前端工程師需要掌握的 28 個 JavaScript 技巧


原理是每遞歸一次將 depth 參數減 1,如果 depth 參數為 0 時,直接返回原數組

9. 實現 ES6 的 class 語法


一個合格的中級前端工程師需要掌握的 28 個 JavaScript 技巧


ES6 的 class 內部是基於寄生組合式繼承,它是目前最理想的繼承方式,通過 Object.create 方法創造一個空對象,並將這個空對象繼承 Object.create 方法的參數,再讓子類(subType)的原型對象等於這個空對象,就可以實現子類實例的原型等於這個空對象,而這個空對象的原型又等於父類原型對象(superType.prototype)的繼承關係

而 Object.create 支持第二個參數,即給生成的空對象定義屬性和屬性描述符/訪問器描述符,我們可以給這個空對象定義一個 constructor 屬性更加符合默認的繼承行為,同時它是不可枚舉的內部屬性(enumerable:false)

而 ES6 的 class 允許子類繼承父類的靜態方法和靜態屬性,而普通的寄生組合式繼承只能做到實例與實例之間的繼承,對於類與類之間的繼承需要額外定義方法,這裡使用 Object.setPrototypeOf 將 superType 設置為 subType 的原型,從而能夠從父類中繼承靜態方法和靜態屬性

10. 函數柯里化


一個合格的中級前端工程師需要掌握的 28 個 JavaScript 技巧


使用方法:


一個合格的中級前端工程師需要掌握的 28 個 JavaScript 技巧


柯里化是函數式編程的一個重要技巧,將使用多個參數的一個函數轉換成一系列使用一個參數的函數的技術

函數式編程另一個重要的函數 compose,能夠將函數進行組合,而組合的函數只接受一個參數,所以如果有接受多個函數的需求並且需要用到 compose 進行函數組合,就需要使用柯里化對準備組合的函數進行部分求值,讓它始終只接受一個參數

借用冴羽博客中的一個例子


一個合格的中級前端工程師需要掌握的 28 個 JavaScript 技巧


11. 函數柯里化(支持佔位符)


一個合格的中級前端工程師需要掌握的 28 個 JavaScript 技巧


使用方法:


一個合格的中級前端工程師需要掌握的 28 個 JavaScript 技巧


一個合格的中級前端工程師需要掌握的 28 個 JavaScript 技巧


通過佔位符能讓柯里化更加靈活,實現思路是,每一輪傳入的參數先去填充上一輪的佔位符,如果當前輪參數含有佔位符,則放到內部保存的數組末尾,當前輪的元素不會去填充當前輪參數的佔位符,只會填充之前傳入的佔位符

12. 偏函數


一個合格的中級前端工程師需要掌握的 28 個 JavaScript 技巧


使用方法:


一個合格的中級前端工程師需要掌握的 28 個 JavaScript 技巧


偏函數和柯里化概念類似,個人認為它們區別在於偏函數會固定你傳入的幾個參數,再一次性接受剩下的參數,而函數柯里化會根據你傳入參數不停的返回函數,直到參數個數滿足被柯里化前函數的參數個數

Function.prototype.bind 函數就是一個偏函數的典型代表,它接受的第二個參數開始,為預先添加到綁定函數的參數列表中的參數,與 bind 不同的是,上面的這個函數同樣支持佔位符

13. 斐波那契數列及其優化


一個合格的中級前端工程師需要掌握的 28 個 JavaScript 技巧


利用函數記憶,將之前運算過的結果保存下來,對於頻繁依賴之前結果的計算能夠節省大量的時間,例如斐波那契數列,缺點就是閉包中的 obj 對象會額外佔用內存

另外使用動態規劃比前者的空間複雜度更低,也是更推薦的解法


一個合格的中級前端工程師需要掌握的 28 個 JavaScript 技巧


14. 實現函數 bind 方法


一個合格的中級前端工程師需要掌握的 28 個 JavaScript 技巧


實現函數的 bind 方法核心是利用 call 綁定 this 指向,同時考慮了一些其他情況,例如

  • bind 返回的函數被 new 調用作為構造函數時,綁定的值會失效並且改為 new 指定的對象
  • 定義了綁定後函數的 length 屬性和 name 屬性(不可枚舉屬性)
  • 綁定後函數的 prototype 需指向原函數的 prototype(真實情況中綁定後的函數是沒有 prototype 的,取而代之在綁定後的函數中有個 內部屬性 [[TargetFunction]] 保存原函數,當將綁定後函數作為構造函數時,將創建的實例的 __proto__ 指向 [[TargetFunction]] 的 prototype,這裡無法模擬內部屬性,所以直接聲明瞭一個 prototype 屬性)

15. 實現函數 call 方法


一個合格的中級前端工程師需要掌握的 28 個 JavaScript 技巧


原理就是將函數作為傳入的上下文參數(context)的屬性執行,這裡為了防止屬性衝突使用了 ES6 的 Symbol 類型

16. 簡易的 CO 模塊


一個合格的中級前端工程師需要掌握的 28 個 JavaScript 技巧


使用方法:


一個合格的中級前端工程師需要掌握的 28 個 JavaScript 技巧


run 函數接受一個生成器函數,每當 run 函數包裹的生成器函數遇到 yield 關鍵字就會停止,當 yield 後面的 promise 被解析成功後會自動調用 next 方法執行到下個 yield 關鍵字處,最終就會形成每當一個 promise 被解析成功就會解析下個 promise,當全部解析成功後打印所有解析的結果,衍變為現在用的最多的 async/await 語法

17. 函數防抖


一個合格的中級前端工程師需要掌握的 28 個 JavaScript 技巧


leading 為是否在進入時立即執行一次,原理是利用定時器,如果在規定時間內再次觸發事件會將上次的定時器清除,即不會執行函數並重新設置一個新的定時器,直到超過規定時間自動觸發定時器中的函數

同時通過閉包向外暴露了一個 cancel 函數,使得外部能直接清除內部的計數器

18. 函數節流


一個合格的中級前端工程師需要掌握的 28 個 JavaScript 技巧


和函數防抖類似,區別在於內部額外使用了時間戳作為判斷,在一段時間內沒有觸發事件才允許下次事件觸發,同時新增了 trailing 選項,表示是否在最後額外觸發一次

19. 圖片懶加載


一個合格的中級前端工程師需要掌握的 28 個 JavaScript 技巧


getBoundClientRect 的實現方式,監聽 scroll 事件(建議給監聽事件添加節流),圖片加載完會從 img 標籤組成的 DOM 列表中刪除,最後所有的圖片加載完畢後需要解綁監聽事件


一個合格的中級前端工程師需要掌握的 28 個 JavaScript 技巧

intersectionObserver 的實現方式,實例化一個 IntersectionObserver ,並使其觀察所有 img 標籤

當 img 標籤進入可視區域時會執行實例化時的回調,同時給回調傳入一個 entries 參數,保存著實例觀察的所有元素的一些狀態,比如每個元素的邊界信息,當前元素對應的 DOM 節點,當前元素進入可視區域的比率,每當一個元素進入可視區域,將真正的圖片賦值給當前 img 標籤,同時解除對其的觀察

20. new 關鍵字


一個合格的中級前端工程師需要掌握的 28 個 JavaScript 技巧


21. 實現 Object.assign


一個合格的中級前端工程師需要掌握的 28 個 JavaScript 技巧


Object.assign 的原理可以參考我另外一篇博客

22. instanceof


一個合格的中級前端工程師需要掌握的 28 個 JavaScript 技巧


原理是遞歸遍歷 right 參數的原型鏈,每次和 left 參數作比較,遍歷到原型鏈終點時則返回 false,找到則返回 true

23. 私有變量的實現


一個合格的中級前端工程師需要掌握的 28 個 JavaScript 技巧


使用 Proxy 代理所有含有 _ 開頭的變量,使其不可被外部訪問


一個合格的中級前端工程師需要掌握的 28 個 JavaScript 技巧


通過閉包的形式保存私有變量,缺點在於類的所有實例訪問的都是同一個私有變量


一個合格的中級前端工程師需要掌握的 28 個 JavaScript 技巧


另一種閉包的實現,解決了上面那種閉包的缺點,每個實例都有各自的私有變量,缺點是捨棄了 class 語法的簡潔性,將所有的特權方法(訪問私有變量的方法)都保存在構造函數中


一個合格的中級前端工程師需要掌握的 28 個 JavaScript 技巧


通過 WeakMap 和閉包,在每次實例化時保存當前實例和所有私有變量組成的對象,外部無法訪問閉包中的 WeakMap,使用 WeakMap 好處在於當沒有變量引用到某個實例時,會自動釋放這個實例保存的私有變量,減少內存溢出的問題

24. 洗牌算法

早前的 chrome 對於元素小於 10 的數組會採用插入排序,這會導致對數組進行的亂序並不是真正的亂序,即使最新的版本 chrome 採用了原地算法使得排序變成了一個穩定的算法,對於亂序的問題仍沒有解決


一個合格的中級前端工程師需要掌握的 28 個 JavaScript 技巧


一個合格的中級前端工程師需要掌握的 28 個 JavaScript 技巧


通過洗牌算法可以達到真正的亂序,洗牌算法分為原地和非原地,圖一是原地的洗牌算法,不需要聲明額外的數組從而更加節約內存佔用率,原理是依次遍歷數組的元素,將當前元素和之後的所有元素中隨機選取一個,進行交換

25. 單例模式


一個合格的中級前端工程師需要掌握的 28 個 JavaScript 技巧


通過 ES6 的 Proxy 攔截構造函數的執行方法來實現的單例模式

26. promisify


一個合格的中級前端工程師需要掌握的 28 個 JavaScript 技巧


使用方法:


一個合格的中級前端工程師需要掌握的 28 個 JavaScript 技巧


promisify 函數是將回調函數變為 promise 的輔助函數,適合 error-first 風格(nodejs)的回調函數,原理是給 error-first 風格的回調無論成功或者失敗,在執行完畢後都會執行最後一個回調函數,我們需要做的就是讓這個回調函數控制 promise 的狀態即可

這裡還用了 Proxy 代理了整個 fs 模塊,攔截 get 方法,使得不需要手動給 fs 模塊所有的方法都包裹一層 promisify 函數,更加的靈活

27. 優雅的處理 async/await


一個合格的中級前端工程師需要掌握的 28 個 JavaScript 技巧


使用方法:


一個合格的中級前端工程師需要掌握的 28 個 JavaScript 技巧


無需每次使用 async/await 都包裹一層 try/catch ,更加的優雅,這裡提供另外一個思路,如果使用了 webpack 可以編寫一個 loader,分析 AST 語法樹,遇到 await 語法,自動注入 try/catch,這樣連輔助函數都不需要使用

28. 發佈訂閱 EventEmitter


一個合格的中級前端工程師需要掌握的 28 個 JavaScript 技巧


通過 on 方法註冊事件,trigger 方法觸發事件,來達到事件之間的鬆散解耦,並且額外添加了 once 和 off 輔助函數用於註冊只觸發一次的事件以及註銷事件

29. 實現 JSON.stringify(附加)

使用 JSON.stringify 將對象轉為 JSON 字符串時,一些非法的數據類型會失真,主要表現如下

  • 如果對象含有 toJSON 方法會調用 toJSON
  • 在數組中
  • 存在 Undefined/Symbol/Function 數據類型時會變為 null
  • 存在 Infinity/NaN 也會變成 null
  • 在對象中
  • 屬性值為 Undefined/Symbol/Function 數據類型時,屬性和值都不會轉為字符串
  • 屬性值為 Infinity/NaN ,屬性值會變為 null
  • 日期數據類型的值會調用 toISOString
  • 非數組/對象/函數/日期的複雜數據類型會變成一個空對象
  • 循環引用會拋出錯誤

另外 JSON.stringify 還可以傳入第二第三個可選參數,有興趣的朋友可以深入瞭解

實現代碼較長,這裡我直接貼上對應源代碼地址 JSON.stringify

源代碼

源代碼:https://github.com/yeyan1996/practical-javascript 歡迎 star,以後有後續的技巧會第一時間添加新的內容


分享到:


相關文章: