每週分享第 2 期

阮一峰發表於2018-04-28

這裡分享過去一週高階前端進階群裡的學習彙總,每天會在群裡給大家發一些資料一起學習,晚上會上傳我的筆記,內容以前端基礎、原始碼分析、面試題解為主,如果你上班沒時間看,那週末可以好好學習了,彎道超車重在堅持。

本週學習彙總

11月9號

  • HTML5 History 模式

    • vue-router 預設使用hash 模式來模擬一個完整的 URL,當 URL 改變時,頁面不會重新載入。

    • 用路由的 history 模式替換hash模式,利用 history.pushState API 來完成 URL 跳轉而無須重新載入頁面。

      const router = new VueRouter({
        mode: 'history',
        routes: [...]
      })
      複製程式碼
    • 後臺需要對路由進行配置

  • 理解 Javascript 執行上下文和執行棧(優)

    • 執行上下文總共有三種型別:全域性執行上下文函式執行上下文(只有在函式被呼叫的時候才會被建立)、Eval 函式執行上下文

    • LIFO原則,引擎首次讀取指令碼時,會建立一個全域性執行上下文並將其推入當前的執行棧。每當發生一個函式呼叫,引擎都會為該函式建立一個新的執行上下文並將其推到當前執行棧的頂端。函式執行完成後,其對應的執行上下文將會從執行棧中彈出,上下文控制權將移到當前執行棧的下一個執行上下文。

    • 執行上下文分兩個階段建立:1)建立階段; 2)執行階段

    • 建立階段發生三件事:1)確定 this 的值,也被稱為 This Binding2)LexicalEnvironment(詞法環境) 元件被建立。3)VariableEnvironment(變數環境) 元件被建立。

    • 環境記錄 還包含了一個 arguments 物件,該物件包含了索引和傳遞給函式的引數之間的對映以及傳遞給函式的引數的長度(數量)

      function foo(a, b) {  
        var c = a + b;  
      }  
      foo(2, 3);
      
      // arguments 物件  
      Arguments: {0: 2, 1: 3, length: 2},
      複製程式碼
    • 在建立階段,函式宣告儲存在環境中,而變數會被設定為 undefined(在 var 的情況下)或保持未初始化(在 letconst 的情況下)。所以這就是為什麼可以在宣告之前訪問 var 定義的變數(儘管是 undefined ),但如果在宣告之前訪問 letconst 定義的變數就會提示引用錯誤的原因。這就是所謂的變數提升。

    • 如果 Javascript 引擎在原始碼中宣告的實際位置找不到 let 變數的值,那麼將為其分配 undefined 值。

  • JavaScript萬物產生順序(優)

    • 對於 Object 來說,它是一個 Function的例項,因為 Object instanceof Function // true;對於 Function 來說,它是 Object 的例項,因為 Function instanceof Object // true,所以到底是先有 Object 還是先有 Function呢?

    • 一種理解:Object基於 null 為模板(__proto__),所以Object.prototype.__proto__ === null

    • new Object 建立一個物件,基於Object.prototype為模板,所以new Object({}).__proto__ === Object.prototype

      // Object
      new Object().__proto__ === Object.prototype;
      Object.__proto__ === Function.prototype;
      Object.prototype.__proto__ === nulll;
      
      // Function
      Function.prototype === Function.__proto__;
      Function.prototype.__proto__ === Object.prototype;
      
      // Foo
      new Foo().__proto__ === Foo.prototype;
      Foo.prototype.__proto__ === Object.prototype;
      Foo.__proto__ === Function.prototype
      複製程式碼

      image

