前端獵奇系列之探索Python來反補JavaScript——上篇

原始碼終結者發表於2018-11-24

寫在最前

人生苦短,我用 JavaScript。 然鵝,其他圈子裡還流行這樣一句話:人生苦短,我用 Python。 當然還有什麼完美秀髮程式設計,這就不提了。當你在前端學到一定地步的時候,你會有種想出去看一看的衝動,翻過這座山,去山的外面,看看外面的風景。雖然外面的風景也就那樣,但是還是忍不住去看一看(%E8%B4%B1%E5%91%97%0A)。

多說一句

作為一個不安分的 FEE,我表示就算學了忘,我還是會學的。學了忘,和從來沒有學過,是完全不同的概念。我記得有一個成語是這樣的,叫 觸類旁通

通常我們會這樣理解,我舉個例子,比如我學習 JavaScript,我通過學習 JavaScript 來慢慢通曉其他類似的語言。但是我覺得我們還可以換種角度理解,比如我 JavaScript 學習的很好了,我想去學習 Python ,通過學習 Python 來反補我的 JavaScript ,通過 Python 的學習,使我對 JavaScript 的思考變得更深了。比如,import等關鍵詞在 Python 中早就有了。其實我想說的是,當你學習的內容越廣泛的時候,涉獵的知識越多的時候,看待事情的格局越高的時候,你會發現,很多問題,都可以用 萬變不離其宗 來解決。嗯….這哪是多說一句,明明就是一段。。。。

恩恩,,開始探索了啦!!

出自一家門

我喜歡把其他所學的知識和前端的知識進行比較。在整個較為系統(看慕課視訊和閱讀好書)的學習了 Python 後。其實收穫還是挺多的。

其實 PYJS 有很多相同的地方,一個最重要的相同點就是: JSPY 都是解釋型語言。解釋型語言俗稱指令碼語言,翻開程式語言的歷史,會發現 Perl 語言的誕生是指令碼語言走向成熟的標誌,有興趣可以自行查閱資料去了解,如果想深入瞭解程式語言原理,可以看《程式語言原理》第10版,作者是賽巴斯塔。寫的非常棒!別問我為什麼,因為我以前看過了,嘻嘻。

其實解釋型語言和 Java 這種編譯型語言本質的區別就是如下一段話:

解釋型語言是不需要開發者進行編譯的,是在執行程式時才被翻譯成機器語言。而Java這種語言是需要開發者去手動編譯的,究竟哪個好呢,其實沒法去比較,要是說效能,肯定是Java的效能更好,因為一次編譯後,後面多次執行就可以直接執行位元組碼檔案,不需要再次編譯了。而JavaScriptPython這種語言,雖然效能差了點,每次執行都需要進行編譯,但是開發效率高啊,可移植性也非常好。給它一個rumtime,它送你一個快樂的人生。 而且,我是指令碼語言,我慢咋了?沒聽過電腦科學領域有一句非常 NB 的名言麼:

電腦科學領域的任何問題都可以通過增加一個間接的中間層來解決。

根據這個理論,完全可以找到一個解決解釋型語言執行慢的方法,那就是JIT compilation,翻譯一下就是即時編譯,用一箇中間層,把翻譯的機器碼儲存下來,等下次再次呼叫的時候,就直接從快取中執行之前就已經編譯好的機器碼。

JS一直在吸收PY的優點

作為指令碼行業的翹楚,PY一直扮演著高貴的角色,PY的設計哲學是優雅、明確、簡單。而回頭望一眼當初用了10天就發明出來的JS,我發現JS就好像一個白手起家的屌絲(人窮志不窮)。通過農村包圍城市的方法,一步一個腳印,最終殺出一片屬於自己的天空。

為什麼要這樣說呢,是因為我個人理解的,JS在被創造出來的時候,並沒有給與太大期望,這也導致了很多東西在創造出來的時候沒有考慮到,比如JS是基於面對物件進行設計的,但是卻沒有類、繼承等面對物件的語言所必須擁有的特性。而PY從被設計之初就已經是一門面對物件的語言了。我舉個例子,JS中的this,有多種指向,對於剛入門前端的同學,是一個很難理解的知識點。縱觀整個OOP(面對物件程式設計)語言,this的指向都很明確,指向由類建立出來的物件。而在JS中,正是因為設計之初的定位模稜兩可,說是OOP,缺沒有實現類等特性,但又是基於面對物件進行設計的。

