類比JS面向物件和JAVA面向物件的設計思想
(個人理解,如有誤導請指正)
我們都知道JS面向物件是採用原型鏈的方式設計的,JAVA是採用正統的面向物件的思想設計的,通過我的類比分析,我得出一個結論,它們之間本質上是沒有區別的。
先講一講面向物件的特點: 封裝和繼承和多型。
封裝: 最原始的結構化程式設計理念是 “資料結構+操作”,比如C語言,通過結構體和對相應結構體的一堆操作函式來實現業務邏輯,相應函式的第一個引數都有一個指標形參來指向要操作的結構體。這其實也是一種封裝,只是函式和函式之間的沒有明顯的界限。到了面向物件的世界裡,我們把資料結構和其操作函式嚴格的封裝到一塊,用類的思想進行了有界限的劃分,不同的類之間起操作和資料都有了明顯的劃分,編譯器給類函式自動添加了this指標來指向要操作的資料。
繼承:自然界的事物從來不是宇宙誕生之初就是這個樣式的,而是進過不停的進化才變的越來越高階,比如 單細胞-多細胞-動物-人,進化的過程中,人自然有著動物的屬性,那麼我們程式設計的時候想要封裝人類,把對人類的所有操作和對動物的所有操作都封裝到人這個類裡面顯然是不合理的,我們自然的想法是給動物封裝一個類來包含對動物的所有操作,給人封裝一個類來包含對人類的所有操作,然後人類繼承動物類,通過繼承,邏輯更加清晰合理。
多型:人雖然有著動物屬性,但是人說話的方式和動物說話的方式有著明顯的區別,在繼承裡面,我們應該在動物類裡實現動物說話這個操作方法,在人類裡面實現人說話這個操作方法,然後人類繼承動物類,然後對於要操作的資料物件,如果是資料物件是人,那麼理應呼叫人說話這個操作來實現人說話,如果資料物件是動物,那麼就應該呼叫動物說話這個操作來實現動物操作,如果人類裡面沒有實現人說話這個操作,我們應該根據繼承鏈向上找,找到動物說話這個操作來執行人說話這個行為,這種邏輯方式是自然而然的思維,我們沒有必要深入的考慮為什麼~
我們仔細思考一下,上面講的封裝、繼承、多型、更多關注的是類對資料的操作,是操作函式!沒有體現這個類要操作的資料模型是什麼樣的,那麼資料模型在類中如何體現呢?答案是建構函式!給類新增一個特殊的操作函式--建構函式,然後通過執行一個類的建構函式,我們就能在堆記憶體中是例項化出一個特定結構的資料模型,多次構造,會生成多個數據模型~ 對所有資料模型的操作函式,我們只需要一套就可以了--那就是類本身。
好了,通過上面的分析我們可以得出結論,一個類的設計實現只需要設計兩部分東西--- 1建構函式(代表資料模型)2一堆的操作函式!
我們來分析下JS是如何實現面向物件的。JS宣告一個類的時候,最簡單的情況,只實現一個建構函式就可以了,比如
function Person(){ this.name = "a";this.age = 12;} JS直譯器預設會給函式物件再新增一個prototype屬性來指向一個原型物件,這個prototype對應的原型物件其實就是我上面的我說到的一堆的操作函式的包含物件!prototype原型物件預設還會包含一個__proto__引用來指向下一個原型,預設JS直譯器給函式的prototype的__proto__指向Object類的原型物件,其中包含了所有物件共有的基礎操作,這其實就是JS裡的繼承! 好了,這裡我總結下JS類的組成,建構函式+建構函式對應的prototype物件來構成當前類,prototype物件的__proto__指向的下一級prototype代表了當前類繼承的父類、爺爺類! 類執行的時候,同過建構函式構造出資料模型,同樣給資料模型物件的__proto__屬性指向對應的原型物件(邏輯上理解就是資料模型有指向對應的類的引用),呼叫資料模型操作的時候,直譯器根據資料模型的__proto__指向的原型物件尋找對應的操作函式,順著__proto__鏈一直找到爺爺類、祖先類。總結:JS類就是建構函式+原型物件鏈! 一個類在直譯器記憶體中只存在一個建構函式和原型物件鏈!
(補充:JS中的原型物件預設屬性有兩個:__proto__和constructor,__proto__指向父原型,constructor指向建構函式,我們也可以這麼理解JS類:一個函式的原型物件代表了一個類!一個函式的原型物件在直譯器中只存在一個!new物件的時候,執行對應的原型物件的constructor來生成資料模型!)
JAVA的類模型簡單說明:編譯後的class檔案裝載進虛擬機器後代表一個類,同一個虛擬機器同一個ClassLoader範圍內只在記憶體中存在一個,其中包含了類物件建構函式還有一堆操作函式,類通過extend繼承關鍵字來引用其他的class~
大結論:JS面向物件和JAVA面向物件是一致的,本質上都是來源於面向物件理論思想!!!