javascript
深入理解面向对象 -- 基于 JavaScript 实现
我們在學習編程時,避免不了會接觸一個概念,叫:面向對象編程(Object-oriented programming,縮寫:oop) (不是搞對象那個對象哈),其實我們的編程方式,不止有面向對象,還有 面向過程編程、面向流編程、面向函數編程、面向接口編程 等。作為一名一直混跡在前端的小菜鳥,今天就來跟大家深入的探討一下 JavaScript面向對象。作為程序員,我們多多少少都會接觸自己擅長語言之外的編程語言,比如我作為一名前端,同時我還會 Java,從這兩個語言本身出發的話,我們會發現這兩種語言的 面向對象 存在著一絲絲的不同,到底哪里不同呢?我們今天就拿這兩種語言對比著來,拿具體的實例看一下,到底什么叫 面向對象編程。
現在很多文章都會講 面向對象三大特性、面向對象七原則、設計模式 等概念,今天這篇文章不準備講這些概念,從實例出發,理解 面向對象 是什么,如何做 面向對象 程序設計。
我們在深入探討 面向對象 之前,我們先來復習一下 面向過程編程,這里可能有人會問了,不是講 面向對象 嗎?為什么還要講 面向過程 呢?主要是因為,面向過程編程 是軟件思想中的鼻祖。面向過程編程 還是很好理解的,因為它是一種以 過程 作為中心的編程思想,其中 過程 的含義就是 完成一件事情的步驟。
面向過程 其實是一種 機械的思想,它就像流水線一樣,一個階段銜接一個階段,每個階段都有自己的輸入、處理、輸出,而在流水線上流動的就是我們的原料或者中間產品,每個階段都有一個機械進行處理,最后的輸出就是我們的產品。
在運用 面向過程 的方法時,你也需要設計這樣一條程序:將程序劃分為不同的階段,設計好各個階段如何銜接,然后定義好每個階段需要處理的數據。
在實際開發中,我們會把需求拆成一個一個的命令,然后串起來交給計算機去執行。舉個例子,有個需求是:在淘寶給女朋友買口紅,那么程序員接到這個命令,會列出如下幾個步驟:
- 打開淘寶
- 買口紅
- 送女朋友
上面的每一個步驟,程序員都會用一個 函數 或 方法 來實現,而 函數 或 方法 是一些代碼的集合體,每個 函數 或 方法 可以實現一個功能,那么根據上述需求,我們可能會定義如下的函數:
- openTaoBao();
- buyLipstick();
- sendGrilFriend();
那么程序就會順序調用了。需求完成,順利交工。但是,你覺得這樣就算結束了么?No。產品經理說:"這才剛剛開始哦~~~"。
在開始介紹 面向對象 之前,我們先來簡單概述一下,什么是 對象?對象 是一個自成一體的實體,它僅包含屬性和行為,不含任何其他內容。與面向過程的方法相比,面向對象 不在局限于計算機的機器本質,而更加側重于對現實世界的 模擬。在 面向過程 的方法中,有一套設計嚴格的操作順序,有一個類似 中央控制器 的角色來進行統一調度;而 面向對象 的方法中,并沒有明確的 中央控制器 的角色,也不需要指定嚴格的操作循序,而是設計了很多 對象,并且指定了這些 對象 需要完成的任務,以及這些 對象 如何對外界的刺激做出反應。
如果說 面向過程 像一條流水線,那么 面向對象 就像是一個籃球隊。沒有哪個人能夠在一場比賽開始的時候,就精確指定每個隊員的每一次跑動、每一次傳球、每一次投籃...而是要指定隊員的角色(前鋒、中鋒、后衛等等),然后由隊員們自己根據情況做出反應。所以說,世界上可以有兩個一模一樣的生產線,但絕對不會存在兩場一模一樣的比賽。
簡單介紹了一下 對象,現在讓我們回到上面的例子。接下來,產品經理又提了需求:
- 在京東給女朋友買防曬霜
- 在唯品會給麻麻買貂
- 在蘇寧易購給爸爸買刮胡刀
- ...
如果我們還是用 面向過程 的方法,每次需求的變更,程序員就要把整個系統通讀一遍,找出可用的函數(如果沒有就再定義一個),最后依次調用它們。最后系統越來越雜亂無章難以管理,程序員不堪重負,紛紛操起刀走上了不歸路[笑哭]...
面向對象 從另一個角度來解決這個問題,它拋棄了函數,把 對象 作為程序的基本單元。那么 對象 到底是個什么東西呢?對象 就是對 事務的一種 抽象 描述。其實現實中的 事務,都可以用 數據 和 能力 來描述。比如我要描述一個人,數據 就是他的年齡、性別、身高、體重等,能力 就是他能做什么工作,承擔什么樣的責任。描述一臺電視,數據 就是它的屏幕尺寸、亮度,能力 就是播放青春偶像劇。
面向對象 的世界里,到處都是 對象。對象 不光有 數據 和 能力 ,還可以接受命令。例如,你可以讓 貓 這個對象 吃貓糧,就可以把 吃貓糧 的命令發給 貓 讓其執行(雖然傲嬌的貓咪并不能聽你的話吧[笑哭],這里只是舉個例子),然后我們就實現了 貓吃貓糧 的需求。
現在 對象 有了,那接下來該如何進行 面向對象 的編程呢?其實很簡單,我們依次向不同的 對象 發送命令就可以了。回到上面的例子,我們用 面向對象 來實現;先定義一個 app 對象,它的 數據 就是商城名稱、商品類型等,能力 就是打開、關閉;還有一個 人 對象,它的 數據 是姓名、性別、稱謂等,能力 就是買口紅、送口紅。然后我們依次下達命令:
- 向app下達 打開 的命令;
- 向人下達 買口紅、送女朋友 的命令;
- 向app下達 關閉 的命令。
其實,我們創建的對象,應該是剛剛好能做完它能做的事情,不多做,也不少做。多做了容易耦合,各種功能雜糅在一個對象里。比如我有一個對象叫 汽車,可以 載人,現在的需求是要實現 載人飛行,就不能重用這個 對象,必須新定義一個對象 飛機 來做。如果你給 汽車 插上了翅膀,賦予了它 飛行的能力,那么新來的同學面對你的代碼會莫名其妙,無從下手。
接下來,我們來看一下,上面的例子用代碼是如何實現的:
基于上面的例子,我們可以看到,JavaScript 的 面向對象 是基于 原型 的,也就是 prototype,而 Java 呢?Java 是基于 類 的,也就是所謂的 class。其實不管語言對于 面向對象 是基于什么的,從概念上講,大家都是一樣的,只是我們的實現方式不同。
在這里,我就不舉 Java 基于 面向對象 是如何實現上述實例的了,因為這篇文章講的就是 JavaScript [斜眼笑],想看 Java 的可以根據上述的文字描述自己實現一下哈,博主在這里就皮一下[笑哭]。
抽象
抽象 的中文概念非常形象,簡單來說就是 抽取出來比較像的部分。那么,在 面向對象 的領域里,抽取什么東西是比較像的部分?我們畫個圖來看一下 抽象 是個什么東東:
這里的抽象分為兩個層次:
第一個層次:對象是抽象成集合(類)
例如:西瓜 和 蘋果 抽象成 水果,這一層的 抽象 主要是將 屬性類似 的對象抽象出來。
注意:這里的 屬性類似 是指 屬性類別 一致,而屬性的取值是不一樣的。例如,將"西瓜"和"蘋果"都抽象成"水果",那么其屬性有顏色、重量、味道等等,但"西瓜"和"蘋果"的這些屬性取值肯定是不同的。
第二個層次(或更高層次):將對象抽象為超集合(超類,或者說父類,就是更高一級的集合或者類)
例如:水果 和 蔬菜 抽象成 食物,這一層的抽象主要是將 行為類似 的抽象成父集合(父類)。
注意:這里是 行為類似,而不是第一層抽象的那樣 屬性類似,因為在 面向對象 領域,行為一致的話就認為是同一類的,當然也不能是完全不同,完全不同的話就沒有相似點,也就無法抽象成類了,所以這一層抽象的重點是 相似。
在實際應用中,抽象的層次是不限的,根據業務需要,或者不同的觀察角度,可以抽象出很多層。
抽象的作用
抽象 并不是面向對象領域特有的概念和方法,在我們的日常生活和學習中,抽象 最主要的作用是 劃分類別,而 劃分類別 的主要目的其實還是關注隔離點,降低復雜度。所以,抽象是面向對象領域里面發現集合(類)的主要方法。
在JavaScript中,抽象 是允許模擬工作問題中通用部分的一種機制。這可以通過繼承(具體化)或組合來實現。JavaScript通過 繼承 實現 具體化,通過讓類的實例是其他對象的屬性值來實現組合。
JavaScript Function類 繼承自 Object類(這是典型的具體化)。Function.prototype 的屬性是一個 Object實例(這是典型的組合)。
多態(polymorphism)
引用 MDN web docs 中的一段話來描述一下 JavaScript多態:
就像所有定義在原型屬性內部的 方法 和 屬性 一樣,不同的類可以定義具有相同名稱的方法;方法是作用于所在的類中。并且這僅在這兩個類不是父子關系時成立(繼承鏈中,一個類不是繼承自其他類)。
[笑哭]大家看完這段話之后,是不是覺得很懵,這是在說什么啊,什么類,什么繼承。不著急哈,接下來我會詳細解釋一下在 JavaScript 中,多態究竟是怎么樣的存在哈...
polymorphism,翻譯成中文:多態性,我們從字面意思上就可以看出,多態 就是 多種形態 的意思。但仔細探究一下:多種形態 其實還是沒法很好的理解,不同的人也還是有不同的理解。
動畫片看得多的同學可能會以為:多種形態,就是很多種變身,就像孫悟空72變一樣,一會兒可以變成房子,一會兒可以變成牛魔王;
擅長打扮的美女可能會以為:多種形態,其實就是換不同的衣服嘛,一會兒文藝小清新打扮,一會兒高貴典雅的貴婦裝束;
學院派技術宅男可能會以為: 多種形態,其實就是多種狀態啦,比如說TCP協議棧有XX種狀態...
可能還有很多其它各種各樣的理解,但在 面向對象 領域,這些理解都不正確,多態不是變身、換裝、狀態變化,而是多胎...
哇!!博主你打錯字了,怎么可能是 多胎呢?這是什么意思啊?
其實,多胎 在這里也是一個形象的說法,在 面向對象 領域,多態 的真正含義是:使用指向父類的指針或者引用,能夠調用子類的對象。
我要是在這里引用 Java 代碼,會不會引起公憤[笑哭],還是乖乖的用 JavaScript 來寫個 多態 的例子:
嗯,沒錯,是我們想要的結果[嘿嘿]。那接下來,我們在來看一下,如果不用 多態,上述的例子要怎么寫。
當然最后的執行結果肯定是一樣的,那讓我們來看一下,這兩種寫法到底有什么區別:
- 首先,最開始的那個例子,我們用了個 test 函數,注釋也寫了,不需要關心對象具體是哪個,只要對象包含需要調用的方法就OK;
- 而第二個例子呢?我們仔細看一下,第二個例子現在看起來很清晰,但是不利于擴展,為什么?關鍵點在執行函數中,如果我要是在加了 女人、 男人 這兩個對象的話,那是不是還得在 Test 對象里面在添加兩個對應的執行方法?答案是肯定的,不然沒地方執行呀。
- 所以說,這就是 多態 的特點。
有興趣的同學還可以用 es6 語法的 class、extends 來寫一下上述的例子,我這里就不在贅述了。
我們說了這么多,主要是想講述一下在 JavaScript 中,是怎么體現這些思想的,我最想說的一句話就是:JavaScript 是基于 原型 的語言,也希望大家能一直記住這句話。
而 Java 呢,是基于 類(class) 的語言,這兩種語言從語法上就有本職的區別,但是概念性的東西,是不會變的。我們應該拋開語言層面,更進一步的去學習面向對象的概念,然后在從語言上下手,學習如何實現這一概念。
啊哈,對了,突然想到,雖然現在對于前端來說,JavaScript 挺重要的,但是我們大多數同學在開發時,會用到三大框架的其中之一(react、vuee、angular),也有可能都用過,我們大多數人把關注點都放在了 JavaScript,而忽略了我們的 html + css 也是有 面向對象 概念的。今天就拿 vue 中的組件概念,來簡單說說 模板 是如何實現 面向對象 的。
不知道用 vue 做過開發的同學們,記不記得組件有個概念叫 動態組件[斜眼笑]。對,就是那個 <component></component>,需要通過 is 屬性來加載不同的組件。這里就不在具體舉例子講這個東西怎么用了,但是它就是 多態 的一種的實現形式,component 不會關心你的組件都有什么(作用相當于上述js例子的test函數),知道你傳過來的數據,我能匹配上,找到你想要的那個結果就好了。官方舉的例子是關于 tab 進行動態切換時,不管加幾個標簽,對本身功能并不會有影響,只需要多建幾個模板就好了。
好了,就講這么多吧,對 Java 感興趣的同學,可以根據上述的例子,用 Java 來寫一下,體驗一下 面向對象、多態 的快感,啊哈哈~~~
轉載于:https://www.cnblogs.com/liuqiuyue/p/11216951.html
總結
以上是生活随笔為你收集整理的深入理解面向对象 -- 基于 JavaScript 实现的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 生成ftp文件的目录树
- 下一篇: 从数论中的原理来说算法