1、浏览器的核心是两部分:渲染引擎和 JavaScript 解释器(又称 JavaScript 引擎)。
渲染引擎的主要作用是,将网页代码渲染为用户视觉可以感知的平面文档。
不同的浏览器有不同的渲染引擎。
渲染引擎处理网页,通常分成四个阶段。
- 解析代码:HTML 代码解析为 DOM,CSS 代码解析为 CSSOM(CSS Object Model)。
- 对象合成:将 DOM 和 CSSOM 合成一棵渲染树(render tree)。
- 布局:计算出渲染树的布局(layout)。
- 绘制:将渲染树绘制到屏幕。
以上四步并非严格按顺序执行,往往第一步还没完成,第二步和第三步就已经开始了。所以,会看到这种情况:网页的 HTML 代码还没下载完,但浏览器已经显示出内容了。
2. 渲染引擎,优化重绘重流的小技能:
- 读取 DOM 或者写入 DOM,尽量写在一起,不要混杂。不要读取一个 DOM 节点,然后立刻写入,接着再读取一个 DOM 节点。
- 缓存 DOM 信息。
- 不要一项一项地改变样式,而是使用 CSS class 一次性改变样式。
- 使用
documentFragment
操作 DOM - 动画使用
absolute
定位或fixed
定位,这样可以减少对其他元素的影响。 - 只在必要时才显示隐藏元素。
- 使用
window.requestAnimationFrame()
,因为它可以把代码推迟到下一次重流时执行,而不是立即要求页面重流。 - 使用虚拟 DOM(virtual DOM)库。
3. 浏览器的JavaScript 引擎
主要功能:读取网页中的JavaScript代码,对其处理后运行。
JavaScript 是一种解释型语言,即,它不需要编译,由解释器实时运行。好处:运行和修改都比较方便,刷新页面就可以重新解释;缺点:每次运行都要调用解释器,系统开销较大,运行速度慢于编译型语言。
字节码(bytecode)的中间代码,以提高运行速度。
早期,浏览器内部对 JavaScript 的处理过程如下:
- 读取代码,进行词法分析(Lexical analysis),将代码分解成词元(token)。
- 对词元进行语法分析(parsing),将代码整理成“语法树”(syntax tree)。
- 使用“翻译器”(translator),将代码转为字节码(bytecode)。
- 使用“字节码解释器”(bytecode interpreter),将字节码转为机器码。
JIT),即字节码只在运行时编译,用到哪一行就编译哪一行,并且把编译结果缓存(inline cache)。通常,一个程序被经常用到的,只是其中一小部分代码,有了缓存的编译结果,整个程序的运行速度就会显著提升。
字节码不能直接运行,而是运行在一个虚拟机(Virtual Machine)之上,一般也把虚拟机称为 JavaScript 引擎。
并非所有的 JavaScript 虚拟机运行时都有字节码,有的 JavaScript 虚拟机基于源码,即只要有可能,就通过 JIT(just in time)编译器直接把源码编译成机器码运行,省略字节码步骤。这一点与其他采用虚拟机(比如 Java)的语言不尽相同。这样做的目的,是为了尽可能地优化代码、提高性能。下面是目前最常见的一些 JavaScript 虚拟机:
- Chakra (Microsoft Internet Explorer)
- Nitro/JavaScript Core (Safari)
- Carakan (Opera)
- SpiderMonkey (Firefox)
- V8 (Chrome, Chromium)
4. window对象 指当前浏览器窗口,也是当前页面的顶层对象,即最高一层的对象。
一个变量如果未声明,那么默认就是顶层对象的属性。
window.open 可以打开新窗口。
5. window.requestAnimationFrame() 推迟某个函数 到浏览器下一次重流时执行,执行完了才会进行下一次重绘。
如果某个函数会改变网页的布局,一般就放在window.requestAnimationFrame()
里面执行,这样节能,网页效果平滑。
window.requestIdleCallback() 推迟到系统资源空闲时执行。
6.window.navigator
属性指向一个包含浏览器和系统信息的 Navigator 对象。脚本通过这个属性了解用户的环境信息。
7.Cookie 是服务器保存在浏览器的一小段文本信息,一般大小不能超过4KB。浏览器每次向服务器发出请求,就会自动附上这段信息。
Cookie 主要保存状态信息,以下是一些主要用途。
- 对话(session)管理:保存登录、购物车等需要记录的信息。
- 个性化信息:保存用户的偏好,比如网页的字体大小、背景色等等。
- 追踪用户:记录和分析用户行为。
Cookie 不是一种理想的客户端储存机制。它的容量很小(4KB),缺乏数据操作接口,而且会影响性能。客户端储存应该使用 Web storage API 和 IndexedDB。只有那些每次请求都需要让服务器知道的信息,才应该放在 Cookie 里面。
每个 Cookie 都有以下几方面的元数据。
- Cookie 的名字
- Cookie 的值(真正的数据写在这里面)
- 到期时间(超过这个时间会失效)
- 所属域名(默认为当前域名)
- 生效的路径(默认为当前网址)
8. AJAX (Asynchronous JavaScript and XML ) :AJAX 通过原生的XMLHttpRequest
对象发出 HTTP 请求,得到服务器返回的数据后,再进行处理。现在,服务器返回的都是 JSON 格式的数据,XML 格式已经过时了,但是 AJAX 这个名字已经成了一个通用名词,字面含义已经消失了。
AJAX 包括以下几个步骤。
- 创建 XMLHttpRequest 实例
- 发出 HTTP 请求
- 接收服务器传回的数据
- 更新网页数据
9.同源:三个相同;协议相同,域名相同,端口相同。
A 网页设置的 Cookie,B 网页不能打开,除非这两个网页“同源”。同源,是为了保证用户信息的安全,防止恶意的网站窃取数据。
如果非同源,有3种限制:
(1) 无法读取非同源网页的 Cookie、LocalStorage 和 IndexedDB。
(2) 无法接触非同源网页的 DOM。
(3) 无法向非同源地址发送 AJAX 请求(可以发送,但浏览器会拒绝接受响应)。
9. CORS 是一个 W3C 标准,全称是“跨域资源共享”(Cross-origin resource sharing)。它允许浏览器向跨域的服务器,发出XMLHttpRequest
请求,从而克服了 AJAX 只能同源使用的限制
10. Storage 接口用于脚本在浏览器保存数据。两个对象部署了这个接口:window.sessionStorage
和window.localStorage
。
- sessionStorage :保存的数据用于浏览器的一次会话(session),当会话结束(通常是窗口关闭),数据被清空;
- localStorage: 保存的数据长期存在,下一次访问该网站的时候,网页可以直接读取以前的数据。
2种stroage,除了保存期限不同,其他方面一致,都是key-value的格式。
storage 支持数据变化的事件监听。
11. window.history
属性指向 History 对象,它表示当前窗口的浏览历史。
History 对象保存了当前窗口访问过的所有页面网址。
window.history.length //代表当前窗口访问过几个网址
history.back() or history.go(-1) //回退到上一个页面
History.state : History 堆栈最上层的状态值。
History.forward() : 移动到下一个网址,相当于浏览器的前进按钮,对于最后一个访问的网址,该方法无效果。
History.go()
:接受一个整数作为参数,以当前网址为基准,移动到参数指定的网址,比如go(1)
相当于forward()
,go(-1)
相当于back()
。如果参数超过实际存在的网址范围,该方法无效果;如果不指定参数,默认参数为0
,相当于刷新当前页面
注意,移动到以前访问过的页面时,页面通常是从浏览器缓存之中加载,而不是重新要求服务器发送新的网页。
12. Location
对象是浏览器提供的原生对象,提供 URL 相关的信息和操作方法。通过window.location
和document.location
属性,可以拿到这个对象。
Location
对象提供以下属性。
-
Location.href
:整个 URL。 -
Location.protocol
:当前 URL 的协议,包括冒号(:
)。 -
Location.host
:主机。如果端口不是协议默认的80
和433
,则还会包括冒号(:
)和端口。 -
Location.hostname
:主机名,不包括端口。 -
Location.port
:端口号。 -
Location.pathname
:URL 的路径部分,从根路径/
开始。 -
Location.search
:查询字符串部分,从问号?
开始。 -
Location.hash
:片段字符串部分,从#
开始。 -
Location.username
:域名前面的用户名。 -
Location.password
:域名前面的密码。 -
Location.origin
:URL 的协议、主机名和端口。
13. Location.href 写入新的URL地址,浏览器会立刻跳转到这个新地址。
// 跳转到新网址
document.location.href = 'http://www.example.com';
这个特性常常用于让网页自动滚动到新的锚点。
document.location.href = '#top';
// 等同于
document.location.hash = '#top';
直接改写location
,相当于写入href
属性。
document.location = 'http://www.example.com';
// 等同于
document.location.href = 'http://www.example.com';
另外,Location.href
属性是浏览器唯一允许跨域写入的属性,即非同源的窗口可以改写另一个窗口(比如子窗口与父窗口)的Location.href
属性,导致后者的网址跳转。Location
的其他属性都不允许跨域写入。
其他方法:
Location.assign() : 跳转到新的URL
Location.replace(): 跳转到新的URL, 同时替换History中当前网址
Location.reload(): 刷新
14: URL的编码 和解码
1)网页的URL只能包含合法的字符。合法字符包含2类:
- URL 元字符
- 分号(
;
),逗号(,
),斜杠(/
),问号(?
),冒号(:
),at(@
),&
,等号(=
),加号(+
),美元符号($
),井号(#
)
- 语义字符
-
a-z
,A-Z
,0-9
,连词号(-
),下划线(_
),点(.
),感叹号(!
),波浪线(~
),星号(*
),单引号('
),圆括号(()
)
除了以上字符,其他字符出现在URL之中都必须转义, 规则是根据操作系统的默认编码,将每个自己转为百分号(%
)加上两个大写的十六进制字母。
2)JavaScript 提供四个 URL 的编码/解码方法。
encodeURI()
- 用于转码整个 URL。它的参数是一个字符串,代表整个 URL。它会将元字符和语义字符之外的字符,都进行转义。
encodeURIComponent()
- 用于转码 URL 的组成部分,会转码除了语义字符之外的所有字符,即元字符也会被转码。所以,它不能用于转码整个 URL。它接受一个参数,就是 URL 的片段。
decodeURI()
decodeURIComponent()
15. URL 对象是浏览器的原生对象,可用来构造、解析和解码URL。一般情况下,通过window.URL
可以拿到这个对象。
<a>
元素和<area>
元素都部署了这个接口。这就是说,它们的 DOM 节点对象可以使用 URL 的实例属性和方法。
var a = document.createElement('a');
a.href = 'http://example.com/?foo=1';
a.hostname // "example.com"
a.search // "?foo=1"
上面代码中,a
是<a>
元素的 DOM 节点对象。可以在这个对象上使用 URL 的实例属性,比如hostname
和search。
16. Blob 对象表示一个二进制文件的数据内容,比如一个图片文件的内容就可以通过 Blob 对象读写。它通常用来读写文件,它的名字是 Binary Large Object (二进制大型对象)的缩写。它与 ArrayBuffer 的区别在于,它用于操作二进制文件,而 ArrayBuffer 用于操作内存。
17.File 对象代表一个文件,用来读写文件信息。它继承了 Blob 对象,或者说是一种特殊的 Blob 对象,所有可以使用 Blob 对象的场合都可以使用它。
18.表单(<form>
)用来收集用户提交的数据,发送到服务器。比如,用户提交用户名和密码,让服务器验证,就要通过表单。表单提供多种控件,让开发者使用,具体的控件种类和用法请参考 HTML 语言的教程
19. IndexedDB 就是浏览器提供的本地数据库,它可以被网页脚本创建和操作。IndexedDB 允许储存大量数据,提供查找接口,还能建立索引。这些都是 LocalStorage 所不具备的。就数据库类型而言,IndexedDB 不属于关系型数据库(不支持 SQL 查询语句),更接近 NoSQL 数据库。
IndexedDB 具有以下特点。
(1)键值对储存。 IndexedDB 内部采用对象仓库(object store)存放数据。所有类型的数据都可以直接存入,包括 JavaScript 对象。对象仓库中,数据以“键值对”的形式保存,每一个数据记录都有对应的主键,主键是独一无二的,不能有重复,否则会抛出一个错误。
(2)异步。 IndexedDB 操作时不会锁死浏览器,用户依然可以进行其他操作,这与 LocalStorage 形成对比,后者的操作是同步的。异步设计是为了防止大量数据的读写,拖慢网页的表现。
(3)支持事务。 IndexedDB 支持事务(transaction),这意味着一系列操作步骤之中,只要有一步失败,整个事务就都取消,数据库回滚到事务发生之前的状态,不存在只改写一部分数据的情况。
(4)同源限制。 IndexedDB 受到同源限制,每一个数据库对应创建它的域名。网页只能访问自身域名下的数据库,而不能访问跨域的数据库。
(5)储存空间大。 IndexedDB 的储存空间比 LocalStorage 大得多,一般来说不少于 250MB,甚至没有上限。
(6)支持二进制储存。 IndexedDB 不仅可以储存字符串,还可以储存二进制数据(ArrayBuffer 对象和 Blob 对象)。
20. Web Worker 的作用,就是为 JavaScript 创造多线程环境,允许主线程创建 Worker 线程,将一些任务分配给后者运行。
Worker 线程一旦新建成功,就会始终运行,不会被主线程上的活动(比如用户点击按钮、提交表单)打断。这样有利于随时响应主线程的通信。但是,这也造成了 Worker 比较耗费资源,不应该过度使用,而且一旦使用完毕,就应该关闭。
Web Worker 有以下几个使用注意点。
(1)同源限制
分配给 Worker 线程运行的脚本文件,必须与主线程的脚本文件同源。
(2)DOM 限制
Worker 线程所在的全局对象,与主线程不一样,无法读取主线程所在网页的 DOM 对象,也无法使用document
、window
、parent
这些对象。但是,Worker 线程可以使用navigator
对象和location
对象。
(3)全局对象限制
Worker 的全局对象WorkerGlobalScope
,不同于网页的全局对象Window
,很多接口拿不到。比如,理论上 Worker 线程不能使用console.log
,因为标准里面没有提到 Worker 的全局对象存在console
接口,只定义了Navigator
接口和Location
接口。不过,浏览器实际上支持 Worker 线程使用console.log
,保险的做法还是不使用这个方法。
(4)通信联系
Worker 线程和主线程不在同一个上下文环境,它们不能直接通信,必须通过消息完成。
(5)脚本限制
Worker 线程不能执行alert()
方法和confirm()
方法,但可以使用 XMLHttpRequest 对象发出 AJAX 请求。
(6)文件限制
Worker 线程无法读取本地文件,即不能打开本机的文件系统(file://
),它所加载的脚本,必须来自网络。