11月8號

  • ES6 系列之 Babel 是如何編譯 Class 的(下)(優)

    • 寄生組合式繼承優點在於只呼叫一次 Parent 建構函式,避免了在 Parent.prototype 上面建立不必要的、多餘的屬性,同時原型鏈還能保持不變。

    • ES6的super 關鍵字表示父類的建構函式,相當於 ES5 的 Parent.call(this)。所以子類只有呼叫 super 之後,才可以使用 this 關鍵字

    • 子類的 __proto__ 屬性,表示建構函式的繼承,總是指向父類。

    • 子類 prototype 屬性的__proto__屬性,表示方法的繼承,總是指向父類的 prototype 屬性。

    • 相比寄生組合式繼承,ES6 的 class 多了一個 Object.setPrototypeOf(Child, Parent) 的步驟。

    • 語法糖其實用的就是Object.create(),第一個引數是新建立的物件,第二個參數列示要新增到新建立物件的屬性,注意這裡是給新建立的物件即返回值新增屬性,而不是在新建立物件的原型物件上新增。

    • 首先執行 _inherits(Child, Parent),建立 Child 和 Parent 的原型鏈關係,即Object.setPrototypeOf(Child.prototype, Parent.prototype)Object.setPrototypeOf(Child, Parent)。然後呼叫 Parent.call(this, name),根據 Parent 建構函式的返回值型別確定子類建構函式 this 的初始值 _this。最終,根據子類建構函式,修改 _this 的值,然後返回該值。

  • 構建Promise佇列實現非同步函式順序執行

    • 場景:有a、b、c三個非同步任務,要求必須先執行a,再執行b,最後執行c,且下一次任務必須要拿到上一次任務執行的結果,才能做操作。

    • 解決方法一:使用then鏈式操作

      //鏈式呼叫
      a()
        .then(function (data) {
          return b(data)
        })
        .then(function (data) {
          return c(data)
        })
        .then(function (data) {
          console.log(data) // abc
        })
      複製程式碼
    • 解決方法二:構建佇列

      // 構建佇列
      function queue(arr) {
        let sequence = Promise.resolve()
        arr.forEach(function (item) {
          sequence = sequence.then(item)
        })
        return sequence
      }
      
      // 執行佇列
      queue([a, b, c])
        .then(data => {
          console.log(data) // abc
        })
      複製程式碼
    • 解決方法三:使用async、await構建佇列

      async function queue(arr) {
          let res = null
          for (let promise of arr) {
              res = await promise(res)
          }
          return await res
      }
      
      queue([a, b, c])
          .then(data => {
          	console.log(data) // abc
      	})
      複製程式碼
  • URL中的#

    • javascript 可以通過 window.location.hash來讀取或改變 #

    • location.href += '#caper'; 瀏覽器滾動到新的位置,但頁面不會重新整理,改變了瀏覽器記錄,可以通過瀏覽器上一頁按鈕回到原始的位置。

    • HTML 5新增onhashchange 事件,對於不支援onhashchange的瀏覽器,可以用setInterval監控location.hash的變化。

      // 使用方法有三種
      window.onhashchange = func;
      
      <body onhashchange="func();">
      
      window.addEventListener("hashchange", func, false);
      複製程式碼

11月7號

  • React事件系統和原始碼淺析(優)

    • 1、React為了效能複用,採用了事件代理,池,批量更新,跨瀏覽器和跨平臺相容等思想,將事件監聽掛載在document上構造合成事件,在內部模擬了一套捕獲冒泡並觸發回撥函式的機制
    • 2、執行順序:document原生事件 --> 合成事件(冒泡 innerClick --> outerClick)--> window
    • 3、如果是由React引發的事件處理(比如通過onClick引發的事件處理),呼叫setState會非同步更新state。除此之外的setState呼叫會同步執行state,包括通過addEventListener直接新增的事件處理函式,還有通過promise/setTimeout/setInterval產生的非同步呼叫。
    • 4、React合成的SyntheticEvent採用了的思想,從而達到節約記憶體,避免頻繁的建立和銷燬事件物件的目的。
    • 5、React原始碼中隨處可見batch做批量更新,基本上凡是可以批量處理的事情(最普遍的setState)React都會將中間過程儲存起來,留到最後面才flush掉。
    • 6、非同步渲染的情況下假如我點了兩次按鈕,那麼第二次按鈕響應的時候,可能第一次按鈕的handlerA中呼叫的setState還未最終被commit到DOM樹上,這時需要把第一次按鈕的結果先給flush掉並commit到DOM樹,才能夠保持一致性。
  • Vue 生命週期梳理

    • 1、beforeCreate中拿不到任何資料,在vue例項化之後
    • 2、created中已經可以拿到data中的資料了,但是dom還沒有掛載,適合ajax請求和頁面初始化
    • 3、beforeMount 頁面掛載之前
    • 4、mounted 頁面掛載成功,vue例項物件中有template引數選項,則將其作為模板編譯成render函式,編譯優先順序render函式選項 > template選項
    • 5、beforeUpdate 頁面更新之前
    • 6、updated 頁面已更新成功
    • 7、destroyed 例項化銷燬,用於解除繫結
    • 8、處理 HTML 標記並構建 DOM 樹,處理 CSS 標記並構建 CSSOM 樹,將 DOM 與 CSSOM 合併成一個渲染樹。根據渲染樹來佈局,以計算每個節點的幾何資訊,將各個節點繪製到螢幕上。
  • JavaScript 複雜判斷的更優雅寫法(優)

    • 1、一個物件的鍵只能是字串或者Symbols,但一個Map的鍵可以是任意值。、
    • 2、通過size屬性很容易地得到一個Map的鍵值對個數,而物件的鍵值對個數只能手動確認。
    • 3、actions是Map物件時,[...actions]可以轉換成陣列
    • 4、在Map中可以用正則型別作為key,這樣就有了無限可能

