JavaScript 居然沒有標準庫?

最近在SegmentFault解題,一個問題比較讓我比較印象深入:一個初學者試圖在瀏覽器中導入Node.js的net模塊。結果在控制檯打印後是一個空對象javascript

對於有點Javascript經驗的人來講,這是一個‘弱智’問題,怎麼能夠在瀏覽器端運行Node程序呢?由於這些Node模塊通過Webpack處理, 因此變成了一個空對象,更好的處理方式應該是拋出異常.html

仔細反思一下,對於這些剛入門Javascript的或者從其餘語言切換過來的開發者,他們壓根就沒有概念,好比Python、Ruby、Java這些語言都有強大的標準庫,能夠知足80%的開發需求,無論它在什麼環境、什麼平臺運行,基本上均可以統一使用這套標準庫。而Javascript目前的現狀是:不一樣的運行環境,API結構是割裂的java

Javascript這門十幾天開發出來的、專供瀏覽器的語言,可能當初設計是根本就沒有考慮標準庫這些玩意,好比文件系統,網絡等等。由於這個背景, Javascript長期不具有獨立性,它深度依賴於瀏覽器這個運行環境, 處於一種給瀏覽器打輔助的角色, 因此Javascript不少年沒有走出瀏覽器玩具語言這個範圍.node

固然這既是劣勢,也是優點, 如今沒任何語言能撼動Javascript在瀏覽器中的地位。python

我想不少人跟我當初同樣認爲瀏覽器提供的Web API === Javascript的標準庫, 好比console.logsetTimeout(下文會介紹這些功能都不在Javascript規範裏面). 正如當年那些把JQuery當成‘Javascript’的人.webpack

直到NodeJS的出現,Javascript才掙脫瀏覽器約束,延伸到服務器領域, 再也不是一個'沙盒語言'。NodeJS定義了不少模塊來支撐服務端的開發, 如fs、os、Buffer、net。可是這些API同樣不是Javascript的標準、也就是說NodeJS !== Javascript.git

再到後來,學不動了,NodeJS原做者吐槽了一通NodeJS,又搞出了一個Deno, 它也會有本身標準庫,會定義本身的文件系統、網絡API。從名字上就暗示着這些API不可能和NodeJS兼容。Ok,如今回到文章開始那個問題,若是deno發展起來,說不定哪天又有人嘗試在瀏覽器引用Deno的模塊github



現有的Javascript API結構

如上圖, Javascript實際上是有一層比較薄全局的、通用的、標準的、核心的API層,即標準內置對象,這是一些語言核心的內置對象,能夠全局訪問。關鍵的是這些是標準的,它們在ECMAScript規範中被定義. 在這個基礎之上,不一樣的運行環境拓展了本身的API。golang

以瀏覽器爲例:web

瀏覽器端的Web API是一個很是複雜API集合,上圖總結了一下,基本就包含兩塊東西:

  • Core DOM. DOM是一個通用的技術,不只僅侷限於瀏覽器,這個規範定義告終構化(structured document)文檔的解析和操做規範。定義了基本的節點類型和操做方法。不侷限於HTML的操做
  • HTML DOM. 能夠認爲是Core DOM的擴展,這裏面定義了各類HTML元素對象類型、擴展了元素的操做方法,另外還包含了瀏覽器相關的接口,如XMLHttpRequest。這一塊一般也被統稱爲BOM

WebAPI基本概覽:

若是你有留心查看MDN文檔下面的規範引用,你會發現有些規範引用了W3C, 有些則引用了WHATWG. 到底誰說了算?

若是你掀開鍋蓋,就會發現這是一場鬧劇. 若是前陣子有關注新聞,會看到這些標題‘WHATWG 擊敗 W3C,贏得 HTML 和 DOM 的控制權’、'W3C將與WHATWG合做制定最新HTML和DOM規範標準'. 大概能夠猜出這兩個組織之間的關係. 本文就不扯這些‘八卦’了,相關背景能夠看這篇文章WHATWG 擊敗 W3C,贏得 HTML 和 DOM 的控制權

