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结构,介绍什么是标准库,辩证考虑标准库的优缺点,以及推行上面可能会遇到的阻碍.


扩展

相关文章
相关标签/搜索