華文網

javascript王國的一次旅行,一個沒有類的世界怎麼玩轉物件導向?

1 前言

作為Java 帝國的未來繼承人,Java小王子受到了嚴格的教育, 不但精通Java語言、Java虛擬機器、java類庫和框架,還對各種官方的Java規範瞭若指掌。

近日他聽說一個叫做Javascript的屌絲逆襲了, 成功地建立了一個獨立的王國,

不但成了前端程式設計之王, 還不斷地蠶食Java帝國的領地 !

按照小王子宮廷老師的說法: 想當年, 這傢伙只是運行在流覽器中,完完全全是蹭了Java的熱度這才發展起來, 現在竟然回過頭來要欺負我們, 還有沒有天理了? 是可忍孰不可忍? !

小王子可不這麼認為, 存在必然是合理的,javascrip必有獨特之處, 俗話說知己知彼,百戰不殆,他覺得有必要去Javascript王國刺探一下,搜集一下情報,

看看這個曾經的流覽器中的物件導向語言是怎麼回事, 為什麼那麼多碼農趨之若鶩。

2 初步印象

喬裝打扮以後,小王子來到Javascript 王國,這裡看起來一派生氣勃勃的景象,人們隨性而奔放, 不像Java帝國那麼嚴肅而呆板, 讓人感覺心情愉悅。

不過令小王子感到不可思議的是, 這裡竟然沒有官方提供的類庫! 人們幹活用的工具五花八門,讓人眼花繚亂, 什麼AngularJS, React , Backbone,Vue, Ember,JQuery, ...... 互相之間還吵來吵去,

爭來爭去,煞是熱鬧。

對比這下,Java帝國有著嚴密的統治,有著官方提供的龐大類庫, 還有一統天下的Web框架 SSH/SSM ,再加上各種各樣的Java規範, 碼農們只需要拿來學習,幹活就行。

沒有了選擇的煩惱, 但同時也減少了選擇的權利, 是好還是壞? 小王子自己也不知道。

小王子還注意到Javascript王國的人寫程式幾乎沒人使用IDE, 找個趁手的文字編輯器就可以開工, 然後扔到流覽器中去運行測試,

真是羽量級啊! 唉, 我們Java帝國還在爭論IntelliJ IDEA和Eclipse孰優孰劣, 實在是沒有必要啊。

3 沒有類怎麼創建物件?

隨著調查的深入,小王子愈發覺得吃驚, 這裡竟然沒有類的概念! 一個物件導向的語言竟然沒有類! 這和小王子從出生就被灌輸的概念可是背道而馳!

沒有類怎麼創建物件 ? 小時候宮廷老師經常說: 先寫一個類, 然後才能從這個類new出一個物件出來 。

可是眼前卻有著無數的javascript物件, 他們在不斷地產生、消亡,

一起辛苦地工作,支撐起龐大的、生機勃勃的帝國。

這些物件是從哪裡來的? 小王子百思不得其解, 正值正午時分, 小王子看到前面有一家JSON酒館,決定先歇歇腳,美美地吃一頓再說。

小王子要了二斤熟牛肉,三碗酒,正要開始享用, 只聽到旁邊桌子的一個穿著長袍的人問道:哎,你說的那個物件的原型是什麼?

另一位戴眼鏡的則低聲說:噓,噤聲,國王剛頒佈命令,

原型法是我們帝國的秘密,禁止公開討論,以防被Java帝國給學了去。

小王子心中一動, 馬上把小二叫來,要來上等酒菜, 送到鄰桌,請兩位吃酒。 一番酒喝下來, 小王子終於獲得了兩人的初步信任, 原來他們還是負責審查javscript語言規範的官員。

小王子問道: “我家世代經商, 走南闖北,去過C++王國,Java帝國, C#帝國, 他們都是號稱物件導向的語言, 都有class 和 object的區分, 可是到了咱們javascript王國, 我怎麼連一個class 都沒有看到啊? ”

戴眼鏡的官員說: “我們不用class, 那玩意兒太不直觀了 !”

小王子暗暗稱奇, 可是仔細一想, 好像就是這樣啊, 想當初我學習Java的時候, 費了好大的勁才接受了class這個概念,實際上物件導向的系統,不就是物件之間的交互嗎? 要類幹什麼?

然後小王子問了一個關鍵問題: “沒有class, 怎麼創建物件啊”

“外鄉人, 沒那麼複雜,你想想什麼是物件啊,不就是屬性加上方法嗎? 你看看我們這就創建一個物件出來 ” 這位官員說著,手指頭沾著酒水在桌子上寫了起來:

看到沒有,這個animal物件定義了一個屬性name, 和一個方法 eat , 簡單吧?”

的確是簡單又明瞭,完全不需要class, 一個物件就創建了,小王子面前似乎打開了一扇新的大門。

“由於物件並不和類關聯, 我們可以隨意地給這個物件增加屬性:” 眼鏡官員補充到。

“還能這麼玩?!” 小王子被驚到了,沒有類的約束,這些物件也太自由了吧。

4 沒有類怎麼繼承?

“那繼承怎麼實現, 繼承可是物件導向的重要概念啊”

眼鏡官員說: “簡單啊,繼承不就是讓兩個物件建立關聯嘛! 在我們javascript王國,每個物件都有一個特殊的屬性叫做__proto__, 你可以用這個屬性去關聯另外一個物件(這個物件就是所謂的原型了) , 來我給你畫一下”

這段酒水寫成的代碼不長,但是卻深深地震撼了小王子, 因為其中信息量非常巨大,隱藏了“原型”的秘密, 小王子不由得陷入了深思:

物件dog 的原型是animal (注意:也是一個物件), 物件cat的原型也是animal 。

無論是dog還是cat ,都沒有定義eat()方法, 那怎麼可以調用呢?

當eat方法被調用的時候,先在自己的方法列表中尋找, 如果找不到,就去找原型中的方法, 如果原型中找不到, 就去原型的原型中去尋找...... 最後找到Object那裡, 如果還找不到, 那就是未定義了。

這裡的這幾個物件肯定是通過__proto__建立了一個原型鏈!

嗯, 我師父給我講JVM虛擬機器的時候, 也提到了一個物件在執行方法的時候,需要查找方法的定義,這個查找的次序也是先從本物件所屬的類開始, 然後父類, 然後父類的父類...... 直到Object, 思路是一模一樣的!

只不過Java 的方法定義是在class中, 而這個javascript 的方法就在物件裡邊, 現在我覺得似乎在物件裡更加直觀一點啊。

屬性和方法應該類似,也是沿著原型鏈向上查找, 不過這裡dog的name屬性似乎覆蓋了animal的name屬性, 還有那個this, 在調用dog.eat()的時候,應該是指向dog這個物件的。

看來物件導向的理念都是想通的啊。 想著想著,小王子臉上竟然露出了笑容。

看到小王子像程式卡住一樣,不動了, 穿長袍的官員推了小王子一把: 外鄉人, 你怎麼了?

小王子意識到自己的失態, 趕緊說: “哦,沒啥, 我覺得你們使用的這個’原型‘的辦法很精妙啊, 完全不用類就實現了繼承。”

眼鏡官員一愣: “外鄉人, 看來你悟性不錯, 帝國的秘密已經被你給洞察了, 不過很多新來的程式師就不容易體會到這一點, 於是我們就做了一個變通, 讓javascript可以像Java那樣new 出物件出來。說來慚愧, 這完全是為了遷就那些C++,Java, C#程式師啊 ”

5 向Java靠攏

小王子說:”什麼變通辦法? 難道你們也開始使用類了嗎?“

“不不, 我們提供了一個叫做構造函數的東西。還是給你寫點兒代碼吧 ” 官員說著,又蘸著酒水寫了起來:

小王子說道: “那個function 已經有點 class的感覺了啊, 天呐我竟然看到了this這個關鍵字, 對了那個Student是你故意寫的大寫嗎? ”

“是啊 , 這樣以來看起來就像Java的類了。但是,中間有個問題,你看出來了嗎? ”

小王子想了一陣:“ 是不是說每個新創建物件都有一個sayHello函數? 在Java中函數都是定義在class 上的。 如果定義物件上, 那就意味著每個物件都有一份, 太浪費了。”

“是的,所以我們得提供一種更加高效的辦法, 把這個sayHello函數放到另外一個地方去! ”

“放到哪裡? ”

“記得我們剛才說的原型鏈嗎? 當一個物件調用方法的時候,會順著鏈向上找,所以我們可以創建一個原型物件,其中包含sayHello函數, 讓andy, lisa這些從Student創建起來的物件指向這個原型就ok了。”

“可是你這裡只有構造函數Student, 在哪裡創建原型物件呢? 怎麼把andy,lisa 這些物件的__proto__指向原型物件呢? 不會讓我手工來指定吧。”