11月6號

  • ES6 系列之 Babel 是如何編譯 Class 的(上)

    • 1、ES5 的建構函式對應 ES6 的constructor 方法。

    • 2、ES6定義的所有方法,都是不可列舉的。

    • 3、在方法前加上 static 關鍵字不會被例項繼承,是直接通過類來呼叫,這個是"靜態方法"。

    • 4、類和普通建構函式的一個主要區別是類必須使用 new 呼叫,否則會報錯。

    • 5、使用 get 和 set 關鍵字攔截屬性預設行為

    • 6、Babel 轉換通過_classCallCheck檢查類是否是通過 new 的方式呼叫

    • 7、靜態屬性static bar = 'bar'轉換成Person.bar = 'bar'

    • 8、Babel 生成了一個 _createClass 輔助函式,該函式傳入三個引數,第一個是建構函式,在這個例子中也就是 Person,第二個是要新增到原型上的函式陣列,第三個是要新增到建構函式本身的函式陣列,也就是所有新增 static 關鍵字的函式。該函式的作用就是將函式陣列中的方法新增到建構函式或者建構函式的原型中,最後返回這個建構函式。

  • ES6的Symbol竟然那麼強大,面試中的加分點

    • 1、symbol for 全域性共享Symbol
    • 2、symbol keyFor 獲取全域性共享Symbol的key
    • 3、Object.getOwnPropertySymbols獲取Symbol
    • 4、Symbol.hasInstance 內部方法,判斷某物件是否為某構造器的例項
    • 5、Symbol.toPrimitive內部方法,轉換為原始值,加法運算會觸發三種型別轉換:將值轉換為原始值,轉換為數字,轉換為字串
  • 【Vue.js 牛刀小試】事件修飾符的使用

    • 1、DOM事件流中存在著三個階段:事件捕獲階段、處於目標階段、事件冒泡階段
    • 2、.stop:阻止事件冒泡
    • 3、.prevent:阻止預設事件
    • 4、.capture:新增事件監聽器時使用事件捕獲模式
    • 5、.self:只當在 event.target 是當前元素自身時觸發處理函式
    • 6、.once:事件只觸發一次
    • 7、.passive:滾動事件的預設行為 (即滾動行為) 將會立即觸發

11月5號

  • 使用Promises(優)

    • Promise 本質上是一個繫結了回撥的物件,而不是將回撥傳進函式內部。

    • 在 JavaScript 事件佇列的當前執行完成之前,回撥函式永遠不會被呼叫

    • 通過 .then 形式新增的回撥函式,甚至都在非同步操作完成之後才被新增的函式,都會被呼叫

    • 如果想要在回撥中獲取上個 Promise 中的結果,上個 Promise 中必須要返回結果

    • 在一個.catch操作之後可以繼續使用鏈式.then操作

    • 通常遞迴呼叫一個由非同步函式組成的陣列時相當於一個 Promise 鏈式:Promise.resolve().then(func1).then(func2)

    • 傳遞到then中的函式被置入了一個微任務佇列,而不是立即執行,這意味著它是在JavaScript事件佇列的所有執行時結束了,事件佇列被清空之後才開始執行

    • 巢狀的 catch 僅捕捉在其之前同時還必須是其作用域的 failureres,而捕捉不到在其鏈式以外或者其巢狀域以外的 error。

    • 一個好的經驗法則是總是返回或終止 Promise 鏈,並且一旦你得到一個新的 Promise,返回它。

  • 基於webpack實現react元件的按需載入

    • 實現靜態資源的按需載入,最大程度的減小首頁載入模組體積和首屏載入時間,其提供的Code Splitting(程式碼分割)特性正是實現模組按需載入的關鍵方式。

    • 將某些第三方基礎框架模組(例如:moment、loadash)或者多個頁面的公用模組(js、css)拆分出來獨立打包載入,通常這些模組改動頻率很低,將其與業務功能模組拆分出來並行載入,一方面可以最大限度的利用瀏覽器快取,另一方面也可以大大降低多頁面系統的程式碼冗餘度。

    • CommonsChunkPlugin公共基礎庫模組單獨打包到一個檔案中

    • 經常使用webpack的css-loader來將css樣式匯入到js模組中,再使用style-loader將css樣式以<style>標籤的形式插入到頁面當中,缺點是無法單獨載入並快取css樣式檔案,頁面展現必須依賴於包含css樣式的js模組,從而造成頁面閃爍的不佳體驗。

    • 將js模組當中import的css模組提取出來,需要用到extract-text-webpack-plugin

    • 使用React動態路由來按需載入react元件

  • 大前端的技術原理和變遷史

    • Web1.0 到 Web2.0過渡的標誌,就是Ajax的出現(2005年)。
    • AJAX 即 Asynchronous JavaScript and XML(非同步的 JavaScript 與 XML 技術)
    • SPA 即單頁面,就是頁面整體不重新整理,不同的頁面只改變區域性的內容的一種實現方式。
    • 一個完整的URI有以下幾部分組成:scheme:[//[user[:password]@]host[:port]][/path][?query][#fragment],以上規則中,只有 # 後面的 fragment 發生改變時,頁面不會重新請求,其它引數變化,均會引起頁面的重新請求,而在Js中恰恰還有事件 window.onhashchange 能監聽到 fragment 的變化,於是就利用這個原理來達到一個修改區域性內容的操作。#fragment 部分就是對應到Js中的 location.hash 的值。
    • 一個符合前端工程化要求的方案應該包含以下要素:開發規範、模組化開發、元件化開發、元件倉庫、效能優化、部署、開發流程、開發工具

交流

本人Github連結如下,歡迎各位Star

github.com/yygmind/blo…

我是木易楊,網易高階前端工程師,跟著我每週重點攻克一個前端面試重難點。接下來讓我帶你走進高階前端的世界,在進階的路上,共勉!

如果你想加群討論每期面試知識點,公眾號回覆[加群]即可

每週分享第 2 期