从宿主环境认识JavaScript

179 阅读12分钟

JavaScript(缩写:JS)是一种具有函数优先特性的轻量级、解释型或者说即时编译型的编程语言。应用场合极其广泛,简单到幻灯片、照片库、浮动布局和响应按钮点击,复杂到游戏、2D/3D 动画、大型数据库驱动程序等等。

JavaScript 相当简洁,却非常灵活。开发者们基于 JavaScript 核心编写了大量实用工具,可以使 开发工作事半功倍。其中包括:

  • 浏览器应用程序接口(API)—— 浏览器内置的 API 提供了丰富的功能,比如:动态创建 HTML 和设置 CSS 样式、从用户的摄像头采集处理视频流、生成 3D 图像与音频样本等等。
  • 第三方 API —— 让开发者可以在自己的站点中整合其他内容提供者(Twitter、Facebook 等)提供的功能。
  • 第三方框架和库 —— 用来快速构建网站和应用。

脚本语言

JavaScript 是一种轻量级的脚本语言。所谓“脚本语言”(script language),指的是它不具备开发操作系统的能力,而是只用来编写控制其他大型应用程序(比如浏览器)的“脚本”。

1994年12月,Navigator 发布了1.0版,市场份额一举超过90%。Netscape 公司很快发现,Navigator 浏览器需要一种可以嵌入网页的脚本语言,用来控制浏览器行为。当时,网速很慢而且上网费很贵,有些操作不宜在服务器端完成。管理层对这种浏览器脚本语言的设想是:功能不需要太强,语法较为简单,容易学习和部署。

1995年,Netscape 公司雇佣了程序员 Brendan Eich 开发这种网页脚本语言。Brendan Eich 有很强的函数式编程背景,希望以 Scheme 语言(函数式语言鼻祖 LISP 语言的一种方言)为蓝本,实现这种新语言。

1995年5月,Brendan Eich 只用了10天,就设计完成了这种语言的第一版。它是一个大杂烩,语法有多个来源。

  • 基本语法:借鉴 C 语言和 Java 语言。
  • 数据结构:借鉴 Java 语言,包括将值分成原始值和对象两大类。
  • 函数的用法:借鉴 Scheme 语言和 Awk 语言,将函数当作第一等公民,并引入闭包。
  • 原型继承模型:借鉴 Self 语言(Smalltalk 的一种变种)。
  • 正则表达式:借鉴 Perl 语言。
  • 字符串和数组处理:借鉴 Python 语言。

为了保持简单,这种脚本语言缺少一些关键的功能,比如块级作用域、模块、子类型(subtyping)等等,但是可以利用现有功能找出解决办法。这种功能的不足,直接导致了后来 JavaScript 的一个显著特点:对于其他语言,需要学习语言的各种功能,而对于 JavaScript,常常需要学习各种解决问题的模式。而且由于来源多样,从一开始就注定,JavaScript 的编程风格是函数式编程和面向对象编程的一种混合体。

虽然作为 Web 页面中的脚本语言被人所熟知,但是它也被用到了很多非浏览器环境中,例如 Node.js、Apache CouchDB、Adobe Acrobat 等。进一步说,JavaScript 是一种基于原型、多范式、单线程的动态语言,并且支持面向对象、命令式和声明式(如函数式编程)风格。

从语法角度看,JavaScript 语言是一种“对象模型”语言。各种宿主环境通过这个模型,描述自己的功能和操作接口,从而通过 JavaScript 控制这些功能。但是,JavaScript 并不是纯粹的“面向对象语言”,还支持其他编程范式(比如函数式编程)。这导致几乎任何一个问题,JavaScript 都有多种解决方法。

JavaScript 也是一种嵌入式(embedded)语言。它本身提供的核心语法不算很多,只能用来做一些数学和逻辑运算。JavaScript 本身不提供任何与 I/O(输入/输出)相关的 API,都要靠宿主环境(host)提供,所以 JavaScript 只合适嵌入更大型的应用程序环境,去调用宿主环境提供的底层 API。

