JavaScript進階(十):高階類

這篇博客我們要說的東西叫做 HOC(Higher Order Class),高階類。

那麼這個高階類是什麼意思呢?

一般正常的情況下,繼承的過程當中,我們都是子類能夠使用父類當中的東西。

但是,反過來,我們這個高階類,它高在哪呢?它高在了,父類能夠反過來使用子類的東西。

 

本身高階類這個東西,它並不是那麼的常用,但是偶爾的時候,拿出來用那麼一次,會對你有極大的一個幫助,所以我們在這來好好的瞭解它一下,正好也是跟繼承關係比較緊密的一個東西。

那麼我們就直接搞個例子來看看:

到這爲止,一切正常。

再接下來,我想打印 this.age:

這就有問題了,我的 this 有 age 嗎?並沒有。

所以說,這個正常情況下,打印出來的會是一個 undefined。

寫錯代碼了?其實不是的。

我們接下來準備在此基礎之上,在來搞一個類 B,它是繼承自 A 的:

你會發現一個很有意思的事,name 肯定是會出來的,但是這個 age 也有了。

注意,實際上來說,A 這個類,它本身是沒有 age 的。

那麼它現在用到的 age 是從哪來的?其實是到了子類才加上的。

 

首先肯定一點,如果說平白無故的這麼去用,其實是不太保險的,爲什麼呢?

萬一沒人繼承你,或者有人把你這個 A 單獨拿去用了,等等一系列的,是不是就會出事啊?

所以這是一個技巧性的東西。

 

接下來大家一定會覺得很奇怪,這個好像還挺簡單的,能用來做什麼呢?

別忘了,我們平常真正在工作的過程當中,經常會用到一些比較公共的東西。我們假設現在有一堆的類,它們是一些組件,我們需要用它們來做事。

那我們是不是經常涉及到組件之間的數據通信,也就是數據上要有所交互?

那麼現在這個時候,我希望的很簡單,就是有這麼一個全局的數據,只要有人改東西,就都是往它裏面去改,這樣的一個狀態。

那麼這個過程,其實用我們所謂的高階類,會非常容易來實現。

 

當然,我們所說的全局並不是真的全局,如果說就弄一個全局變量,往那一放,好嗎?不好,因爲全局的,必然是不太好的,很容易衝突。

所以我們希望它是一個受控制的這麼一個狀態,就只在這幾個類之間通用。那麼具體來說怎麼做呢?

既然說到了數據的共享,那麼我可能會有一個單獨的類,這個類是專門負責管理這些數據的,就叫它 Store.js:

既然它是存東西的,那麼我們需要做幾件事。

首先,存數據得有個地方吧,所以我得有一個數據 state:

然後我們不用搞的太複雜,對 state 就 2 個操作:get 和 set。

首先,get,它能夠給我返回 state 身上的數據:

這裏順便一說,我們在這裏這麼寫,其實不是特別好。

我們在前面就有提到過,你必須得保證你代碼的健壯性,說白了,就是有錯就得趕緊提出來。

所以說在這,我更希望加上一個東西,什麼東西呢?

就是我看一看,如果這個 key 是在 state 裏面的,這個沒問題,說明它是在找你要一個已經定義過的數據,那我們就把數據給它。

但如果你試圖訪問的是一個不存在的數據,那我就給你拋錯。

然後,我們還可以有一個 set 方法,就是簡單的賦值:

然後接下來,有意思的事就來了,我希望的是,我的那些類,都可以使用這個 Store。

那麼這時候有一個問題,我就創建一個,然後大家都來用,行不行?行,但是我想要更方便的方法。

我希望的是,你的那些類,可以像直接訪問自己的屬性一樣來用我上面的東西。

 

所以,我們現在就來設計一下,比如現在我有一個類 A。

然後現在有兩種方法:

第一種方法,我在初始化的時候,把 store 給你,然後你就可以把它存起來了。

存起來了之後,以後需要數據的時候,就可以直接從它上面去 get:this.store.get。

絕對沒問題,但是這種方式太過於麻煩了。

我希望更加簡單一點。

 

所以我們用第二種方法,我希望這個類,直接就能用 Store 的東西。

那麼在 Store.js 裏面,我們加上一個方法,我們就叫它 connect:

然後這個 connect 有點特殊,它不是 connect 到某一個東西上,而是直接 connect 到一個類上,一個 class 上:

然後現在我們就要考慮下具體怎麼做。

實際上來說,我們在這既然想要讓 A 那邊直接就能用我這個東西,那麼我可以這麼來寫,我們直接去繼承一下。

我們在 Store 的 connect 裏邊弄一個新的 class,這個 class 名字都可以不給,它直接繼承自 cls:

