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