JavaScript進階(二):面向對象的寫法和本質,類型檢測

上篇博客我們講了面向對象,但是說的在好,沒用。你要把它變成可以執行的代碼,才能夠真正的工作起來。

所以接下來,我們就來看一看,面向對象怎麼寫。

 

首先我們都知道,任何一個語言,它發展都是一個過程,尤其是我們的 js,更是如此。

所以大家常見的,我們的面向對象一般情況下,其實有 2 種寫法。

一個是舊版本的寫法,就是在 ES6 出現之前的那種寫法。

還有個,就是我們現在新版的寫法,也就是 ES6 裏面的 class。

 

我們前面說過,對於一個面向對象的東西來說,它應該具有類。

所以我們得先寫一個類,然後接下了來有了類之後,再去實例化,等到實例化完了之後,就會得到一個實例(也有人管它叫對象)。

類 -> new -> 實例(對象)

 

那麼在我們 ES6 之前的寫法當中,我們會用一個 function 來做。

首先,我們知道任何一個類,其實它都需要一個東西,就是構造函數。(也叫做構造器)

什麼叫構造函數呢?

很簡單,就是當你去實例化我這個類的時候,我要做一些初始化的工作。

實際上來說,在老寫法裏面,類和構造函數是不分的,這也是它最大的一個問題。

比如: function A() { }

那麼就這個函數 A,它到底是什麼?

其實它既是構造函數,同時它也是類。所以這個就搞的很不清不楚的,很模糊。

那麼我們來試試:

可以看到控制檯上的輸出:被創建了。

實際上來說,這裏的被創建了,就是 new 的過程。

所以,我們知道了2件事:

1,在 ES5 裏面,構造函數和類,是不分的。

2,在我們去實例化一個類的過程當中,它就會運行你的構造函數,然後裏面的代碼就都可以被執行到。

 

那麼在 ES6 裏面,它又是怎麼樣的一個寫法呢?

首先它有了一個專門的關鍵字叫做 class,類的英文就是 class。

原來的 ES5 有一個最大的問題,就是一眼望過去,我壓根不知道它到底是個類還是個普通函數,因爲都是 function。

而現在有了一個專門的關鍵字,這個是件好事,這是第一。

第二,它有了專門的一個構造函數,constructor。

所以到了 ES6 裏面你會發現,類是類,構造函數是構造函數,兩者終於拆開了,這樣至少從概念上沒有那麼容易混淆。

可以看到,這個時候我們的構造函數也會被執行。

從這點來說,兩邊的寫法上是完全一樣的。

 

然後前面我們也說過,所有的類也好,對象也好,它上面就 2 個東西,一個是屬性,一個是方法。

所以接下來,我們就給它上面添加屬性:

可以看到,加屬性的這個環節,不論是 ES 幾裏面,都還是這樣的。

所以,不論是以前的寫法,還是現在的寫法,其實它裏面的意思差不多。

只不過寫法上更加的清晰了而已。

 

然後接下來,類還包括一個東西,叫做方法。

在原來的寫法中,我們是通過給原型 prototype 添加方法:

這個寫法其實有一個特別大的問題。

就是你把這個類給拆散了,相當於你是毫無關聯,獨立的 2 部分。

函數是一塊,下面的 prototype 是另外一塊。

不是說它不行,就是比較亂。

 

而到了 ES6 裏面,它的寫法就好了很多,這個方法可以直接寫在類裏面,它是一個整體:

可以看到,效果上 2 邊是一樣的。

 

當然,順帶一提,有的人可能問會:class 裏面的方法爲什麼不用加 function,是簡寫嗎?

我們加一個試試:

加完這個之後,你會發現不行,報錯了。

實際上來說,它並不是一個簡寫,它本身就不是一個函數,它就是一個方法。

 

那麼 constructor 有參數嗎?

其實 constructor 就是一個普通的函數,所以它傳參的方式,就和我們平常函數的傳參是一樣的。

那麼 constructor 的參數是在哪來的?

就是你在 new 的時候來的,我們在 new 的時候,其實就是在調用那個 constructor。

那麼我們來總結下,ES5 這種寫法到底有什麼樣的缺陷:

第一,它本身沒有專用的類,它就是 function,那我跟普通函數根本分不清。

第二,它會額外的需要一個 prototype 來幫忙。

不是說不好,這個 prototype,它本身的作用不是在給你寫這個類的時候去用的,而是將來我需要對一個類動態的修改的時候,我才需要用到它,以及 prototype 在繼承的階段還會有很多其他的問題。

而新版的寫法就方便很多,乾淨很多。

那麼到這爲止,我們對類的寫法已經沒問題了。

 

其實很多的技術,它本質上就純粹是爲了方便你,並不是說離了它不行。

比方說我們的這個面向對象,就是其中的一個。

我希望大家可以不光會用面向對象,你還要了解面向對象它的本質是什麼,現在我們要說的就是這個事。

實際上來說,我現在寫出的這個代碼,本質上它完全可以用另一套寫法來代替。

什麼意思呢?我們再創建個 html,兩邊對比着來看:

其實這個東西,就完全等價於右邊這個寫法。

當我想要用 a.show() 的時候,其實完全等價於 a_show()。

或者我說的更直白點,爲什麼要把它拆了?

很簡單,因爲這些所謂的屬性也好,方法也好,你可以認爲,它是在這個類當中給你提供了一個命名的空間。

什麼意思呢?

比方說,因爲各個實例之間,它都有自己的 name 和 age,這個它們之間是不衝突的,其實就相當於在右邊定義了 2 套變量,是一樣的效果。