眼鏡官員瞪了一眼小王子說: “我們javascript帝國肯定不會這麼麻煩程式師的, 我們可以把這個原型物件放到Student.prototype這個屬性中(注意,不是__proto__), 這樣一來,每次當你創建andy,lisa這樣的物件時, javascript 就會自動的把原型鏈給建立起來!”

小王子面露難色:“唉,這理解起來有點難啊。”

"還是畫個圖吧, 當你去new Student的時候,javascript會建立這樣的關係鏈:"

小王子說: “明白了,這個所謂的構造函數Student 其實就是一個幌子啊, 每次去new Student的時候,確實會創建一個物件出來(andy或者lisa) , 並且把這個物件的原型(__proto__)指向 Student.prototype這個物件,這樣一來就能找到sayHello()方法了。”

眼鏡官員回答:“沒錯,這個地方容易讓人混淆的就是__proto__和prototype這兩個屬性, 唉,我也不知道最早為什麼這麼幹, 實在是不優雅。”

“是啊,這個構造函數再加上prototype的概念,實在是讓人費解, 所以我們商量著提供一點語法糖降低程式師的負擔。” 長袍官員附和到。

6 語法糖

聽到語法糖,小王子覺的很親切, 因為Java 中也提供了很多方便程式師的語法糖。

當長袍官員寫出javascript的語法糖的時候, 小王子不由得大吃一驚:

這語法糖已經把javascript變得非常像Java, C#,C++的類了, 看來javascript帝國為了“討好”程式師, 已經努力的在改變了, 我們java帝國看來得加油啊。

小王子現在明白了Javascript是一個基於原型實現的物件導向的語言, 根本沒有類的概念, 新的方式給小王子的思維觀念帶來了重大的衝擊。

在這裡待久了,他又瞭解到javascript強大的函數式程式設計,越來越喜歡javascript, 都有點樂不思蜀了。

小王子還會回到Java帝國嗎?

學習Java的同學注意了!!!

學習過程中遇到什麼問題或者想獲取學習資源的話,歡迎加入Java學習交流群495273252,我們一起學Java!

戴眼鏡的官員說: “我們不用class, 那玩意兒太不直觀了 !”

小王子暗暗稱奇, 可是仔細一想, 好像就是這樣啊, 想當初我學習Java的時候, 費了好大的勁才接受了class這個概念,實際上物件導向的系統,不就是物件之間的交互嗎? 要類幹什麼?

然後小王子問了一個關鍵問題: “沒有class, 怎麼創建物件啊”

“外鄉人, 沒那麼複雜,你想想什麼是物件啊,不就是屬性加上方法嗎? 你看看我們這就創建一個物件出來 ” 這位官員說著,手指頭沾著酒水在桌子上寫了起來:

看到沒有,這個animal物件定義了一個屬性name, 和一個方法 eat , 簡單吧?”

的確是簡單又明瞭,完全不需要class, 一個物件就創建了,小王子面前似乎打開了一扇新的大門。

“由於物件並不和類關聯, 我們可以隨意地給這個物件增加屬性:” 眼鏡官員補充到。

“還能這麼玩?!” 小王子被驚到了,沒有類的約束,這些物件也太自由了吧。

4 沒有類怎麼繼承?

“那繼承怎麼實現, 繼承可是物件導向的重要概念啊”

眼鏡官員說: “簡單啊,繼承不就是讓兩個物件建立關聯嘛! 在我們javascript王國,每個物件都有一個特殊的屬性叫做__proto__, 你可以用這個屬性去關聯另外一個物件(這個物件就是所謂的原型了) , 來我給你畫一下”

這段酒水寫成的代碼不長,但是卻深深地震撼了小王子, 因為其中信息量非常巨大,隱藏了“原型”的秘密, 小王子不由得陷入了深思:

物件dog 的原型是animal (注意:也是一個物件), 物件cat的原型也是animal 。

無論是dog還是cat ,都沒有定義eat()方法, 那怎麼可以調用呢?

當eat方法被調用的時候,先在自己的方法列表中尋找, 如果找不到,就去找原型中的方法, 如果原型中找不到, 就去原型的原型中去尋找...... 最後找到Object那裡, 如果還找不到, 那就是未定義了。

這裡的這幾個物件肯定是通過__proto__建立了一個原型鏈!

嗯, 我師父給我講JVM虛擬機器的時候, 也提到了一個物件在執行方法的時候,需要查找方法的定義,這個查找的次序也是先從本物件所屬的類開始, 然後父類, 然後父類的父類...... 直到Object, 思路是一模一樣的!

