Javascript为什么没有标准库?

内容纲要

Javascript竟然没有标准库?

Bobi.ink

2019-07-16

最近在SegmentFault热心解题,一个问题比较让我比较印象深刻:一个初学者试图在浏览器中导入Node.js的net模块。结果就是在控制台打印后是一个空对象。

对于有点Javascript经验的人来说,这是一个‘弱智’问题,怎么可以在浏览器端运行Node程序呢?因为这些node模块经过webpack处理, 所以变成了一个空对象,更好的处理应该是抛出异常.

仔细反思一下,对于这些刚入门Javascript的或者从其他语言切换过来的,他们压根就没有概念,比如Python、Ruby、Java这些语言都有强大的标准库,可以满足80%的开发需求,不管它在什么环境、什么平台运行,都可以统一使用这套标准库。而Javascript目前的现状就是不同的运行环境API结构是割裂的

Javascript这门十几天开发出来的、专供浏览器的语言,可能当初设计是根本就没有考虑标准库这些玩意,比如文件系统,网络等等。因为这个背景, Javascript不具备独立性,它深度依赖于浏览器这个运行环境, 处于一种给浏览器打辅助的角色, 所以Javascript很多年没有走出浏览器玩具语言这个范围. 当然这既是劣势,也是优势, 现在没任何语言能撼动Javascript在浏览器中的地位。

我想很多人跟我当初一样将浏览器提供的Web API等价于Javascript的标准库, 比如console.logsetTimeout(下文会介绍这些功能都不在Javascript规范里面). 正如当年那些把JQuery当成Javascript的人.

直到NodeJS的出现,Javascript才走出浏览器约束,延伸到服务器领域, 不再是一个’沙盒语言’。NodeJS定义了很多模块来支撑服务端的开发, 如fs、os、Buffer、net。但是这些和WebAPI一样不是标准的、也就是说NodeJS !== Javascript.

再到后来,学不动了,NodeJS原作者吐槽了一通NodeJS,又搞出了一个Deno, 它也会有自己标准库,会定义自己的文件系统、网络API。从名字上就暗示着这些API不可能和NodeJS兼容。Ok,现在回到文章开始那个问题,如果deno发展起来,说不定哪天又有人尝试在浏览器引用Deno的模块?

现有的Javascript API结构

如上图, Javascript其实是有一层比较薄全局的、通用的、标准的、核心的API层,即标准内置对象,这是一些语言核心的内置对象,可以全局访问。关键的是这些是标准的,它们在ECMAScript规范中被定义. 在这个基础之上,不同的运行环境拓展了自己的API。

以浏览器为例:

浏览器端的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

这些全局基本对象数量很少, 平时我们使用的非常频繁的定时器和Console都不再此列. 这些对象是每个JavaScript开发者必须掌握的.

这些对象只能满足很基本开发需求, 根本不能和其他语言的标准库相比. 当然这和语言的定位也有一定关系,JavaScript最初的定位就是浏览器脚本,谁知道它现在发展得这么快?

什么是标准库?

标准库是什么没有一个规范化的定义,按照Wiki的说法标准库就是该语言在不同实现中都按例提供的库, 比如Ruby官方实现和基于JVM的JRuby都应该按照规范实现标准库。 标准库怎么设计,需要包含什么内容取决于语言各自秉持的哲学和定位。 我认为标准库应该有以下特征:

标准化的,有规范明确定义它的内容和行为

内容经过仔细雕琢和挑选,可以覆盖大部分使用场景或者符合的语言定位

可选的、按需导入. 标准库不是全局的,需要通过模块导入, 非强制性使用

至于标准库需要包含什么内容,可以参考其他语言的实现。比如:

go

ruby

python

大概分析一下,它们标准库大致都有这些内容:

网络协议

文件系统

文件系统

标准输入输出

二进制处理

算法

密码算法

编码

压缩、归档

排序

数学

字符串、文本

数据结构, 例如树、堆、队列等等

数据持久化和序列化. 比如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上的第三方库,有时候就是懒得去比较和选择

优雅的标准库,是学习的榜样. 网上很多教程都是钻研标准库算法和实现的,对语言的开发者来说标准库是一块宝藏

学习成本。其他语言的开发者,可以较快入手

标准库可能会有什么问题?

标准可能滞后跟不上社区发展. Javascript正处于快速发展阶段,很多规范的定义是由社区驱动的,比如Promise、async/await. 跟不上社区的发展结果可能就是没人用

想下WebComponent目前的境遇

标准库不可能满足所有人的口味

如何设计标准库? 标准库推进进程可能会有什么障碍?

NodeJS已经是事实上的标准, 怎么兼容现有的生态?

标准库应该包含什么内容,如何保持和社区同步?

如何把控标准库内容的尺度? 最小化的标准库容易被维护和升级,就可能出现没什么卵用的情况;最大化的标准库,例如Java的标准库,几乎包含了所有的东西,开发者可以快速开发一个东西, 但是过了几年很多API就会变得过时,一般为了保持向下兼容,这些API会一直像一根刺一样卡在那里,另一个非常典型的反例就是PHP的标准库,这里可以看到各种风格的API. 而且标准库是跟随语言发布的,如果你的项目中使用了过时的API,又想升级语言版本,就需要重构项目。而使用第三方库则可能可以保持不动。

Javascript的主要战场还是浏览器, 标准库是否应该有一个基本版(浏览器或者一些操作系统抽象的运行环境)还有个旗舰版(服务端), 或者只提供一个跨越所有平台的标准库?

如何处理兼容性问题?

如何与现有的全局对象或用户模块分离?

近期的一些尝试

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

Javascript发展非常快,已经不再是当初的玩具语言,相信有一天可以实现一直用一直爽.

扩展

Brendan Eich: JavaScript standard library will stay small

What if we had a great standard library in JavaScript?

The JavaScript Standard Library

W3C

Web API 索引

Explain Like I’m Five: What’s a standard library?

Please enable JavaScript to view the comments powered by Disqus.

本文转自:https://cloud.tencent.com/developer/article/1482236