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

概述

最近最近作項目的時候總會思考一些大的應用設計模式相關的問題,我把本身的思考記錄下來,供之後開發時參考,相信對其餘人也有用。vue

只執行一次的函數

咱們常常會遇到這種狀況,就是但願某個函數只執行一次,之後就不執行了。通常狀況下,咱們會這麼寫:設計模式

<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了。代碼以下:app

<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裏面去。this

// 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。code

捕獲this

能用箭頭函數的地方咱們都用了,可是爲何咱們仍是捕獲不了this呢?如此一來是否是完成不了這個任務了?ip

並非,方法仍是有的,方法是不用箭頭函數捕獲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。
相關文章
相關標籤/搜索