既然沒有在語言設計層面給出相應的關鍵字,例如class關鍵字,如果沒有類,那JS還有什麼,那就都是函式了丫?,OOP的不明顯,那剩下的只能OPP(程式導向)了呀?。那這樣一來,this的指向就會變得有多種情況,具體情況就不說了,自行查閱資料。但是呢,是時候拿出名言了:

電腦科學領域的任何問題都可以通過增加一個間接的中間層來解決。

你說沒有class關鍵字 ? 無法實現繼承? 無法實現多型性?不存在的,我就算餓死,我也會把這個實現了。嗯….真香~~ 然後就出現了一開始的使用原型鏈來實現繼承,同時為了減少記憶體消耗,可以優化成組合繼承。其實這一切的一切,都是為了填當初設計時沒有規劃好的坑,前同事挖坑,我來填。再後面,等ES6出來的時候,終於可以使用class來寫OOP了。但是這個class也只是語法糖,說白了同事留下的坑,已經深入地心了!全世界都在用,我能怎麼辦?我內心謊的一筆,不能直接從根本上進行修改,那隻能採取靠上面那句名言了,我造一個間接的中間層來實現class功能,其他的底層我都不動,向下相容,畢竟使用者是全世界。所以日子久了,你造一箇中間層,我造一箇中間層,然後JS變成的越來越優雅、明確、簡潔。日子就越來越好了。

在全世界的FEE的努力下,JS一直向優雅、明確、簡潔的方向上努力前進。

JS是如何吸收PY的

吸星大法也是分等級的,吸的不好,可能會炸。

第一個關於分號這個事情:

編寫PY程式碼時不加分號是一個標準,使用換行符作為行程式碼結束標誌。目前JS主流的框架都提倡不加分號。目的很簡單:簡單高效。JS日後的標準。

第二個關於關鍵字

PYJS都使用import作為模組匯入關鍵詞

第三個關於函式

都有閉包、匿名函式,都可以使用lamada表示式。

這裡說一下,其實有些其他語言也有,我學習PY的目的是為了反補JS,讓我換個角度去看我的小可愛JS。其實在前端,很多人搞不清楚閉包。知其然,不能知其所以然。為什麼會出現這種情況呢?

我個人認為,最主要的原因是因為閉包在JS語言中扮演著很重要的角色,說開點就是用到閉包的地方和場景太多了,很多場景必須使用閉包才能完成,然後呢閉包在很多場景的作用也不一樣。很多剛入貴圈沒多久的寶寶,還只停留在很淺的理解上。比如防止全域性變數汙染、模組化等。並不能深入的理解到閉包在JS中的重要作用。想透徹掌握閉包,那編譯語言原理是肯定要掌握或者瞭解的。

閉包在JS中有多重要,我個人認為:閉包在很多語言中都存在,但JS對閉包的依賴,超過其他任何語言對閉包的依賴。為什麼這麼說呢?我們把格局放的大一點:從程式語言原理的角度來看來閉包,閉包其實就是:

一個子程式和定義它的引用環境。也就是如果子程式可以從程式的任意位置呼叫,就需要引用環境。

中斷一下,先不看閉包,你會發現有個詞很陌生,叫引用環境。其實這裡的引用環境也叫作用域。不同叫法而已,那麼問題來了,引用環境(作用域)是什麼東東?

引用環境(作用域)是指這條語句中所有可見變數的集合。

怎麼理解這句話,其實這句話對你理解前端經常提的作用域非常非常重要,請看一個非常簡單的程式碼:

const g = 'haha'const a = 'i am godkun'function fun() { 
let b = 'hello world' console.log(b + a)
}fun()複製程式碼