只不過Java 的方法定義是在class中, 而這個javascript 的方法就在物件裡邊, 現在我覺得似乎在物件裡更加直觀一點啊。

屬性和方法應該類似,也是沿著原型鏈向上查找, 不過這裡dog的name屬性似乎覆蓋了animal的name屬性, 還有那個this, 在調用dog.eat()的時候,應該是指向dog這個物件的。

看來物件導向的理念都是想通的啊。 想著想著,小王子臉上竟然露出了笑容。

看到小王子像程式卡住一樣,不動了, 穿長袍的官員推了小王子一把: 外鄉人, 你怎麼了?

小王子意識到自己的失態, 趕緊說: “哦,沒啥, 我覺得你們使用的這個’原型‘的辦法很精妙啊, 完全不用類就實現了繼承。”

眼鏡官員一愣: “外鄉人, 看來你悟性不錯, 帝國的秘密已經被你給洞察了, 不過很多新來的程式師就不容易體會到這一點, 於是我們就做了一個變通, 讓javascript可以像Java那樣new 出物件出來。說來慚愧, 這完全是為了遷就那些C++,Java, C#程式師啊 ”

5 向Java靠攏

小王子說:”什麼變通辦法? 難道你們也開始使用類了嗎?“

“不不, 我們提供了一個叫做構造函數的東西。還是給你寫點兒代碼吧 ” 官員說著,又蘸著酒水寫了起來:

小王子說道: “那個function 已經有點 class的感覺了啊, 天呐我竟然看到了this這個關鍵字, 對了那個Student是你故意寫的大寫嗎? ”

“是啊 , 這樣以來看起來就像Java的類了。但是,中間有個問題,你看出來了嗎? ”

小王子想了一陣:“ 是不是說每個新創建物件都有一個sayHello函數? 在Java中函數都是定義在class 上的。 如果定義物件上, 那就意味著每個物件都有一份, 太浪費了。”

“是的,所以我們得提供一種更加高效的辦法, 把這個sayHello函數放到另外一個地方去! ”

“放到哪裡? ”

“記得我們剛才說的原型鏈嗎? 當一個物件調用方法的時候,會順著鏈向上找,所以我們可以創建一個原型物件,其中包含sayHello函數, 讓andy, lisa這些從Student創建起來的物件指向這個原型就ok了。”

“可是你這裡只有構造函數Student, 在哪裡創建原型物件呢? 怎麼把andy,lisa 這些物件的__proto__指向原型物件呢? 不會讓我手工來指定吧。”

眼鏡官員瞪了一眼小王子說: “我們javascript帝國肯定不會這麼麻煩程式師的, 我們可以把這個原型物件放到Student.prototype這個屬性中(注意,不是__proto__), 這樣一來,每次當你創建andy,lisa這樣的物件時, javascript 就會自動的把原型鏈給建立起來!”

小王子面露難色:“唉,這理解起來有點難啊。”

"還是畫個圖吧, 當你去new Student的時候,javascript會建立這樣的關係鏈:"

小王子說: “明白了,這個所謂的構造函數Student 其實就是一個幌子啊, 每次去new Student的時候,確實會創建一個物件出來(andy或者lisa) , 並且把這個物件的原型(__proto__)指向 Student.prototype這個物件,這樣一來就能找到sayHello()方法了。”

眼鏡官員回答:“沒錯,這個地方容易讓人混淆的就是__proto__和prototype這兩個屬性, 唉,我也不知道最早為什麼這麼幹, 實在是不優雅。”

“是啊,這個構造函數再加上prototype的概念,實在是讓人費解, 所以我們商量著提供一點語法糖降低程式師的負擔。” 長袍官員附和到。

6 語法糖

聽到語法糖,小王子覺的很親切, 因為Java 中也提供了很多方便程式師的語法糖。

當長袍官員寫出javascript的語法糖的時候, 小王子不由得大吃一驚:

這語法糖已經把javascript變得非常像Java, C#,C++的類了, 看來javascript帝國為了“討好”程式師, 已經努力的在改變了, 我們java帝國看來得加油啊。

小王子現在明白了Javascript是一個基於原型實現的物件導向的語言, 根本沒有類的概念, 新的方式給小王子的思維觀念帶來了重大的衝擊。

在這裡待久了,他又瞭解到javascript強大的函數式程式設計,越來越喜歡javascript, 都有點樂不思蜀了。

小王子還會回到Java帝國嗎?

學習Java的同學注意了!!!

學習過程中遇到什麼問題或者想獲取學習資源的話,歡迎加入Java學習交流群495273252,我們一起學Java!