JS進階(一)高階函數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

思考個問題🤔

getState和setState方法里使用的this指向?

我先把SubClass的完整代碼列下來

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];
    }
    return "";
  };
  HOC(cls) {
    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;
      }
    }
  }
}

可以發現凡是在方法內使用了類里的數據我都使用了箭頭函數,我們想想,如果不使用箭頭函數this指向會出現什么問題?

getState(key) {
  // ...
}

我們執行

const subCls = new SubClass({name: 'xiaobe'});
const supClsHoc2 = subCls.HOC(SuperClass2); 
const supCls2 = new supClsHoc2(); 
console.log('supCls2', supCls2.get('name'));

會發現瀏覽器直接給報錯:
Cannot use 'in' operator to search for 'name' in undefined

因為在SuperClass2類里根本不存在state!換句話說即this.state = undefined,在undefined里使用對象方法,當然出錯。

找到問題所在(我們需要的數據/方法在SubClass類里),我們有以下幾種解決方法:

// 1.在HOC方法里傳入state
this.state = sub_this.state;

// 2.getState使用箭頭函數,使得this永遠指向SubClass方法
getState = key => { ... };

// 3.將傳入的getState方法this綁定到SubClass類上
this.getState = sub_this.getState.bind(sub_this);

這么一來就解決了this指向錯誤的問題,關於this的全面解析,大家可以看我的這篇文章:

--- 后面介紹

React HOC


免責聲明!

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



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