OK,看上面程式碼,fun()語句的作用域是什麼?按照靜態作用域語言的特性,你要去fun函式的程式碼執行處去看,通過fun函式可以知道,fun函式的作用域(引用環境)就是fun函式體內的區域性作用域中宣告的的變數b和全域性作用域下的變數a的集合,這裡強調一點,就算全域性變數g沒有被fun函式使用。那也是算在fun函式的引用環境裡的。

總結一下就是: 在靜態作用域語言中,語句的引用環境是在它的區域性作用域中宣告的變數,和在它的祖先作用域中宣告的所有可見變數的集合。

好了,繼續開始閉包吧,也就是如果作用域為靜態的程式語言不允許巢狀子程式,那閉包就沒有什麼用。如果允許巢狀子程式,那就支援閉包。這種允許巢狀子程式的語言中,子程式引用環境中的所有變數(其本地變數和全域性變數)都是可訪問的,無論子程式在程式的什麼地方呼叫。 這句話不好理解,我們可以以JS為例子進行通俗闡述:

JS是一個靜態作用域語言,同時允許巢狀子程式,如果JS不允許巢狀子程式,那一首涼涼送給小可愛JS啊。什麼是靜態作用域語言,官方解答就不說了,我通俗點說,就是你的JS程式在宣告時,就已經確定好作用域了。學習過程式語言原理的應該知道 靜態作用域又叫做詞法作用域,使用詞法作用域的變數叫詞法(lexical)變數。

function say() { 
let str = 'hello world' console.log(str)
} 複製程式碼

從上面可以知道,變數str就是詞法變數。那麼最核心的本質要出來了。

詞法變數都有一個確定的作用域和不確定的生存期。

詞法變數的作用域可以是一個函式或block,使得其在這段程式碼區域內都有效。自從JS支援了塊級作用域(let宣告的也不能算真正意義上的塊級作用域,不說這個了),這個block也就成為了現實。不過為什麼說詞法變數的生存期不確定呢,是因為詞法變數的生存期取決於該變數需要引用多久。而引用多久,這是我們可以人為控制的。而人為控制總是會不靠譜的。所以就誕生了 GC 這種神器。

你會發現,從上面的靜態作用域可以知道,應該還存在動態作用域。動態作用域是什麼呢,不說了!,畢竟和JS無關。我只說我反補的!說到這,我是不是應該寫個閉包文章,算了吧,以後再說吧。

發現說不完了

一本來想一篇搞定的,發現寫著寫著收不住了,一想到我今天9點(寫到這時間已1542994654097)還要參加VueConf,明天還要參加上海的谷歌開發者大會。然後刺激的是今天我要5點起床做高鐵,嗯,默默的在文章標題最後加了 ——上篇, 我還是收手睡覺吧。

其實我學習PY的目的是為了反補JS,通過PY來看清楚JS。其實學習服務端語言,對前端有一個很重要的幫助,就是可以深入理解Web程式設計中的關於網路方面的很多知識,TCP/IP 、 Socket 、HTTP等等,以及在web安全方面,服務端是如何做的。等等吧,下篇再說吧,當然還有我學習PY後的一次小實戰,也算是學有所用吧。雖然用途不大,但是有趣就好。

可能有人會說,Node.js不也是服務端語言麼,為什麼不學習Node.js,這個我要說一下,Node.js我也學丫。比如Node.js原始碼中就大量用到了閉包,非同步IO操作就用到了閉包。但是Node.js也是用JS寫的?,我是獵奇系列,所以Node.js 不考慮在內。

PY有裝飾器,JS沒有,但是偉大的轉譯器Babel可以解決這個問題,我覺得可以腦補一下,Babel這種神器以後甚至可以統一指令碼語言。按照Babel規定的語法規範寫,然後Babel通過對現有的程式碼進行分析,解析成AST,然後轉換,生成新的AST,然後再讓直譯器去解釋新的程式碼結構。

然後你懂的,根據命令來編譯出你想要的指令碼語言。PYJS結合為一體,最終變成了:

人生苦短,我愛 PS。 卒

兄得,如果你也愛 PS ,就點個贊吧,嘻嘻。歡迎關注,後續系列將更加精彩?!

來源:https://juejin.im/post/5bf7fb70e51d45452f2d4fe8#comment

相關文章