相對而言, 語言層則由ECMAScript規範定義的,比較獨立, 近些年成果也比較顯著.


標準內置對象層主要包含這些東西

  • 特殊值
    • Infinity
    • NaN
    • undefined
    • null
    • globalThis
  • 函數
    • eval()
    • uneval()
    • isFinite()
    • isNaN()
    • parseFloat()
    • parseInt()
    • decodeURI()
    • decodeURIComponent()
    • encodeURI()
    • encodeURIComponent()
  • 基礎對象
    • Object
    • Function
    • Boolean
    • Symbol
    • Error
    • EvalError
    • InternalError
    • RangeError
    • ReferenceError
    • SyntaxError
    • TypeError
    • URIError
  • 數值和時間
    • Number
    • BigInt
    • Math
    • Date
  • 文本處理
    • String
    • RegExp
  • 索引容器
    • Array
    • 'TypedArray'
  • 鍵值容器
    • Map
    • Set
    • WeakMap
    • WeakSet
  • 結構化數據
    • ArrayBuffer
    • SharedArrayBuffer
    • Atomics
    • DataView
    • JSON
  • 控制抽象化對象
    • Promise
    • Generator
    • GeneratorFunction
    • AsyncFunction
  • 反射
    • Reflect
    • Proxy
  • 國際化
    • Intl
  • WebAssembly
  • 其餘
    • arguments

這些全局基本對象數量不多, 這些對象是每一個JavaScript開發者必須掌握的.

平時咱們使用的很是頻繁的Timer和Console都再也不此列.

這些對象只能知足很基本開發需求, 根本不能和其餘語言的標準庫相比. 固然這和語言的定位也有必定關係



什麼是標準庫?

標準庫沒有一個嚴格的定義,按照Wiki的說法標準庫就是該語言在不一樣實現中都按例提供的庫, 好比Ruby官方實現cRuby和基於JVM的JRuby都按照官方標準庫規範實現了標準庫。 標準庫怎麼設計,須要包含什麼內容取決於語言各自秉持的哲學和定位。 我認爲標準庫應該有如下特徵:

  • 標準化的. 有規範明肯定義它的內容和行爲
  • 內容通過仔細雕琢和挑選,能夠覆蓋大部分使用場景或者符合的語言定位
  • 可選的、按需導入. 標準庫不是全局的,須要經過模塊導入, 非強制性使用

至於標準庫須要包含什麼內容,能夠參考其餘語言的實現。好比:


大概分析一下,它們標準庫大體都有這些內容:

  • 網絡協議
  • 文件系統
    • 文件系統
    • 標準輸入輸出
    • 二進制處理
  • 算法
    • 密碼算法
    • 編碼
    • 壓縮、歸檔
    • 排序
    • 數學
    • 字符串、文本
  • 數據結構, 例如樹、堆、隊列等等
  • 數據持久化和序列化. 好比JSON序列化,二進制序列化,數據庫操做等等
  • 調試/輔助
  • 單元測試
  • 文檔處理
  • 設計模式. 標準庫中常常會攜帶(或輔助設計)該語言的最佳實踐和設計模式, 例如go中的context, Ruby中的singleton
  • 國際化
  • 時間、日期
  • 操做系統
    • 命令行
    • 環境變量
    • 系統資源
  • 併發
    • 進程
    • 線程
    • 協程
  • 語言或運行時的底層接口

大部分語言的核心都很小(C++除外),咱們學一門語言,大部分時間是花在標準庫上和語言的生態上面,可是你會發現這些標準庫通常都是大同小異,這就是爲何有經驗的開發者能夠很快地入手一門語言.

顯然上面這些功能大部分在NodeJS中已經實現了,鑑於NodeJS這麼普遍的使用率,NodeJS能夠算是事實上的標準了


咱們須要標準庫?

顯然要結合當前的背景來辯證地考慮。