目前,已经嵌入 JavaScript 的宿主环境有多种,最常见的环境就是浏览器,另外还有服务器环境,也就是 Node 项目。

宿主环境

浏览器是用来检索、展示以及传递Web信息资源的应用程序。

应用程序(英语:application program),简称应用(application或app),是软件的主要分类,指为针对用户的某种特殊应用目的所撰写的程序,例如文本处理器、表格、会计应用、浏览器、媒体播放器、航空飞行模拟器、命令行游戏、图像编辑器等。与之相对应的是主要功能为驱动计算机运行的系统软件。或者说,应用软件可以直接完成终端用户的工作。从某种意义上来讲,系统软件是为应用软件服务的,应用软件才是真正直接提供用户工作的。

应用程序通常被我们称为软件,但严格来说,它们只是程序的一个实例,是程序的一个运行实体。

电子计算机(Electronic computer)亦称电脑,是利用模拟或者数字电子技术,根据一系列指令指示并且自动执行任意算术或逻辑操作串行的设备。通用计算机因有能遵循被称为“程序”的一般操作集的能力而使得它们能够执行极其广泛的任务。

计算机程序(英语:Computer Program)是指一组指示电子计算机或其他具有消息处理能力的电子设备每一步动作的指令,通常用某种程序设计语言编写,运行于某种目标体系结构上。

编程语言(英语:programming language),是用来定义计算机程序的形式语言。它是一种被标准化的交流技巧,用来向计算机发出指令,一种能够让程序员准确地定义计算机所需要使用数据的计算机语言,并精确地定义在不同情况下所应当采取的行动。

计算机是现代一种用于高速计算的电子计算机器,可以进行数值计算,又可以进行逻辑计算,还具有存储记忆功能。是能够按照程序运行,自动、高速处理海量数据的现代化智能电子设备。由硬件系统和软件系统所组成,没有安装任何软件的计算机称为裸机。

电脑硬件(英语:computer hardware)常简称为硬件,是电子计算机的物理设备。系统软件存储在硬件内,包含固件(如BIOS)以及操作系统,系统软件使应用程序可以提供用户所需的功能。操作系统通常借由总线与设备沟通,这就需要驱动程序。

软件(英语:software)是一系列按照特定顺序组织的电脑数据和指令,是电脑中的非有形部分。电脑中的有形部分称为硬件,由电脑的外壳及各零件及电路所组成。电脑软件需有硬件才能运作,反之亦然,软件和硬件都无法在不互相配合的情形下进行实际的运作。

软件并不只是包括可以在计算机上运行的电脑程序,与这些电脑程序相关的文档一般也被认为是软件的一部分。简单的说软件就是程序加文档的集合体。也就是说,软件并不一定是可执行文件。

可执行文件在计算机科学中指一种内容可被电脑解释为程序的电脑文件。通常可执行文件内,含有以二进制编码的微处理器指令,也因此可执行文件有时称为二进制档。这些二进制微处理器指令的编码,于各种微处理器有所不同,故此可执行文件多数要分开不同的微处理版本。一个电脑文件是否为可执行文件,主要由操作系统的传统决定。例如根据特定的命名方法(如扩展名为exe)或文件的元数据信息(例如UNIX系统设置“可执行”权限)。

JavaScript不是可执行文件,需要依靠浏览器等宿主环境才能运行。

浏览器

浏览器除了要解析网页,还有界面、交互、渲染、网络请求、数据存储、数据处理、数据传输等。为了完成不同的工作,浏览器将这些工作分配给不同的进程处理。为了更为高效地处理这些工作,不同的进程又被划分为不同的线程。

进程(英语:process),是指计算机中已执行的程序,曾经是分时系统的基本运作单位。在面向进程设计的系统(如早期的UNIX,Linux 2.4及更早的版本)中,是程序的基本执行实体;在面向线程设计的系统(如当代多数操作系统、Linux 2.6及更新的版本)中,进程本身不是基本执行单位,而是线程的容器。

线程(英语:thread)在计算机科学中,是将进程划分为两个或多个线程(实例)或子进程,由单处理器(单线程)或多处理器(多线程)或多核处理系统并发执行。

进程与线程的区别:进程是计算机管理运行程序的一种方式,一个进程下可包含一个或者多个线程。线程可以理解为子进程。进程是CPU资源分配的最小单位,线程是CPU调度的最小单位。

调度或译排班(英语:schedule),是将任务分配至资源的过程,在计算机或生产处理中尤为重要。

浏览器的进程

  • 主进程:主要负责界面显示、用户交互、子进程管理、网络资源管理。
  • 渲染进程:会开启一个渲染主线程,负责执行HTML、CSS、JavaScript代码。
  • 网络进程:负责加载网络资源,进程内部会启动多个线程处理不同的网络任务。
  • GPU进程:用于3D绘制等,将开启了3D绘制的元素的渲染由CPU转向GPU。
  • 插件进程:每种类型的插件对应一个进程,仅当使用该插件时才创建。
  • 音频进程:浏览器音频管理。

浏览器的线程

  • GUI渲染线程
    • 负责渲染浏览器界面,解析HTML,CSS,构建DOM树和RenderObject树,布局和绘制。
    • 当界面需要重绘或由于某种操作引发回流时,该线程就会执行。
    • 与JS引擎互斥,当执行JS引擎线程时,GUI会pending,当任务队列空闲时,才会继续执。行GUI。
  • JS引擎线程
    • 也称为JS内核,负责处理javascript脚本程序。
    • JS引擎线程负责解析Javascript脚本,运行代码。
    • JS引擎一直等待任务队列中任务的到来,然后加以处理,浏览器无论什么时候都只有一个JS线程在运行JS程序。-
    • GUI渲染线程与JS引擎线程时互斥的,所以如果JS执行的时间过长,这样就会造成页面的渲染不连贯,导致页面渲染加载阻塞。
  • 事件触发线程
    • 事件触发线程归属于浏览器而不是JS引擎,用来控制事件循环。
    • 当JS引擎执行代码块如setTimeOut时,会将对应的任务添加到事件线程中。
    • 当对应的事件符合触发条件被触发时,该线程会把事件添加到待处理队列的队尾,等待JS引擎的处理。
    • 由于JS的单线程关系,所以这些待处理队列的事件都得排队等待JS引擎的处理
  • 定时触发器线程
    • setInterval、setTimeOut所在线程。
    • 浏览器定时计数器并不是由JavaScript引擎计数,因此通过单独线程来计时并触发。
    • W3C在HTML标准中规定要求setTimeOut中低于4ms的时间间隔为4ms
  • 异步HTTP请求线程(IO线程)
    • 在XMLHttpRequest在连接后是通过浏览器新开一个线程请求。
    • 将检测到状态变更时,如果设置有回调函数,异步线程就产生状态变更事件,将这个回调再放入事件队列中,再由JavaScript引擎执行。

渲染原理

1.jpg

2.jpg

  • 解析HTML:解析HTML为DOM,解析CSS为CSSOM。

3.jpg

4.jpg

HTML解析过程中遇到CSS代码:为了提高解析效率,浏览器会启动一个预解析器率先下载和解析CSS。

5.jpg

HTML解析过程中遇到JavaScript:渲染主线程遇到JavaScript必须暂停一切行为,等待下载执行完成后才能继续,预解析线程可以分担下载JavaScript的任务。

6.jpg
  • 样式计算:合并CSSOM到DOM。
7.jpg
  • 布局:计算出样式布局信息。

8.jpg

DOM树不一定是一一对应的。

11.jpg
  • 分层:按照层叠关系进行处理。
12.jpg
  • 绘制:为每一层生成如何绘制的指令。
13.jpg
  • 分块:会将每一层分为多个小的区域。

15.jpg

分块的工作是交给多个线程同时进行的。

16.jpg
  • 光栅化:将每个块变成位图,靠近视口的块会被优先处理。

17.jpg

此过程会用到GPU加速。

18.jpg
  • 画:合成线程计算出每个位图在屏幕上的位置,交给GPU进行最终呈现。
19.jpg