国产99福利精品视频|杨幂精品国产福利在线|国精品午夜福利视频不卡|欧美精品黄网站在线播放|精品国产乱码久久久久久久|国产大片中文字幕在线观看|国产肥熟女视频一区二区三区|国产乱码无码视频一区二区三区

從Sun離職后,我“拋棄”了Java,擁抱JavaScript和Node

來源:|發(fā)布時間:2018-10-19 16:04:19

我是前 Sun 公司 Java SE 團隊的一名成員,在工作了 10 多年之后——2009 年 1 月——也就是在甲骨文收購 Sun 公司之前,我離開了公司,然后迷上了 Node.js.

  我對 Node.js 的癡迷到了怎樣的程度?自 2010 年以來,我撰寫了大量有關(guān) Node.js 編程的文章,出版了四本與 Node.js 開發(fā)有關(guān)的書籍,以及與 Node.js 編程有關(guān)的其他書籍和眾多教程。

  在 Sun 公司工作期間,我相信 Java 就是一切。我在 JavaONE 上發(fā)表演講,共同開發(fā)了 java.awt.Robot 類,組織 Mustang 回歸競賽(Java 1.6 版本的漏洞發(fā)現(xiàn)競賽),協(xié)助推出了“Java 發(fā)行許可”,這在后來的 OpenJDK 項目啟動過程中起到了一定的作用。我在 java.net(這個網(wǎng)站現(xiàn)已解散)上每周寫一到兩篇博文,討論 Java 生態(tài)系統(tǒng)中所發(fā)生的主要事件,并堅持了 6 年。這些博文的主要主題是關(guān)于“保衛(wèi)”Java,因為總有人在預(yù)言 Java 的“死期”。

  在這篇文章中,我將會解釋我這個 Java 死忠是如何變成一個 Node.js 和 JavaScript 死忠的。

  但其實我并沒有完全脫離 Java。在過去的三年中,我編寫了大量 Java/Spring/Hibernate 代碼。但兩年的 Spring 編碼經(jīng)歷讓我明白了一個道理:隱藏復(fù)雜性并不會帶來簡單性,它只會產(chǎn)生更多的復(fù)雜性。

  Java 已成為一種負擔(dān),Node.js 編程卻充滿了樂趣

  有些工具是設(shè)計師花費數(shù)年磨礪和精煉的結(jié)果。他們嘗試不同的想法,去掉不必要的屬性,最終得到一個只帶有恰到好處屬性的工具。這些工具的簡潔性甚至達到讓人感到驚艷的程度,但 Java 顯然不屬于這一類。

  Spring 是一個非常流行的用于開發(fā) Java Web 應(yīng)用程序的框架。Spring(特別是 Spring Boot)的核心目的是成為一個易于使用的預(yù)配置的 Java EE 棧。Spring 程序員不需要直接接觸 Servlet、數(shù)據(jù)持久化、應(yīng)用程序服務(wù)器就可以獲得一個完整的系統(tǒng)。Spring 框架負責(zé)處理所有這些細節(jié),你只需要把精力放在業(yè)務(wù)編碼上。例如,JPA Repository 類為“findUserByFirstName”方法合成數(shù)據(jù)庫查詢——你不需要編寫任何查詢代碼,只需按照特定方式給方法命名,并添加到 Repository 中即可,Spring 將負責(zé)處理其余的部分。

  這原本是一個偉大的故事,一種很好的體驗,但其實并不然。

  當(dāng)你遇到 Hibernate 的 PersistentObjectException 時,你知道是哪里出了問題嗎?你可能花了幾天時間才找到問題所在,導(dǎo)致這個異常的原因是發(fā)給 REST 端點的 JSON 消息里帶有 ID 字段。Hibernate 想要自己控制 ID 值,所以拋出了這個令人感到困惑的異常。看,這就是過度簡化所帶來的惡果。除了這個,還有其他成千上萬個同樣令人感到困惑的異常。在 Spring 棧中,一個子系統(tǒng)套著另一個子系統(tǒng),它們坐等你犯錯,然后再拋出應(yīng)用程序崩潰異常來懲罰你。

  然后,你會看到滿屏的堆棧跟蹤信息,里面滿是這樣那樣的抽象方法。面對這種級別的抽象,顯然需要更多的邏輯才能找到你想要的內(nèi)容。如此多的堆棧跟蹤信息不一定是不好的,但它也是在提醒我們:這在內(nèi)存和性能方面的開銷究竟有多大?

  而零代碼的“findUserByFirstName”方法又是如何被執(zhí)行的?Spring 框架必須解析方法名稱,猜測程序員的意圖,構(gòu)造類似抽象語法樹的東西,生成一些 SQL 語句……那么完成這個過程需要多少開銷?

  在反反復(fù)復(fù)經(jīng)歷這樣的過程之后,在花了大量時間學(xué)習(xí)你本不該學(xué)習(xí)的東西之后,你可能會得出相同的結(jié)論:隱藏復(fù)雜性并不會帶來簡單性,它只會產(chǎn)生更多的復(fù)雜性。

  另一面是 Node.js

  Spring 和 Java EE 非常復(fù)雜,而 Node.js 卻是一股清流。首先是 Ryan Dahl 在核心 Node.js 平臺上所應(yīng)用的設(shè)計美學(xué)。他追求別樣的東西,花了數(shù)年時間磨練和改進了一系列核心的 Node.js 設(shè)計理想,最終得到一個輕量級的單線程系統(tǒng)。它巧妙地利用了 JavaScript 匿名函數(shù)進行異步回調(diào),成為一個實現(xiàn)了異步機制的運行時庫。

  然后是 JavaScript 語言本身。JavaScript 程序員似乎更喜歡無樣板的代碼,這樣他們的意圖才能發(fā)揮作用。

  我們可以通過實現(xiàn)監(jiān)聽器的例子來說明 Java 和 JavaScript 之間的差別。在 Java 中,監(jiān)聽器需要實現(xiàn)抽象接口,還需要指定很多啰七八嗦的細節(jié)。程序員的意圖的這些繁瑣的樣板中漸漸淹沒。

  而在 JavaScript 中,可以使用最簡單的匿名函數(shù)——閉包。你不需要實現(xiàn)什么抽象接口,只需要編寫所需的代碼,沒有多余的樣板。

  大多數(shù)編程語言都試圖掩蓋程序員的意圖,這讓理解代碼變得更加困難。

  但在 Node.js 中有一點需要注意:回調(diào)地獄。

  沒有完美的解決方案

  在 JavaScript 中,我們一直難以解決兩個與異步相關(guān)的問題。一個是 Node.js 中被稱為“回調(diào)地獄”的東西。我們很容易就掉入深層嵌套回調(diào)函數(shù)的陷阱,每個嵌套都會使代碼復(fù)雜化,讓錯誤和結(jié)果的處理變得更加困難。但 JavaScript 語言并沒有為程序員提供正確表達異步執(zhí)行的方式。

  于是,出現(xiàn)了一些第三方庫,它們承諾可以簡化異步執(zhí)行。這是另一個通過隱藏復(fù)雜性帶來更多復(fù)雜性的例子。