有標準庫有什麼好處?

  • 標準庫提供通用、定義良好、優化的功能和行爲,減小第三方模塊依賴, 並且第三方庫很難保證質量
  • 避免社區割裂, 撫平不一樣運行環境的差別. 如今有NodeJS、後面有Deno,可能還會有Aeno、Beno, 儘管取代NodeJS的可能性很低,有規範化的標準庫能夠避免重複造輪子,否則真會學不動
  • 安全性. 近期npm安全事件頻發,投毒、刪庫(left-pad事件)、npm商業運做, 給社區帶了很多麻煩。而標準庫由運行環境內置,能夠避免引用第三方庫致使的安全問題
  • 今天的Javascript應用會有不少依賴(node_modules hell),打包出來的體積很大,網絡加載和腳本解析須要耗費必定的資源,並且這些資源不能在多個應用之間被緩存. 一個很大的緣由是npm的依賴過於零碎(好比幾行代碼的包)和重複(依賴不一樣的版本、Dead Code),使用標準庫能夠減小這部分依賴
  • 選擇困難症. 沒有標準庫,能夠選擇npm上的第三方庫,在npm上挑選靠譜、高質量的庫是須要必定的時間成本的. 有時候咱們就是懶得去比較和選擇
  • 優雅的標準庫,是學習的榜樣. 網上不少教程都是鑽研標準庫算法和實現的,對語言的開發者來講標準庫是一塊寶藏
  • 學習成本。其餘語言的開發者,能夠較快入手

標準庫可能會有什麼問題?

  • 標準可能滯後跟不上社區發展. Javascript正處於快速發展階段,不少規範的定義是由社區驅動的,好比Promise、async/await. 跟不上社區的發展結果可能就是沒人用
  • 想下WebComponent目前的境遇
  • 標準庫不可能知足全部人的口味

如何設計標準庫? 標準庫推動進程可能會有什麼障礙?

  • NodeJS已是事實上的標準, 怎麼兼容現有的生態?

  • 標準庫應該包含什麼內容,如何保持和社區同步?

  • 如何把控標準庫內容的尺度?

    最小化的標準庫容易被維護和升級,但可能出現'沒什麼卵用'的狀況;

    最大化的標準庫,例如Java的標準庫,幾乎包含了全部的東西,開發者能夠快速開發一個東西, 可是過了幾年不少API就會變得過期,通常爲了保持向下兼容,這些API會一直像一根刺同樣卡在那裏. 另外一個很是典型的反例就是PHP的標準庫,這裏能夠看到各類風格的API.

    標準庫是跟隨語言發佈的,若是你的項目中使用了過期的API,又想升級語言版本,就須要重構項目。而使用第三方庫則可能能夠保持不動。

  • Javascript的主要戰場仍是瀏覽器, 標準庫是否應該有一個'基本版'(用於瀏覽器或者一些抽象操做系統的運行環境), 還有個'旗艦版'(服務端), 或者只提供一個跨越全部平臺的標準庫?

  • 如何處理兼容性問題? 老舊瀏覽器如何Polyfill?

  • 如何與現有的全局對象或用戶模塊分離?


近期的一些嘗試

  • proposal-javascript-standard-library 這是一個很是早期的語言提議,定義瞭如何引用標準庫(built-in modules),可是沒有定義標準庫的內容

  • KV Storage: the Web's First Built-in Module Chrome在年初推出的實驗性功能,嘗試實現proposal-javascript-standard-library提議. 它經過下面方式來引用‘標準庫’模塊:

    import {storage, StorageArea} from 'std:kv-storage'; // std: 前綴,和普通模塊區分開來
    複製代碼

總結

本文從一個SegmentFault上的一個問題開始,對比其餘語言,揭露Javascript沒有標準庫的窘境. 接着介紹現有Javascript的API結構,介紹什麼是標準庫,辯證考慮標準庫的優缺點,以及推行上面可能會遇到的阻礙.


擴展

相關文章
相關標籤/搜索