整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          要深入 JavaScript,你需要掌握這 36 個概念

          文已經原作者授權

          你可能會經常聽到一些人在抱怨 JS 很奇怪,有時甚至是一文不值。之所以有這種想法,是因為他們不太了解 JS 背后的運作方式。我也覺得 JS 在某些情況處理方式與其它語言不太一樣,但這并不能怪它,它也只是以自己的方式展現給大家而已。

          如果,你熱愛一門編程語言,那么應該就會想深入了解并逐個掌握它的概念。

          這里列出了36個JavaScript概念,你需要掌握這些概念才能成為一個更懂 JS 的前端開發者。

          1.調用堆棧執行

          我們都知道堆棧溢出,但是你知道堆棧溢出是由什么原因導致的嗎? 堆棧溢出是與調用堆棧一些操作錯誤相關聯的。

          理解了調用堆棧,你就會清楚解像是JS 這們的編程語言是如何執行的。

          2. 原始數據類型

          const foo = "bar";
          foo.length; // 3
          foo === "bar"; // true

          這里,我們將值bar分配給常量foo時,它屬于原始類型string。這個每個人都知道。但是各位少俠想沒想過一個問題,string是基本數據類型,怎么能調用方法了?

          奇怪嗎? 不。

          這個特性稱為自動裝箱。每當讀取一個基本類型的時候,JS 后臺就會創建一個對應的基本包裝類型對象,從而讓我們能夠調用一些方法來操作這些數據。

          還是拿上面的例子開始:

          const foo = "bar";
          foo.length; // 3
          foo === "bar"; // true

          變量 foo 是一個基本類型值,它不是對象,它不應該有方法。但是 JS 內部為我們完成了一系列處理(即裝箱),使得它能夠調用方法,實現的機制如下:

          • 創建String類型的一個實例
          • 在實例上調用指定的方法
          • 銷毀這個實例
          const foo  = new String("bar");
          foo.length
          foo === 'bar'
          foo = null

          通過對原始數據類型有深入的了解,我們就應該知道這些“怪異”情況是如何發生的,以及它們背后的邏輯原因。

          3.值類型和引用類型

          最近,我對**“引用傳遞"** 在 JS 是怎么工作的感到困惑。盡管我知道 C 和 Java 等語言中有“按引用傳遞”和“按值傳遞”的概念,但是我不確定它在 JS 中如何工作。

          你是否知道分配給 JS 中非原始值的變量對該值的引用?引用指向存儲值的內存位置。。

          var arr1 = [1,2,3];
          var arr2 = arr1;
          arr2.push(10);
          console.log(arr2);
          //[1, 2, 3, 10]
          console.log(arr1);
          //[1, 2, 3, 10]

          上面的示例中可以看到,對arr2所做的任何修改也將體現在arr1上。這是因為它們僅保存值對應的內存地址的引用,而非值本身。

          通過了解值類型和引用類型的概念,你就會更好地了解如何為變量分配值和內存引用。

          4. 強制類型轉換

          這個概念主要解釋了隱式和顯式類型強制之間的區別。這是前端開發中對 JS 迷惑少數幾個領域之一。對于隱式強制轉換的概念尤其如此,因為它對不同的數據類型以不同的方式表現。

          這是 JS 面試中最常被考的。

          Number('789')   // 顯式
          +'789'          // 隱式
          789 != '456'    // 隱式
          9 > '5'         // 隱式
          10/null          // 隱式
          true | 0        // 隱式

          掌握了類型顯隱轉換,恭喜你對 JS 了解就進一步了。

          5. 比較運算符號 和 typeof 運算符

          雙等與三等,它們在大多數情況下在表面上看起來相同并且給出相同的結果,但是,它們有時候可能會給帶來意想不到的錯誤。

          為了了解這兩親兄弟的區別,我們可以借助 typeof 來查看被比較的值的類型。

          typeof 3 // "number"
          typeof "abc" // "string"
          typeof {} // "object"
          typeof true // "boolean"
          typeof undefined // "undefined"
          typeof function(){} // "function"
          typeof [] // "object"
          typeof null // "object"

          6. JavaScript 作用域

          作用域是 JS 中一個很重要的尷尬,JS 也一直在不斷完善自己的作用域。根據Wissam的說法,作用域的簡單定義是,編譯器在需要時查找變量和函數。

          了解作用域有助于我們有效地使用JavaScript。我們還需要了解全局作用域以及塊和函數作用域,也稱為詞法作用域。JS 作用域一開始接觸會感到很困惑,但是一旦你了解了事情的幕后原理,使用它就會非常令人興奮。

          7. 語句和聲明

          JavaScript 程序是一系列可執行語句的集合。所謂語句,就是一個可執行的單元,通過該語句的執行,從而實現某種功能。通常一條語句占一行,并以分號結束。默認情況下,JavaScript 解釋器按照語句的編寫流程依次執行。如果要改變這種默認執行順序,需要使用判斷、循環等流程控制語句。

          我們應該要知道 語句和聲明 的區別,這對我們全面了解 JS 是很有幫助的。

          8. 立即調用的函數表達式和模塊

          IIFE: Immediately Invoked Function Expression,意為立即調用的函數表達式,也就是說,聲明函數的同時立即調用這個函數。它主要用于避免污染全局作用域。后來,引入了ES6模塊,為避免全局作用域的污染提供了一種標準方法,盡管有人認為它不是IIFE的直接替代。

          通過理解IIFE和模塊,你可以構建較少由于全局空間處理不當而導致的錯誤的應用程序。當然,使用模塊,我們還可以做很多事情。

          9.消息隊列和事件循環

          正如MDN文檔所說,JavaScript 有一個基于事件循環的并發模型,事件循環負責執行代碼、收集和處理事件以及執行隊列中的子任務。這個模型與其它語言中的模型截然不同,比如 C 和 Java。

          在并發模型中,消息隊列用于處理最早的消息。只要有事件發生,便會加入消息隊列中。通過理解這些概念,你可以更好地理解JS在底層是如何工作的,以及知道你的代碼是如果運行的。

          10.時間間隔

          想在 JS 有計劃的調用的函數,可以使用下面兩個函數:

          • setTimeout 允許我們在特定時間間隔后運行一次函數。
          • setInterval允許我們重復運行一個函數,從一個特定的時間間隔開始,然后以該間隔連續重復。

          這些與前面的消息隊列和事件處理程序的概念有些關聯。因此,通過理解時間間隔方法,我們可以理解它們是如何工作的,并在我們的用例中有效地使用它們。

          11.JS 引擎

          JavaScript引擎是執行 JS 代碼的計算機程序或解釋器。JS 引擎可以用多種語言編寫。例如,驅動Chrome瀏覽器的V8引擎是用 c++ 編寫的,而驅動Firefox瀏覽器的SpiderMonkey引擎是用 C 和 c++編 寫的。

          要想編寫高效的代碼,你必須了解所使用的 JS 引擎。使用webview的移動開發人員要特別注意這一點。

          12.按位運算

          按位運算操作將值視為位(0和1),而不是十進制,十六進制或八進制數字。按位運算符對此類二進制表示形式執行其操作,但是它們返回標準JavaScript數值。

          通常,很少會在代碼中使用這些操作,但是它們確實有一些用例。比如,可以使用它們來查找偶數和奇數值,顏色轉換,顏色提取等等。

          通過全面了解這些按位操作,您可以很好地使用 WebGL 之類的技術,因為它包含許多像素操作。

          13. DOM 和布局樹

          我們大多數人都聽說過文檔對象模型(DOM),但只有少數人對此有深入的了解。你知道在瀏覽器中看到的不是DOM嗎?而是渲染樹,它實際上是DOM和CSSOM的組合。

          通過理解DOM的工作方式、結構以及頁面的渲染方式,我們就能夠在 JS 的幫助下動態地操作web頁面。這對于確保我們的應用程序具有高標準的性能尤為必要。

          14.類和工廠

          JavaScript 不是一種面向對象的語言。但是,為了模仿OOP屬性,使用了構造函數。根據Tania的說法,“ JavaScript中的類實際上并沒有提供其他功能,只是在原型和繼承上提供語法糖,因為它們提供了更簡潔,更優雅的語法。由于其他編程語言都使用類,因此 JS 中的類語法使開發人員在各種語言之間移動變得更加簡單。”

          工廠函數是不是返回對象的類或構造函數的函數。根據JS專家Eric Elliot的說法,“在JavaScript中,任何函數都可以返回一個新對象。如果它不是構造函數或類,則稱為工廠函數。”

          當開始開發規模更大的應用程序時,理解這兩個概念是很有必要的。

          15.this 關鍵字和 apply,call及bind方法

          就我個人而言,我認為對于一個JS開發人員來說,理解this 關鍵字是至關重要的。如果你不能正確地理解它,將來你開發的項目也會經常遇到this相關的問題。

          如果你對this關鍵字很清楚,則可以看看apply,call和bind方法,這些都可以解決 this 指向引發的問題。

          16.構造函數和 “instanceOf” 運算符

          構造函數就像常規函數一樣。但是它們有很多差異,函數名稱以大寫字母開頭,并且只能由new運算符執行。具有OOP開發經驗的程序員會熟悉new關鍵字。

          為了正確識別對象的類型,我們使用instanceOf運算符。簡單來說,它檢查一個對象是否是另一個對象的實例。

          這才助于你理解對象如何相互繼承,繼承是通過原型實現的。

          17.原型

          這是 JS 中最令人困惑的概念之一,即使對于有十年經驗的人來說也是如此。

          JavaScript中的原型是在對象之間共享通用功能的機制。JavaScript中幾乎所有對象都是Object的實例。對象會從Object.prototype繼承所有屬性和方法。

          簡單來說,原型是 JS 對象從中繼承方法和屬性的對象。

          理解了原型,你就可以構建高效,快速的應用程序。

          18. 使用 new,Object.create 和 Object.assign 創建對象

          創建對象有很多方法。但是,大都會選擇Object.create方法而不是new關鍵字。這是有原因的,因為 使用Object.create方法時,可以將現有對象用作新創建的對象的原型。這樣就可以重用現有對象的屬性和功能,有點像OOP中的繼承概念。

          使用Object.assign方法時,可以將可枚舉的自身屬性從一個或多個源對象復制到目標對象。在這種情況下,目標對象的原型不包含源對象的屬性。這是這兩種方法之間的主要區別。

          通過了解對象創建的這三種方式,可以根據實際情況適當地使用它們,以創建效率更高的程序。

          19.map,filter, reduce 方法

          當涉及到數組操作時,這三種方法非常有用。它們可以在Array原型中找到。

          如果你有一個數組,并且想對每個元素做一些事情,那么您可以使用map方法。

          如果你有一個數組,并且想通過某些條件來過濾一些值時,則可以使用filter方法。

          reduce() 方法對數組中的每個元素執行一個由你提供的reducer函數(升序執行),將其結果匯總為單個返回值。

          典型的例子就是對數組的所有元素進行求和:

          let numbers = [1,2,3,4,5,6]
          const reduced = numbers.reduce( (accumulator, currentValue) => accumulator + currentValue )
          console.log(reduced)
          // 21

          請注意,上述三種方法不會更改原始數組的值。

          20.純函數,副作用和狀態變更

          這三個概念對于 JS 開發人員而言非常重要,狀態變更對于使用 React 的開發人員尤其重要。

          純函數指的是一個函數的返回結果只依賴于它的參數,并且在執行過程里面沒有副作用。

          函數副作用是指當調用函數時,除了返回函數值之外,還對主調用函數產生附加的影響。副作用的函數不僅僅只是返回了一個值,而且還做了其他的事情,比如:

          • 修改了一個變量
          • 直接修改數據結構
          • 設置一個對象的成員
          • 拋出一個異常或以一個錯誤終止
          • 打印到終端或讀取用戶輸入
          • 讀取或寫入一個文件
          • 在屏幕上畫圖

          狀態變更是指你更改變量值的地方。如果你對變量進行更改,則可能會影響到其他函數,具體取決于變量被更改之前的值。在React環境中,建議我不要改變狀態。

          21. 閉包

          閉包很難理解。但是一旦理解,你會覺得 JS 其實也挺好的。在線上有足夠的資源。你花足夠的時間學習閉包,掌握理解它并不難。

          使用閉包可以訪問內部作用域中外部作用域的作用域。每次創建函數時都會在函數創建時創建JavaScript閉包。

          22. 高階函數

          高階函數是將其他函數作為參數或返回結果的函數。你可以創建僅負責一項任務的較小函數,然后在這些較小函數的幫助下構造復雜函數。這也會提交代碼的可重用性。

          23.遞歸

          遞歸是所有編程語言中的一個常見概念。簡單地說,遞歸就是把大問題分解成小問題,然后解決小問題一種思路。

          盡管遞歸可能是一個讓你頭疼的令人困惑的概念,但是通過大量的練習,從一些小問題開始,你可以更好地理解它。

          24.集合與生成器

          ES6 中新引入了集合和生成器。新引入的集合有Map,Set,WeakSet和WeakMap。這些集合為我們提供一些很方便的操作。了解它們的方式至關重要,尤其是對于現代JavaScript。

          生成器有時很難理解,特別是對于初學者。生成器允許我們編寫代碼函數,從而能夠暫停和重新啟動函數,而不會阻止其他代碼的執行,這在JavaScript中是很不常見的。

          25. Promise

          Jecelyn對 Promises 的解釋如下:“想象一下你是個孩子。你媽媽向你保證,她下周會買一部新手機給你。”

          你要到下周才能知道你是否能屋那部手機。你的媽媽要么真的給你買了一個全新的手機,要么因為不開心就不給你買。

          這算是一個承諾。一個 Promise 有三個狀態,分別是:

          1. Pending:你不知道你是否會能得到那個電話
          2. Fulfilled:媽媽高興了,給你買了一部新手機
          3. Rejected:老媽不開心了,就是不給買,愛咋滴就咋滳

          26.異步編程

          要了解什么是異步編程,首先要先積善成德什么是同步編程。同步編程是線程阻塞的,由于 JS 是單線程的,因此代碼將逐行執行。

          但是使用異步代碼,你可以執行一些比較耗時的任務。當你必須執行花費很長時間才能完成的多個任務時,此功能特別有用。但是在某些情況下,即使是需要執行很長時間的代碼,也可能需要用同步的方式,這時就可以使用async/await。

          27. ES6 箭頭函數

          箭頭函數是 ES6 的新增功能,是常規函數的語法替代。區別在于箭頭函數不綁定到this,arguments,super或new.target關鍵字。這使得箭頭函數在某些情況下是一個不錯的選擇,而在另一些情況下則是一個非常糟糕的選擇。

          因此,不要一上來就使用箭頭函數。需要根據你實際情況還使用它們。

          28. 數據結構

          無論使用哪種編程語言,數據結構都是開發人員應具備的基本知識之一。

          糟糕的程序員擔心代碼,好的程序員擔心數據結構和它們之間的關系。

          數據結構方面,你應該了解鏈表,隊列,堆棧,樹,圖和哈希表。

          29.時間復雜度

          不管編程語言如何,時間復雜度分析也是計算機編程的另一個基礎。為了構建更好的應用程序,你應該編寫更好的解決方案。為此,你需要了解時間復雜度的概念。有時也稱為BigO。

          30.算法

          這也是在計算機基礎課程中首先要教的內容之一。簡而言之,算法是逐步實現目標的過程。程序員應該能夠從算法的角度看任何問題。

          盡管有成千上萬個用例的大量算法,但是下面兩個很常見:

          • 查找
          • 排序

          這兩個用例對程序員來說是非常常見的,至少應該了解實現它們的已知算法。沒有固定的規則規定你應該使用這些算法之一,但是這些算法在性能方面是眾所周知的,并且有很好的文檔證明。

          你甚至可以創建自己的算法,并將其介紹給世界。如果它比目前已知的算法更好,你可能會成為下一個編程明星

          31.繼承,多態和代碼重用

          JS 中的繼承可用于原型來實現。這是因為 JS 是非OOP語言。但是 JS 通過提供原型繼承來提供OOP的某些功能。

          多態是對象、變量或函數可以采用多種形式的概念。在 JS 中,要看到多態的效果有點困難,因為在靜態類型的系統中,多態的經典類型更明顯。

          以上兩個概念都可以幫助我們在 JS 中實現更好代碼重用。

          32.設計模式

          設計模式(Design pattern)代表了最佳的實踐,通常被有經驗的面向對象的軟件開發人員所采用。設計模式是軟件開發人員在軟件開發過程中面臨的一般問題的解決方案。這些解決方案是眾多軟件開發人員經過相當長的一段時間的試驗和錯誤總結出來的。

          33. 函數式編程

          函數式編程是一種編程范式,是一種構建計算機程序結構和元素的風格,它把計算看作是對數學函數的評估,避免了狀態的變化和數據的可變。

          你需要掌握函數式編程的幾個概念:

          • 純函數
          • 不可變
          • 引用透明性
          • 高階函數

          34. 簡潔代碼的原則

          無論使用哪種編程語言,這都是每個開發人員都應該掌握的一項基本技能。每種編程語言都有一套單獨的良好實踐。盡管這些“良好”做法是主觀的,并且在工作場所之間存在差異,但有些慣例被認為是“良好”。

          通過遵循這些代碼原則,可以確保每個人都可以閱讀和維護你的代碼。這也會幫助你和你的團隊在應用程序開發過程中順利合作。

          35. 解構賦值

          在ES6中引入了解構賦值操作符,它非常有用。對于相同的用例,它們比以前的實現更簡單、更有效。

          36. ES2020新特性

          編程的優點之一是,如果你不去不斷學習,你永遠不會成為該領域專家。編程語言會隨著時間不斷發展,因為每個主要版本中都引入了其他新的功能。

          這也說明了你對某個概念的專業知識很可能在將來的10年后會過期,因為會有更好的替代版本與版本更新一起發布。對于任何編程語言,這都是非常常見的情況。

          ES202 0發布了幾個新特性,包括可選鏈接、空值合并、動態導入等等。你必須學習這些新概念,以跟上快速變化的It世界。

          掌握一門語言需要多年的經驗和時間,但是知道要掌握什么會讓事情變得更容易,希望這 36 個概念能對你有所幫助。

          人才們的 【三連】 就是小智不斷分享的最大動力,如果本篇博客有任何錯誤和建議,歡迎人才們留言,最后,謝謝大家的觀看。


          作者:Mahdhi Rezvi 譯者:前端小智 來源:medium

          原文:https://medium.com/better-programming/36-javascript-concepts-you-need-to-master-to-become-an-expert-c6630ac41bf4

          于有人站出來,打算跟 JavaScript 生態系統正面交鋒了。這家伙知道自己在干什么,而且也描繪出了干掉 JS 之后要創造的美好新世界。


          2022 年,前 Stripe 開發人員 Jared Sumner 發布了Bun,一種用 Zig 編程語言開發的運行時。據我所知,Bun 最初只是種 JavaScript webserver,但在后續發展中逐漸醞釀出了全面顛覆 JS 生態系統的野心。


          按我個人的關注度排序,Bun 的優勢主要有以下幾點:


          • 據說能提供比 Node 或 Deno 更快的 JavaScript/TypeScript 運行時
          • 包管理器比 NPM 或 Yarn 都快上億倍
          • Browser Bundler——全面支持 tsx、jsx、css、svg 等格式,能替代從 webpack 到 react-scripts 的所有內容,而且速度仍然快如閃電
          • 提供速度極快的 webserver(替代 Express)
          • Sqlite 客戶端
          • Bread


          Bun 改朝換代的思路看著非常簡單粗暴——JS 有的我也要有,而且我的要更簡單、更高效。這里沒有小聰明、沒有曲線救國,要的就是正面對抗而且樣樣比 JS 強。用一種低級語言,編寫出運行極快的代碼,這就是 Bun。


          Bun 還很年輕,也許還沒準備好迎接那些令人頭大的真實生產用例。但它確實發展迅速,所以如果 Bun 真能在幾年后快速占據市場份額,我也覺得完全在情理之中。

          之前的方案到底有什么問題?

          不知道大家在實際工作中有沒有編寫過 JS 或 TS 生產代碼,那種體驗挺難受的。多數情況下,開源工具和小項目也能良好運轉,但一到商業和企業級用例上就經常掉鏈子。而因為傳統、常規的路線走不通,企業只能試遍各種辦法讓項目能在生產環境中正常起效。


          例如,TypeScript 在涉及多位開發者的項目中解決了不少老大難問題,所以只要 JS 的路子走不通,我們就能隨時引入 TS 進行代碼轉換。這里要真心感謝微軟。NPM 對大型項目和單體 repo 來說速度太慢,所以公司可能需要轉向 Yarn。這里又要謝謝 Facebook。總之,我們就是在拼了命地東拼西湊,最終搞出性能勉強說得過去的成果。


          作者提到自己所在企業的整個單體 repo 執行 eslint 需要耗費 79 秒,所以只能單獨配置,保證只對發生變更的文件執行 lint。雖然會引入更多復雜元素,但也沒有辦法。


          總的來說,無數開發者都在用自己的辦法加速 JS 工具鏈中的某些特定部分。比如用 Yarn 3 那瘋狂的“即插即用”節點模塊虛擬化速度來替代 NPM,或者用基于 JSON Schema 的請求解析器解決 Express 的低速問題。其實大多數原有工具都有類似的問題,而且它們是由 JS 開發者編寫、專為 JS 開發者服務的。用 JS 編寫,就等同于速度很慢……


          于是,一些用更快語言編寫的高速工具開始流行起來。每家擁有大型 React 應用程序的企業,肯定都經歷過 WebPack 構建要花掉整整一分鐘的折磨。為此,他們必須轉向用 Go 語言編寫 esbuild。同樣的,其他語言版本的 eslint 替代方案也開始出現,比如用 Rust 重寫 Rome。


          Bun 是這種趨勢的自然延續,但采取的卻是自下而上的推進路徑。這個項目的核心思路就是從零起步、以內置“batteries”的方式,用低級語言重寫整個 JavaScript 生態系統。而且到目前為止,效果還真心不錯。


          Bun 現在可以做些什么?

          讓解釋器快起來

          如果 Bun 只是對所有 JS 輔助工具進行重寫,我當然也很歡迎,但那樣的它只能算是 Node.js 的又一個替代品。Bun 并沒有這樣偷懶,它努力讓解釋器本身也快起來。


          Bun 是用 Zig 編寫的,而且配合蘋果開發的 JavaScriptCore,類似于 Node 使用 v8。Zig 是一種新興的低級語言,主要活躍在 C++占主導地位的場景。我不是低級開發者,所以沒親自用過,更多細節就留給其他技術更強的博主吧。在本文中,大家只要知道 Zig 寫的代碼很快就行了。至于 JavaScriptCore,它的作用跟 v8 一樣,只是 v8 來自谷歌、而它來自蘋果。Safari 和蘋果的很多其他項目都有用到 JavaScriptCore。


          Bun 比 Node 到底快多少還沒有定論,但據稱在某些特定場景下要快得多。很多朋友可能沒經歷過 io.js 剛誕生的時代,總結來說,那時候一個單純能提高解釋器速度的分叉就足以撼動整個 JS 生態系統。而 Bun 的啟動速度又比 Node 快得多。我自己的親身實驗是 7 毫秒左右,大概比 Node.js 快了 10 倍,所以特別適合無服務器環境和邊緣計算場景。


          這一波顛覆依靠的不只是速度優勢,Bun 還添加了不少優秀的標準庫函數。例如,Bun.write()就是用于編寫文件的新函數,它會返回一個承諾,而且號稱可以通過更適合的系統調用進一步加快速度。


          說起 Node API,Bun 目前已經能支持約 90%的現有 Node API。Node 規模很大,其中總有一些別說沒用過、可能大家聽都沒聽過的東西(比如 new AsyncLocalStorage() ),所以能支持 90%已經很好了。誰會運行 NPM 上的所有包呢?根本不需要,而且基本不影響我們的日常開發。


          順便說一句,TypeScript 在 Bun 這邊可是相當有排面,直接調用 bun my-ts-file.ts 就行。Deno 對 TS 的支持也就這個水平了。使用 Bun 對新項目進行模板化,或者把 bun-types 添加到 tsconfig 當中,IDE 中的自動補全功能就將適用于這些新函數!


          Bun 項目最初目標之一就是創建一種更快、更強大的 TypeScript 編譯器。這個目標現在已經實現,同時被淹沒在其他眾多功能中。但目前,它仍然無法支持某些比較高級的 TypeScript 配置和功能,例如裝飾器、tsconfig 中將多個配置合并起來的擴展功能等。

          替代 NPM

          下面來聊 Bun 最振奮人心的能力之一——替代 NPM。它真的很快,能讓人人都滿意那種快。


          在 Linux 上,bun install 的包安裝速度可以達到 npm install 的 20 倍到 100 倍。在 macOS 上,也能達到 4 倍到 80 倍。


          我敢肯定,沒 cache 快,有 cache 更快,總之就是快。


          之前就已經有很多方案在努力幫 NPM 提速了。比如大家熟悉的 Yarn Plug-n-Play,它的思路就是徹底放棄 node_modules 文件夾來加快包安裝速度。雖然有一定效果,但在實際使用中,提速并沒有那么顯著,而且還需要處理大量 polyfill 和 escape-hatches 操作。能用是能用,但我個人實在是不想再用、也不打算向大家推薦。


          Pnpm 是另一種新興的 NPM 替代方案,在繼續使用 TypeScript 編寫的同時實現了一部分智能優化。在 pnpm 中,node_modules 是通過符號鏈接從全局緩存中訪問的,每個包都能在自己的獨立時間內完成安裝,無需等待其他包完成當前操作。


          Bun 的基本思路跟 NPM 一樣,但速度卻更快。它有自己的 lockfile 格式,而且其中的 node_modules 和 package.json 看起來沒什么變化。如果大家對文件系統調用比較熟悉,可以結合低級訪問和快速語言實現極快的安裝效果,而且無需任何花哨的技巧。


          現在,Bun 還不提供工作空間支持,所以暫時沒法對接那些期待它來拯救的大型單體 repo(我們的項目也屬于這類)。但好在 Bun 正保持著迅猛的發展速度,幾周前剛公布的路線圖也提到了工作空間支持。


          請注意,大家不用全面轉向 Bun 也能把它當成包管理器、轉譯器或者解釋器。只需要選擇我們需要的部分,丟棄其余的部分就行。我猜 Bun 的初步普及可能也會走這條道路,就是先當個好用的包管理器,其他的以后再說。這樣接受門檻會變得更低一些。


          內含轉譯器,矛頭指向 webpack、esbuild

          Bun 當中包含一個用于網絡瀏覽器的轉譯器,這明顯是把矛頭指向了 webpack 和 esbuild。順帶一提,Bun 中的解析器就是 esbuild 解析器的一個 Zig 端口,輕松愉快。

          Bun 已經支持多種文件類型,css、svg、tsx、jsx、ts 之類的都行。JS 中的 CSS 等高級選項似乎也能在 Bun 上正常工作。


          由于 Bun 包含一個帶有幾套內置模板的項目腳手架,所以這里我們可以直接調用:

          bun create react my-app


          之后,我運行 bun dev 并在瀏覽器里運行了一個 react 應用程序。我猜可以把 react-scripts 直接添加到 Bun 替換過的工具列表當中。


          把文件擴展名從 jsx 改成 tsx,程序就立刻生效了。導入 svg,沒有問題。開發模式似乎還支持 HMR,也就是前端開發者在使用 webpack 時的一大必備工具。


          那么,轉譯器方面還缺什么嗎?缺的還多,畢竟生產環境的要求可不簡單。首先就是最小化了,這是實際用戶最希望在后續發展路線圖上看到的功能。對于大型插件生態系統來說,還必須要有能夠支持不同文件格式的打包工具。例如,目前.vue 文件和.scss 還沒有實際落地,特別是.scss,這東西幾代開發者都在用,必須趕緊實現。目前我還不確定 Bun 捆綁器的可插拔性怎么樣,而且最重點的是要直接在框架之內解決問題,不要依賴大量外部開源包。


          其他功能——Web server 與 sqlite 客戶端

          Bun 還把不少傳統意義上的框架元素添加到了標準庫當中。就個人而言,我對那些庫類型功能不太感興趣,畢竟 Node 中已經有很多適用于 http server 的功能長城了。


          Bun 的 webserver 看起來非常簡單。Express 雖然有點落后于時代,但對大多數開發者來說仍然夠用(開發團隊今年還剛剛提供了對承諾的支持)。Bun server 好像跟 Cloudflare Worker 頗為相信。只要 JavaScript 生態中的其他問題逐一得到解決,也許 Bun 的開發團隊會轉回頭好好打磨一下 webserver 吧。需要注意的是,在某些情況下,巧用系統調用可以讓 Bun webserver 的速度提高一倍,特別是在文件處理過程中。


          至于新的 SQLite 適配器,我覺得之前 Node 中的 sqlite 實現思路有點脫離正常人的腦洞。現在大多數開發者會把舊有 sqlite 3 包跟 sqlite 打包器結合使用,借此實現對承諾的支持。Bun 的解決方案看起來更簡潔,所以就算速度上沒啥大優勢,我也愿意用。

          酒香也怕巷子深

          我最擔心的是,Bun 的這么多優點難以轉化成對社區成員的實際吸引力。Bun 本身就是 JS 生態系統的完整替代品,這么巨大的轉變一般人恐怕很難快速接受。


          Bun 還很年輕,目前沒有完整的說明文檔。對于大多數問題,我們只能查閱長長的自述文件。但創建一個 docusaurus 站點,再配合具備完整內聯注釋的 TypeScript 類型生成相應的 typedoc 并不困難,所以我猜這一點應該很快就能解決。

          其他產品對比


          服務端渲染 React 每秒 HTTP 請求數 (Linux AMD64) 對比,來自 Bun 官網

          Deno

          如果你從來沒聽說過 Deno、也不打算了解,直接跳過這章也行。而且就個人而言,我覺得 Bun 比 Deno 更有搞頭、更有前途。


          來自 Node 締造者的 Deno 宣稱解決了一些長期困擾開發者的老大難問題。它把 es-modules 設定成默認值,引入了第一方 TypeScript 支持(無需在發布前轉譯 NPM 模塊)等等。但在我看來,Deno 在解決老問題的同時,也引入了不少新問題。


          首先,Deno 對包解析和語法做的變更過于大刀闊斧,導致沒法跟原有 NPM 生態系統兼容。換言之,Deno 需要培養起自己的全新庫生態。雖然 Deno 慢慢開始支持一些早期庫,但我覺得一個項目的影響力會直接決定它的發展上限,所以 Deno 的邊界估計也就到這了。當然也有一些變通方法,比如把 NPM 包轉換成 Deno 包的 CDN,但我覺得這不是什么好招。


          Deno 還有不少在我看來暴露其半成品身份的問題,比如缺少 package.json。無論是從模塊解析的角度來看,還是從缺少 manifest 文件出發,Deno 都不允許開發者為自己的包編寫可擴展元數據。GoLang 甚至專門為此引入了 go.mod。


          另外,我覺得 Deno 設計中的沙箱/權限系統應該是正確的思路,只是粒度不夠細。它位于整個項目的頂層、脫離了包層次,這意味著大型應用程序最終還是需要所有權限,于是問題又回到了原點。而且作為一家安全公司,我們對 Deno 無法保護大型應用免受供應鏈攻擊而頗感失望。當然,Bun 也沒說打算如何解決這個問題,我這里只是發泄一下自己的不滿。


          所以總結起來:Bun 擁有遠超 Deno 的發展潛力。具體原因如下:


          • 它支持現有 NPM 生態系統中的所有庫,也支持大家已經編寫的一切代碼,甚至連 package.json 都不用改。
          • 它解決了生態系統中的幾個突出問題(特別是大企業的訴求問題),而且把解決方案整合到了單一框架當中。
          • 它以人們已經熟悉的方式運行,只是速度更快。不需要改變范式,也不強求轉變思路,用就是了。
          • 可以放心使用,哪怕感覺 Bun 拖慢了開發速度,我們也可以只用它的包管理器;或者說覺得 webpack 太慢,那就只用它的打包程序。
          • 它在幾乎各個維度上都更快,這是種巨大的優勢。從 io.js 就能看到,人們是愿意為了性能而轉投陣營的。

          Rome?

          如上所述,Rome 就是個驗證器。Rome 的維護者們已經開始用 Rust 代替 JS 進行重寫了,而且 79 秒的驗證時長也有點夸張。(不騙人,我們的 eslint 就是用了 79 秒。)


          從路線圖來看,Rome 還打算引入捆綁器、文檔生成器、壓縮器、類型檢查器、測試框架等等。但這一切尚未完成,而 Bun 明顯已經走得更遠。至少 Rome 還沒開始重寫 Node 核心本身,所以我覺得它的影響力也就差不多這樣了。


          總之,很多項目都發現了 Node 生態系統中的現有問題,而且各自嘗試在統一的高性能框架中將其一舉解決。接下來,就看誰發展得更快了。


          開源世界中的生態陣營

          這里我想把視野縮小一點,通過具體案例聊聊開源世界中的生態陣營是怎么產生的。


          相信很多 Node 開發者都知道 Jest 是怎樣力壓 Mocha 測試框架,一路迅猛崛起的。Mocha 想當年也是人們首選的測試運行程序,效果不錯而且語法優秀,但只要涉及更復雜的需求或者斷言,就得引入其他模塊和插件。好在有了社區協作,插件也不算太難找。總之,開發者需要具備更廣泛的知識才能引入相應的庫。


          后來 Facebook 搞出了 Jest,一套內含“batteries”的測試框架。它借鑒了 Mocha 語法和庫,并把一切整合到了單一框架中。Jest 什么都能解決,從偽造時間到需求的檢測和模擬。Jest 也有擴展空間,但我在實際工作中就用過一次。大部分概念驗證和設計都是由 Mocha 承擔的,作為后來者的 Jest 只是把成果統一了起來并使其變得更易于訪問。雖然 Mocha 也不乏鐵桿粉絲,但 Jest 確實更受歡迎。


          開源世界中有很多這樣的案例。首創解決方案拿下先發優勢,而后續一旦增長乏力,就會有熱心的開發商把功能整合起來。這也讓我想到了 Linux 大家族還未統一時的 systemd。如今,systemd 幾乎可以管理大多數 Linux 發行版上的所有內容,而 Bun 也許會以同樣的方式席卷整個 JavaScript 世界。


          我意識到從開源的角度來看,這種合并和統一似乎與開源精神相悖,但用大量庫實現簡單需求確實已經成為折磨開發者們的痛點。而且如果每個庫都有相應的維護團隊,那惡意黑客通過簡單的偽造郵件域就能實施供應鏈攻擊。我們不想這樣,但現實就是如此殘酷。老手尚且容易中招,更遑論剛接觸大量名稱、還不熟悉種種語言的新人了。所以從務實的角度出發,我覺得很多朋友應該跟我一樣,并不覺得把更多常用功能引入標準庫、將多種開發工具整合進統一框架屬于歷史的倒退。

          ?結束語


          截至 2022 年 7 月,Bun 還是沒有做好進軍生產環境的準備,但我強烈建議大家自己裝上試一試。整個流程非常便捷,而且我覺得現在的 Bun 已經足夠應付小型子項目或者公司里的簡單內部儀表板了。


          我不敢說 Bun 在未來幾年能否甚至如何重塑 JavaScript 的面貌,但我真心對它的發展充滿期待。


          原文鏈接:


          https://www.lunasec.io/docs/blog/bun-first-look/

          • 序言
          • var 與 let 的區別作用域重復聲明綁定全局對象變量提升與暫存死區
          • let 與 const 異同
          • 參考


          1.序言

          var、let 和 const 都是 JavaScript 中用來聲明變量的關鍵字,并且 let 和 const 關鍵字是在 ES6 中才新增的。既然都是用來聲明變量的,那它們之間有什么區別呢?讓我們來一探究竟。


          2.var 與 let 的區別

          (1)作用域

          用 var 聲明的變量的作用域是它當前的執行上下文,即如果是在任何函數外面,則是全局執行上下文,如果在函數里面,則是當前函數執行上下文。換句話說,var 聲明的變量的作用域只能是全局或者整個函數塊的。

          而 let 聲明的變量的作用域則是它當前所處代碼塊,即它的作用域既可以是全局或者整個函數塊,也可以是 if、while、switch等用{}限定的代碼塊。

          另外,var 和 let 的作用域規則都是一樣的,其聲明的變量只在其聲明的塊或子塊中可用。

          示例代碼:


          主站蜘蛛池模板: 3d动漫精品成人一区二区三| 国产乱码精品一区二区三区| 精品国产乱子伦一区二区三区| 亚洲高清一区二区三区电影| 国产精品被窝福利一区| 亚洲熟妇无码一区二区三区| 中文人妻av高清一区二区| 免费国产在线精品一区| 国产精品香蕉在线一区| 日韩免费观看一区| 国产一区二区精品久久凹凸 | 亚洲国产日韩一区高清在线| 国产免费一区二区三区免费视频| 精品一区二区久久久久久久网精| 亚洲国产情侣一区二区三区| 日韩一区二区三区无码影院| 亚洲AV综合色一区二区三区| 亚洲一区二区女搞男| 中文字幕人妻无码一区二区三区| 国产精品亚洲不卡一区二区三区| 日本一区二区三区精品国产| 日本在线视频一区| 国产一区二区在线观看app| 日本一区二区在线免费观看| 国产av福利一区二区三巨 | 亚洲av乱码一区二区三区| 无码免费一区二区三区免费播放| 久久精品国产一区二区三区肥胖 | 亚洲熟女综合一区二区三区| 中文字幕一区日韩精品| 亚洲av无一区二区三区| 天堂va视频一区二区| 精品国产一区二区三区久久影院 | 久久国产午夜精品一区二区三区 | 精品少妇一区二区三区在线| 制服丝袜一区二区三区| 久久人妻内射无码一区三区| 国产精品福利一区二区| 四虎在线观看一区二区| 亚洲免费视频一区二区三区| 国产精品伦一区二区三级视频|