實際上來說,類就是這麼個東西,它本質上就是一個命名空間,它只是幫你把這些東西包在一起,爲了讓你用着方便而已。

 

所以,實際上類這個東西,並不是非用不可。它做的事,就是可以不用你自己去搞右邊的那套東西罷了。

你比如說我需要 100 個 A 的實例,那我是不是隻要 new 100次就行了,甚至我還可以直接寫個循環 new。

那這樣的話,比我自己去定義幾百個變量和函數,是不是要方便的多呢?

這個,就是類的本質。

 

那麼總結下:

其實一個類,它僅僅只是一個空間,幫你把這些屬性也好,方法也好,都給你變成一個在這個空間之內是唯一的。

這樣的話,你就可以放心大膽的去用,這些相同的類裏面,不同的實例,它們是不會衝突的,就是爲了這個。

或者說的更直白點,其實類,它最重要的一個目的,就是爲了大量的去用。

你比如說,我這個類將來就只會 new 一個,多了沒有。

那麼這時候,其實你這個類存在的價值並不大,你乾脆還不如直接把他定義成一個變量,反正你就用一次。

所以說,類這個東西,它只是爲了你方便,沒有說你必須得用它。

 

現在我們已經瞭解了類最基本的東西 。

接下來我們要說的其實是一個小事,就是我們如何去做類型檢測。

說個更直白點,比如我現在有個參數,這參數到底是什麼類型的,我得知道吧?因爲有的時候,我需要根據不同的類型來做不同的事。

 

在我們 js 裏面,常用的一共有三種方式的類型檢測:

1,typeof。

2,instanceof。

instance 我們前面也說過,它是實例的意思。那麼 instanceof 它的意思就是:是不是某一個東西的實例。

比如,A instanceof B 的意思你也可以理解爲:看 對象A 的原型鏈上 有沒有 函數B 的原型。

3,constructor。第三種其實不算是官方推薦的方法,它是一個野路子。

(Object.prototype.toString.call() 這裏就不說了)

 

首先,typeof 很簡單,它更適合於用來檢測基本類型。

什麼叫基本類型?

我們 js 裏面就那幾種:number,string,boolean,function,object,undefined。

typeof 它只適合基本類型,什麼意思呢?我們來試試。

那麼爲什麼說 typeof 它更適合用來判斷基本類型呢?

很簡單,因爲它區分不了你具體是哪種對象。比如:

可以看到,它都是 object。

這就是爲什麼 typeof 僅僅適合用來判斷基本類型,因爲如果你是對象類型,它就都是返回 object,這個就不太合適。

 

如果我現在就想檢測它到底是哪種對象,這個時候你就可以用 instanceof。

它的用法很簡單,比如:a instanceof Array,意思就是,a 是不是 Array 的實例。

所以,instanceof 是用來檢測一個東西到底是不是這個類的實例,它更適合於用來去判斷這個實例的具體類型。

 

但是,instanceof 它不光對你那個具體的類型有反應,它還對父類有反應。

什麼意思呢?我們先讓 A 這個類繼承自 Array,然後在 new 出 A 的實例:

你會發現一個有意思的問題,這 2 個都是 true。

第一個還好理解,那麼第二個 true 怎麼理解呢?

我儘管是一個 A,但是我的父類是 Array。

換句話說,instanceof 不光能檢測你的直接類型,也能檢測你的父類型。

 

這時候我們可能就有一個想法了,就是如何判斷它到底是不是一個 Array。

如果我們有一個這樣的需求,constructor 就可以出來了,它可以幫助你很精確的去檢出它具體的類型。

constructor 它的作用很簡單,就是告訴你,這個實例,是用哪個東西構造出來的。

可以看到,constructor 它就標明瞭,當初是誰把我製造出來的。

實際上來說,它就是幹這個用的。

 

然後這個時候,如果我想檢測 a 到底是不是 Array,多了不行,那麼我們就可以這麼做:

所以,如果我真的是一個數組的話,那這時候就是 true。

然後我們需要知道一點,這個 constructor 它本身並不是說專門用來檢測類型的,它的作用是可以幫助你來返回實例的構造器。

只不過我們可以利用它間接的來完成一個精確的類型判斷。用人話說就是,只包括子級,不包括父級。

一般來說,typeof 和 instanceof 用的多一些,constructor 其實是個野路子,極少的情況會用到它。

我們只需要知道 constructor 偶爾也能用來判斷類型就行了。

 

然後順便一提,關於類型判斷,有一道面試題相信大家都聽說過,就是如何判斷是不是一個數組,相信到這裏,你已經沒問題了。

那麼,如果是文檔節點的類型,又該怎麼判斷呢?比如,如何判斷一個東西里面裝的是不是 div?

關於 DOM 裏面的東西,很多人都不是特別瞭解,比如 div 又是誰構造出來的?

這時候,constructor 就可以登場了。

可以看到,它是一個 HTMLDivElement,那麼我們就可以用這個東西去判斷:

那我不想判斷它是不是 div,就想廣義的判斷它是不是元素。

這個時候我們是不是就可以用 instanceof 來判斷,你是不是一個 HTMLElement:

因爲不管你是 div 也好,a 也好,body 也好,你是什麼都可以,最後你這些東西都是從 HTMLElement 這個類裏面繼承出來的。

 

所以我們就看到了,這個類型的檢測還是比較豐富的。

一些基礎類型的,我們可以用 typeof。

然後一些對象類型的,我個人是用 instanceof 更多一些,因爲我們一般來講,會把子級視作父級來處理。

最後的 constructor,它可以幫助你去返回精確的類型。

 

 

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