function 能不帶名字,class 當然也能不帶名字。 

這裏順便一說,因爲我這個 connect 是一個很廣義上的繼承,所以不一定繼承的是個什麼玩意。

所以在這,我們需要把它所有的參數全都收起來,然後並且全都發給那邊:

這樣的話,不論怎麼傳參,我就都能應對的了。

 

然後接下來我要做的事,就是希望讓這個新的 class 的 this 上面,加一個 get。

這個 get 是誰呢?我希望就是 Store 的 get。

那麼這樣寫行嗎?

這相當於啥都沒幹。

實際上來說,在這就有一個問題了:

上面的 this 和裏面的 this 其實是不同的,它們是 2 個 this。

上面的 this 你還在一個方法裏面,而下面的 this 你已經到構造函數裏去了。

構造函數裏的 this 是會變的,這個我們都知道。

所以,爲了方便起見,我們給它起個名字:

這麼寫,我們就清楚了很多。

 

但是,還有一個問題,因爲我們在 Stroe 的 get 和 set 裏面大量的用到了 this。

那麼如果我直接的把它賦給新 class 那邊的話:this.get = store.get。

是不是 get 和 set 裏面的 this 就變成那邊的新對象了啊?那我就沒法用了。

那麼,我們就需要改變 this 的指向,不然 get 和 set 操作的數據就存不到 Store 裏面的 state 了。

所以,最簡單的方法,直接 bind 過去就可以了:

然後 set 我們也會有:

然後接下來,我們現在寫好了這麼一個類之後,我要幹什麼呢?

我的目的其實爲了把它 return 出去:

這樣的話,其實我的這個 connect 就可以幫助我來生成一個新的類。

 

當然,大家肯定很奇怪,弄了這麼半天,幹啥呢?

我們接下來就來試一試:

比方說這個 class A,現在我直接就使用 get,set:

然後你會發現它報錯了,爲什麼呢?

這個並不奇怪,因爲 A 上面確實沒有 get 和 set,你直接寫是出不來的,肯定會報錯。那怎麼辦呢?

 

那麼接下來,我要如何讓它有 get 和 set 呢?

非常的簡單,我們希望對它做一個 connect 的處理:

我們先 new 一個 Store 的實例。

然後我希望其他的這些類,能夠共享某一個數據,所以我直接讓 store 來 connect 這個 class A:

那麼注意,你在用的時候,是沒有任何區別的:

但是你可以看到,這個時候就沒問題了。

 

實際上來說,在 setA 裏面,你有沒有發現,我們在用的 set 和 get ,根本就不是我們自己定義的。

我們也沒有去繼承誰,而這時候我卻可以用這個 set 和 get。

 

當然,如果只是到現在爲止的話,一般是沒有意義的,我們希望的是,能夠在幾個類之間,讓這個數據流動起來。

所以我們接下來再新建一個 B.js。

並且在 A 裏面 setA,B 裏面 getA:

然後這個時候,你可以看到,12 就能夠出來了:

其實現在我們這個數據,就已經跨越了 A 和 B 它們兩個本身在流動。

當然了,原理也很簡單,都是靠 Stroe 在中間調節。

 

所以我們這個高階類,它重點不在於別的,就在於一件事:

說白了就是,我沒有用我自己的方法,我也沒用我父級的方法,而是用了我子類的方法,這是它其中最神奇的一個地方。

高階類這個東西,其實並不是那麼的常用,但是它真的還有點用,尤其是在一些很特殊的場景下,其實有它會特別的方便,像變戲法一樣給你變出一些東西來。

 

然後我們來稍微的總結一下:

1,它是在父類中使用子類纔有的東西。

對於我們這個例子而言,誰是父類?

A 和 B 是父類,這點一定要弄明白。

Store.js 裏面的 connect,它 return 出去的 class 纔是子類。 

爲什麼?就看 extends:

誰有 extends,誰就是子類。

 

2,類寫完不直接用,需要包裹一下。

你會發現它是這樣的:

就是說,平常你寫的這個類,它不會直接拿去用,而是會經過一輪的包裝。

對於我們這來說,它的包裝就是 connect。(當然名字是隨便叫的,你喜歡也可以叫 aaa)

實際上來說,類寫完不直接用,而用其他的類,其他的東西來包裹它一下,然後在包裹的過程當中,我們也看到了,它是默默的在繼承某些東西,當然用戶是沒有任何的感知,但是對用戶來說是方便的。

然後你把它包裹了之後,其實在這個過程當中,就給它加上了它本來應該有的那些方法,但是它自己沒寫,是我們給它加上的。

當然在這個過程當中,有一些細微的小問題,比如 this 的指向問題,寫法問題等等,都需要注意下。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章