設計模式(1):只執行一次的函數


概述

最近最近做項目的時候總會思考一些大的應用設計模式相關的問題,我把自己的思考記錄下來,供以后開發時參考,相信對其他人也有用。

只執行一次的函數

我們經常會遇到這種情況,就是希望某個函數只執行一次,以后就不執行了。一般情況下,我們會這么寫:

<script>
export default {
  data() {
    return {
      runOnce: true,
    };
  },
  methods: {
    func() {
      console.log('hello world', this);
    },
    funcRunOnce() {
      if (this.runOnce) {
        this.func();
        this.runOnce = false;
      }
    },
  },
};
</script>

但是這樣並不優雅,不僅污染了data,還用2個方法進行實現,實在難看。

用閉包改進

於是我們考慮用閉包,把data里面的runOnce這個變量放到閉包里面去,這樣就不會污染data了。代碼如下:

<script>
export default {
  methods: {
    func() {
      console.log('hello world', this);
    },
    funcRunOnce(params) {
      let runOnce = true;
      return () => {
        if (runOnce) {
          this.func();
          runOnce = false;
        }
      }();
    },
  },
};
</script>

但是這么寫顯然是錯了,因為每次調用funcRunOnce都會構造一次閉包,里面的runOnce這個變量根本不會共享。所以繼續改寫如下:

// 方法1
<script>
export default {
  created() {
    this.funcRunOnce = this.runOnce(this.func);
  },
  methods: {
    func() {
      console.log('hello world', this);
    },
    runOnce(func) {
      let runOnce = true;
      return (params) => {
        if (runOnce) {
          func(params);
          runOnce = false;
        }
      };
    },
  },
};
</script>

// 方法2
<script>
export default {
  methods: {
    func() {
      console.log('hello world', this);
    },
    runOnce(func) {
      let runOnce = true;
      return (params) => {
        if (runOnce) {
          func(params);
          runOnce = false;
        }
      };
    },
    funcRunOnce: this.runOnce(this.func),
  },
};
</script>

使用utils

可以看到,上面的方法仍然很不優雅,要么用一個created和2個方法實現,要么用三個方法實現。而都用了一個公共的方法runOnce。所以我們考慮把runOnce放到utils.js里面去。

// utils.js
export function runOnce(func) {
  let runOnce = true;
  return (params) => {
    if (runOnce) {
      func(params);
      runOnce = false;
    }
  };
}

//example.vue
import { runOnce } from '@/utils';
<script>
export default {
  methods: {
    funcRunOnce: runOnce(() => {
      console.log('hello world', this);
    }),
  },
};
</script>

上面的寫法看起來非常簡潔,但是實際上是不行的,因為this的指向錯了。由於runOnce返回的函數並不是vue實例的方法,所以里面的this指向的是undefined

注意:即使看起來我們好像在funcRunOnce方法中用箭頭函數捕獲了外面實例的this,但是實際上它捕獲的並不是外面的實例的this,而是runOnce返回的函數里面的this。

捕獲this

能用箭頭函數的地方我們都用了,但是為什么我們還是捕獲不了this呢?如此一來是不是完成不了這個任務了?

並不是,方法還是有的,方法是不用箭頭函數捕獲this。代碼如下:

// utils.js
export function runOnce(func) {
  let runOnce = true;
  return function(params) {
    if (runOnce) {
      func.apply(this, params);
      runOnce = false;
    }
  };
}

//example.vue
import { runOnce } from '@/utils';
<script>
export default {
  methods: {
    funcRunOnce: runOnce(function h() {
      console.log('hello world', this);
    }),
  },
};
</script>

通過查看代碼可以看出,2個地方的箭頭函數都被改寫成了function,並且還用到了apply函數來強制施加this。

理由很簡單,由於runOnce函數里面沒有用箭頭函數,所以它返回的函數是屬於vue實例的,所以它返回的函數的this,是指向vue實例的;又因為funcRunOnce里面沒有用箭頭函數,所以我們可以用apply把這個this強制附加到func里面去!

同理我們還可以寫出第一次不執行,后續才執行的函數:

// utils.js
// 第一次不執行,后續再執行
export function notRunOnce(func) {
  let once = false;
  return function(params) {
    if (once) {
      func.apply(this, params);
    }
    once = true;
  };
}

學到了什么

  1. 在vue里面可以用賦值的形式初始化方法,或者在created里面初始化方法。
  2. 箭頭函數雖然能捕獲this,但不是萬能的;有時候我們需要用function和apply結合來捕獲this。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM