1. 程式人生 > >高階函式HOF和高階元件HOC(Higher Order Func/Comp)

高階函式HOF和高階元件HOC(Higher Order Func/Comp)

一、什麼是高階函式(元件),作用是什麼?

子類使用父類的方法可以通過繼承的方式實現,那無關聯元件通訊(redux)、父類使用子類方法(反向繼承)呢
為了解決類(函式)功能交叉/功能複用等問題,通過傳入類/函式返回類/函式(繼承)的方式使得類擁有自身未定義的方法。

例如react-redux的connect方法使用了高階元件:

React Redux的connect:

const HOC = connnect(mapStateToProps)(Comp);
// connect為柯里化函式  實際為 =>
function connect(mapStateToProps) {
  // ...
  return function(Comp) {
    // ...
  }
}
// 使用箭頭函式則為
const connect = mapStateToProps => Comp => {...};

二、通過高階函式實現兩個無關函式的通訊

需求介紹

存在一個類SubClass(子類),該類範圍內有資料state物件,且有setState和getState兩個函式方法。現在希望通過SupClass1(超/父類1)去呼叫SubClass(子類)的setState方法,並在SupClass2(超/父類2)裡通過getState方法輸出結果。

注意,子為sub,父為sup

檔案目錄

├  ├── SubClass.js              # 子類
├  ├── SupClass1.js             # 父類1
├  ├── SupClass2.js             # 父類2
├── index.html

SubClass類增加資料state,並賦予查詢和修改的能力

// SubClass.js
class SubClass {
  constructor(args = {}) {
    this.state = {
      ...args,
    };
  }
  // 賦值時需要提供鍵和值
  setState = (key, val) => {
    this.state = {
      [key]: val,
    };
  };
  getState = (key) => {
    if (key in this.state) {
      return this.state[key];
    }
    // 當然我們希望嚴謹點
    const err = '無效key值';
    throw err;
  };
}

我們試試SubClass功能如何

// index.html
const subcls = new SubClass({name: 'xiaobe'});
const res = subCls.getState('name');
console.log('res', res);
// 輸出xiaobe,妥妥的

接下來我們給SupClass1賦予setState的能力

class SuperClass1 {
  set(key, val) {
    // SuperClass1裡沒有setState方法!
    this.setState(key, val);
  }
}

如果直接執行類裡的get方法肯定是會出錯的。所以我們需要先對SupClass1做點事情。

需要給SuperClass1類裡增加方法setState,可以使用繼承

// SuperClass1.js
class SuperClass1 extends SubClass {
  constructor(props) {
    super(props);
  }
  set(key, val) {
    // 父類1上使用子類的setState方法
    this.setState(key, val);
  }
}

// index.html
const supCls1 = new SuperClass1({name: 'sup-xiaobe'});
const res = supCls1.getState('name');
console.log(res);
// 也能輸出sup-xiaobe

但如果單純使用繼承的方式會造成很多的麻煩。例如子類和父類如果有同名方法,預設子類會覆蓋基類(父類的其他叫法)的同名方法,如果基類方法使用了函式繫結或箭頭函式,其this的指向就改變了,指向了基類,導致自身同名方法失效。

因此我們還是需要通過高階元件實現;

首先我們先給子類SubClass增加一個HOC入口

class SubClass {
  // ...
  HOC(cls) {
    // 需要呼叫SubClass類的方法,所以需要存一份其this
    const sub_this = this;
    // 父類除了以下新增的兩個方法,其他無任何變化地返回!
    return class extends cls {
      constructor(props) {
        super(props);
        // 此處this指向該子類,sub_this指向SubClass類
        this.getState = sub_this.getState;
        this.setState = sub_this.setState;
      }
    }
  }
  // ...
}

接著我們來父類1SupClass1例項化前升級(呼叫HOC)!

// index.html
const subCls = new SubClass();
// 在子類例項化後給父類加上HOC方法
const supClsHoc1 = subCls.HOC(SuperClass1);
// 例項化父類
const supCls1 = new supClsHoc1();
// 重新定義state.name
supCls1.set('name', 'sup-xiaobe');

console.log(supCls.getState('name'));
// 輸出sup-xiaobe

同理地完成SupClass2

// SupClass2.js
class SuperClass2 {
  get(key) {
    return this.getState(key);
  }
}

// 最終的index.html
const subCls = new SubClass({name: 'xiaobe'});
const supClsHoc1 = subCls.HOC(SuperClass1);
const supClsHoc2 = subCls.HOC(SuperClass2);
const supCls1 = new supClsHoc1();
const supCls2 = new supClsHoc2(); 

supCls1.set('name', 'sup-xiaobe');
const res = supCls2.get('name');
console.log('res', res);

這麼一個基礎簡單的元件通訊就完成了。

根據這個思路可以封裝一個類似全域性變數的Store.js

思考個問題