vue中methods中的方法閉包緩存問題

vue中methods中的方法閉包緩存問題

問題背景

需求描述

  • 在路由的導航欄中須要, 判斷是否爲第一次點擊
  • 須要一個標誌位來記錄是否點擊過
  • 現狀:html

    • 這個標誌位只在一個函數中用過.不但願存放全局
    • 但願在這個methods中造成閉包, 用來緩存這個函數
    • 作出以下嘗試後, 發現能夠實現.
  • 當前問題:vue

    • 不能在閉包調用時找到正確的this.

詭異點

  • 測試使用時: 返回的this找到了window
// 測試使用:
  <div id="app">
    <button @click="test">測試按鈕</button>
  </div>
  <script>
    var app = new Vue({
      el: '#app',
      methods: {
        test: (() => {
          `use strict`
          console.log(this) // Window
          var flag = true
          return () => {
            console.log(this) // Window
            flag = false
          }
        })()
      }
    })
  </script>
  • 實際項目中的this變成了undefined錯誤截圖
  • 更加詭異的是debugger以後, 咱們一步步來看
  • 當前代碼:
pointJump: (() => {
      let isFirstChanged = false;
      console.log(this);
      debugger;
      return entry => {
        console.log(this);
        console.log(isFirstChanged);
        debugger;
        isFirstChanged = true;
      };
    })(),
  • 操做:緩存

    1. 刷新頁面, 第一次函數當即執行第一步
    2. 頁面生成完成後: 咱們再次經過按鈕觸發事件: 此時debugger顯示內存中爲Vue的頂級對象, 而在控制檯打印出來的依舊是undefined

在debugger中顯示的內容
控制檯顯示的內容

執行過程分析

  • 第一次執行的時候爲undefined是正常的, 由於第一次閉包執行, 沒有找到this
  • 當咱們再次執行的時候, 雖然調用起來的上下文, 也就是this已經改了, 可是由於在做用域中那個this所表明的空間仍是undefined, 因此沒有能改變過來.
  • 就形成了咱們所看到的詭異的現象.

與測試文件有差異的緣由

  • 由於在測試環境下, 沒有能開啓嚴格模式.
  • 通過兩次不一樣位置的的開啓嘗試, 都不對
  • 依舊能夠找到window對象
  • 如今推測是在vue內部進行的實現, 由於引入的vue版本不一樣.須要再進行測試, 看來源碼仍是要好好過一遍
<script>
    var app = new Vue({
      el: '#app',
      methods: {
        test: (() => {
          `use strict`
          console.log(this) // Window
          var flag = true
          return () => {
            console.log(this) // Window
            flag = false
          }
        })()
      }
    })
  </script>

最後找到緣由的測試

  • 由於箭頭函數的this是不會改變, 擁有根據父級可以返回的this
  • 而後由於上面的閉包環境中的this, 指向的一直都是undefined
const test = (() => {
  let aaa = true;
  return function () {
    console.log(this);
    aaa = false;
  };
})();
mainJump(entry) {
  test.call(this);
},

解決方法

  • 造成閉包返回的函數中, 不要使用箭頭函數, 使用function定義便可
pointJump: (() => {
      let isFirstChanged = false;
      return function () {
        console.log(this); // Vue的頂級對象
        isFirstChanged = true;
      };
    })(),

總結

  • 箭頭函數不會被call, bind等方法改變this指向
  • 在閉包中返回函數, 緩存變量時, 使用function進行返回函數的定義.
相關文章
相關標籤/搜索