const async = require (‘a(chǎn)sync’);const fs = require (‘fs’);const cat = function (filez, fini) {  async.eachSeries (filez, function (filenm, next) {    fs.readFile (filenm, ‘utf8’, function (err, data) {      if (err) return next (err);      process.stdout.write (data, ‘utf8’, function (err) {        if (err) next (err);        else next ();      });    });  },  function (err) {    if (err) fini (err);    else fini ();  });};cat (process.argv.slice (2), function (err) {  if (err) console.error (err.stack);});

  這是個模仿 Unix cat 命令的例子。async 庫非常適合用于簡化異步執(zhí)行順序,但同時也引入了一堆模板代碼,從而模糊了程序員的意圖。

  這里實際上包含了一個循環(huán),只是沒有使用循環(huán)語句和自然的循環(huán)結(jié)構(gòu)。此外,錯誤和結(jié)果的處理邏輯被放在了回調(diào)函數(shù)內(nèi)。在 Node.js 采用 ES 2015 和 ES 2016 之前,我們只能做到這些。

  Node.js 10.x 中,等價的代碼是這樣的:

const fs = require (‘fs’) .promises;async function cat (filenmz) {  for (var filenm of filenmz) {    let data = await fs.readFile (filenm, ‘utf8’);    await new Promise ((resolve, reject) => {      process.stdout.write (data, ‘utf8’, (err) => {        if (err) reject (err);        else resolve ();      });    });  }}cat (process.argv.slice (2)).catch(err => {     console.error (err.stack); });

  這段代碼使用 async/await 函數(shù)重寫了之前的邏輯。雖然異步邏輯是一樣的,但這次使用了普通的循環(huán)結(jié)構(gòu)。錯誤和結(jié)果的處理也顯得很自然。這樣的代碼更容易閱讀,也更容易編碼,程序員的意圖也更容易被理解。

  唯一的瑕疵是 process.stdout.write 沒有提供 Promise 接口,因此用在異步函數(shù)中時需要丟 Promise 進行包裝。

  回調(diào)地獄問題并不是通過隱藏復(fù)雜性才得以解決的。相反,是語言和范式的演變解決了這個問題。通過使用 async 函數(shù),我們的代碼變得更加美觀。

  通過明確定義的類型和接口提升清晰度

  當(dāng)我還是 Java 的死忠時,我堅信嚴格的類型檢查對開發(fā)大型的應(yīng)用程序來說是有百利而無一害的。那個時候,微服務(wù)的概念還沒有出現(xiàn),也沒有 Docker,人們開發(fā)的都是單體應(yīng)用。因為 Java 具有嚴格的類型檢查,所以 Java 編譯器可以幫你避免很多錯誤——也就是說可以防止你編譯錯誤的代碼。

  相比之下,JavaScript 的類型是松散。程序員不確定他們收到的對象是什么類型,那么程序員怎么知道該怎么處理這個對象?

  但是,Java 的嚴格類型檢查同樣導(dǎo)致了大量樣板代碼。程序員經(jīng)常需要進行類型轉(zhuǎn)換,或以其他方式確保一切都準確無誤。程序員需要花很時間確保類型是準確的,所以使用更多的樣板代碼,希望通過及早捕獲和修復(fù)錯誤來節(jié)省時間。

  程序員不得不使用復(fù)雜的大型 IDE,僅僅使用簡單的編輯器是不行的。IDE 為 Java 程序員提供了一些下拉列表,用于顯示類的可用字段、描述方法的參數(shù),幫助他們構(gòu)建新的類和進行重構(gòu)。

  然后,你還得使用 Maven……

  在 JavaScript 中,不需要聲明變量的類型,所以通常不需要進行類型轉(zhuǎn)換。因此,代碼更易于閱讀,但可能會出現(xiàn)未編譯錯誤。

  這一點會讓你更喜歡 Java 還是痛恨 Java,取決于你自己。十年前,我認為 Java 的類型系統(tǒng)值得我們花費額外的時間,因為這樣可以獲得更多的確定性。但在今天,我認為代價太大了,使用 JavaScript 會要簡單得多。

  使用易于測試的小模塊來掃除 bug

  Node.js 鼓勵程序員將程序劃分為小單元,也就是模塊。模塊雖小,卻能從一定程度上解決剛剛提到的問題。

  一個模塊應(yīng)該具備以下特點:

  • 自包含——將相關(guān)代碼打包到一個單元中;

  • 強壯的邊界——模塊內(nèi)部的代碼可以防止外部代碼入侵;

  • 顯式導(dǎo)出——默認情況下,代碼和模塊中的數(shù)據(jù)不會導(dǎo)出,只將選定的函數(shù)和數(shù)據(jù)暴露給外部;

  • 顯式導(dǎo)入——聲明它們依賴哪些模塊;

  • 可能是獨立的——可以將模塊公開發(fā)布到 npm 存儲庫或其他私有存儲庫,方便在應(yīng)用程序之間共享;

  • 易于理解——更少的代碼意味著更容易理解模塊的用途;

  • 易于測試——小模塊可以輕松進行單元測試。

  所有這些特點組合在一起,讓 Node.js 模塊更容易測試,并具有明確定義的范圍。

  人們對 JavaScript 的恐懼源自它缺乏嚴格的類型檢查,所以可能很容易導(dǎo)致錯誤。但在具有清晰邊界的模塊中,受影響代碼被限于模塊內(nèi)部。所以,大多數(shù)問題被安全地隱藏在模塊的邊界內(nèi)。

  松散類型問題的另一個解決方案是進行更多的測試。

  你必須將節(jié)省下來的一部分時間(因為編寫 JavaScript 代碼更容易)用在測試上。你的測試用例必須捕獲編譯器可能捕獲的錯誤。

  對于那些想要在 JavaScript 中使用靜態(tài)檢查類型的人,可以考慮使用 TypeScript。我沒有使用 TypeScript,但聽說它很不錯。它與 JavaScript 兼容,同時提供了有用的類型檢查和其他特性。

  但我們的重點是 Node.js 和 JavaScript。

  包管理

  一想起 Maven 我就頭大。據(jù)說一個人要么愛它,要么鄙視它,沒有第三種選擇。

  問題是,Java 生態(tài)系統(tǒng)中并沒有一個核心的包管理系統(tǒng)。Maven 和 Gradle 其實也很不錯,但它們并不像 Node.js 的包管理系統(tǒng)那樣有用、可用和強大。

  在 Node.js 世界中,有兩個優(yōu)秀的包管理系統(tǒng),首先是 npm 和 npm 存儲庫。

  有了 npm,我們就相當(dāng)于有了一個很好的模式用來描述包依賴性。依賴關(guān)系可以是嚴格的(指定具體的版本),或者使用通配符表示最新版本。Node.js 社區(qū)已經(jīng)向 npm 存儲庫發(fā)布了數(shù)十萬個包。

  不僅僅是 Node.js 工程師,前端工程師也可以使用 npm 存儲庫。以前他們使用 Bower,現(xiàn)在 Bower 已被棄用,他們現(xiàn)在可以在 npm 存儲庫中找到所有可用的前端 JavaScript 庫。很多前端框架,如 Vue.js CLI 和 Webpack,都是基于 Node.js 開發(fā)的。

  Node.js 的另一個包管理系統(tǒng)是 yarn,它也是從 npm 存儲庫中拉取包,并使用與 npm 相同的配置文件。yarn 的主要優(yōu)點運行得更快。

  性能

  曾幾何時,Java 和 JavaScript 都因為運行速度慢而橫遭指責(zé)。

  它們都需要通過編譯器將源代碼轉(zhuǎn)換為由虛擬機執(zhí)行的字節(jié)碼。虛擬機通常會進一步將字節(jié)碼編譯為本地代碼,并使用各種優(yōu)化技術(shù)。

  Java 和 JavaScript 都有很大的動機讓代碼運行得更快。在 Java 和 Node.js 中,動機就是讓服務(wù)器端代碼運行得更快。而在瀏覽器端,動機是獲得更好的客戶端應(yīng)用程序性能。

  甲骨文的 JDK 使用了 HotSpot,這是一個具有多種字節(jié)代碼編譯策略的超級虛擬機。HotSpot 經(jīng)過高度優(yōu)化,可以生成非常快的代碼。

  至于 JavaScript,我們不禁在想:我們怎么能期望在瀏覽器中運行的 JavaScript 代碼能夠?qū)崿F(xiàn)復(fù)雜的應(yīng)用程序?基于瀏覽器 JavaScript 實現(xiàn)辦公文檔處理套件似乎是件不可能實現(xiàn)的事情?是騾子是馬,拉出來溜溜就知道了。這篇文章是我用谷歌文檔寫的,它性能非常好。瀏覽器端 JavaScript 的性能每年都在飛漲。

  Node.js 直接受益于這一趨勢,因為它使用的是 Chrome 的 V8 引擎。

  下面是 Peter Marshall 的演講視頻鏈接,他是谷歌的一名工程師,主要負責(zé) V8 引擎的性能增強工作。他在視頻中描述了為什么 V8 引擎使用 Turbofan 虛擬機替換了 Crankshaft 虛擬機。

  V8 引擎中的高性能 JavaScript:https://youtu.be/YqOhBezMx1o

  在機器學(xué)習(xí)領(lǐng)域,數(shù)據(jù)科學(xué)家通常使用R語言或 Python,因為他們十分依賴快速數(shù)值計算。但由于各種原因,JavaScript 在這方面表現(xiàn)很差。不過,有人正在開發(fā)一種用于數(shù)值計算的標(biāo)準 JavaScript 庫。

  JavaScript 中的數(shù)值計算:https://youtu.be/1ORaKEzlnys

  另一個視頻演示了如何通過 TensorFlow.js 在 JavaScript 中使用 TensorFlow。它提供了一個類似于 TensorFlow Python 的 API,可以導(dǎo)入預(yù)訓(xùn)練模型。它運行在瀏覽器中,可用于分析實時視頻,從中識別出經(jīng)過訓(xùn)練的對象。

  基于 JavaScript 的機器學(xué)習(xí):https://youtu.be/YB-kfeNIPCE

  在另一個演講視頻中,IBM 的 Chris Bailey 討論了 Node.js 的性能和可伸縮性問題,特別是在 Docker/Kubernetes 部署方面。他從一組基準測試開始,演示了 Node.js 在I/O吞吐量、應(yīng)用程序啟動時間和內(nèi)存占用方面遠遠超過 Spring Boot。此外,得益于 V8 引擎的改進,Node.js 每次發(fā)布的新版在性能方面都有顯著的提升。

  Node.js 的性能和高度可伸縮的微服務(wù):https://youtu.be/Fbhhc4jtGW4

  在上面的這個視頻中,Bailey 說我們不應(yīng)該在 Node.js 中運行計算密集型的代碼。因為 Node.js 采用了單線程模型,長時間運行計算密集型任務(wù)會導(dǎo)致事件阻塞。

  如果 JavaScript 的改進還無法滿足你的應(yīng)用程序的要求,還有其他兩種方法可以將本地代碼直接集成到 Node.js 中。最直接的方法是使用 Node.js 本地代碼模塊。Node.js 工具鏈中包含了 node-gyp,可用于處理與本地代碼模塊的鏈接。下面的視頻演示了如何集成 Rust 庫和 Node.js:

  JavaScript 與 Rust 集成,遠比你想象得簡單:https://youtu.be/Pfbw4YPrwf4

  WebAssembly 可以將其他語言編譯為運行速度非常快的 JavaScript 子集。WebAssembly 是一種可在 JavaScript 引擎內(nèi)運行的可執(zhí)行代碼的可移植格式。下面的視頻做了一個很好的概述,并演示了如何使用 WebAssembly 在 Node.js 中運行代碼。

  在 NodeJS 中使用 WebAssembly:https://youtu.be/hYrg3GNn1As

  富 Internet 應(yīng)用程序(RIA)

  十年前,軟件行業(yè)一直熱議利用快速的 JavaScript 引擎實現(xiàn)富 Internet 應(yīng)用程序,從而取代桌面應(yīng)用程序。

  這個故事實際上在二十多年前就已經(jīng)開始了。Sun 公司和 Netscape 公司達成了共識,在 Netscape Navigator 中使用 Java 小程序(Applet)。JavaScript 語言在某種程度上是作為 Java 小程序的腳本語言而開發(fā)出來的。服務(wù)器端有 Java Servlet,客戶端有 Java Applet,這樣就可以在兩端使用同樣的一門編程語言。然而,由于各種原因,這種美好的愿望并沒有實現(xiàn)。

  十年前,JavaScript 開始變得足夠強大,可以實現(xiàn)復(fù)雜的應(yīng)用程序。因此,RIA 被認為是 Java 客戶端應(yīng)用程序的終結(jié)者。

  今天,我們開始看到 RIA 的想法得以實現(xiàn)。服務(wù)器端的 Node.js 和兩端都有的 JavaScript 讓這一切成為可能。

  當(dāng)然,Java 作為桌面應(yīng)用程序平臺的消亡并不是因為 JavaScript RIA,而是因為 Sun 公司忽視了客戶端技術(shù)。Sun 公司把注意力放在要求快速服務(wù)器端性能的企業(yè)客戶身上。當(dāng)時我還在 Sun 公司任職,我親眼看著這件事情發(fā)生。真正殺死 Applet 的是幾年前在 Java 插件和 Java Web Start 中發(fā)現(xiàn)的一個安全漏洞。這個漏洞導(dǎo)致全球一致呼吁停止使用 Java Applet 和 Java Web Start 應(yīng)用程序。

  我們?nèi)匀豢梢蚤_發(fā)其他類型的 Java 桌面應(yīng)用程序,NetBeans 和 Eclipse IDE 之間的競爭仍然存在。但是,Java 在這個領(lǐng)域工作是停滯不前的,除了開發(fā)工具之外,很少有基于 Java 的應(yīng)用程序。

  JavaFX 是個例外。

  10 年前,JavaFX 意欲成為 Sun 公司對 iPhone 的反擊。它用于開發(fā)基于 Java 的手機 GUI 應(yīng)用程序,想把 Flash 和 iOS 應(yīng)用程序打垮。然而,這一切都沒有發(fā)生。JavaFX 現(xiàn)在仍然可以使用,但沒有了當(dāng)初的喧囂。

  這個領(lǐng)域的所有興奮點都發(fā)生在 React、Vue.js 和類似的框架上,JavaScript 和 Node.js 在很大程度上要得益于此。

  結(jié)論

  現(xiàn)在,開發(fā)服務(wù)器端應(yīng)用程序有很多選擇。我們不再局限于“P”開頭的語言(Perl、PHP、Python)和 Java,我們還有 Node.js、Ruby、Haskell、Go、Rust 等等。

  至于為什么我會轉(zhuǎn)向 Node.js,很明顯,我更喜歡在使用 Node.js 編程時的那種自由的感覺。Java 成了負擔(dān),而 Node.js 沒有這樣的負擔(dān)。如果我再次拿起 Java,那肯定是因為有人付了錢。

  每個應(yīng)用程序都有其真實需求。只是因為個人喜歡而一直使用 Node.js 也不見得是對的。在選擇一門語言或一個框架時總歸是有技術(shù)方面的考量的。例如,我最近完成的一些工作涉及 XBRL 文檔,由于最好的 XBRL 庫是用 Python 實現(xiàn)的,所以就有必要學(xué)習(xí) Python。


上一篇:甲骨文Java案仍在拉鋸 谷歌誓要向最高法院上訴
下一篇:Java全棧工程師“真硬核”帶你快速跳槽轉(zhuǎn)行

熱門話題

招生熱線: 4008-0731-86 / 0731-82186801

學(xué)校地址: 長沙市天心區(qū)團結(jié)路6號

Copyright © 2006 | 湖南大計信息科技有限公司 版權(quán)所有

湘ICP備14017520號-3

關(guān)注我們
在線咨詢
嘿,我來幫您!