使用 MutationObserver 监视 DOM 变化

MutationObserver 可以用来监控 DOM 的变动。 这个 API 定义在 DOM4 中,被设计来替代已经废弃的 DOM3 事件:Mutation Events。

该 API 与事件不同的是,它并不会在每个 DOM 节点变化后立即执行回调函数,而是在 DOM 操作都完成后,将所有变化记录存储在数组中,在事件循环的 microtask 阶段执行回调函数,一次性处理这些存储的变化。

注: 一次事件循环,从 macrotask quene 中取出一个任务执行,执行完毕后,执行整个 microtask quene 中的任务。

构造函数:

1
MutationObserver(callback)

callback 在每个 DOM 变动中被调用,这个回调函数接受两个参数:

  1. mutationRecords 改变记录列表
  2. observer 即 MutationObserver 实例
READ MORE...

JavaScript 闭包

闭包(词法闭包)的定义

闭包是「函数」以及该函数相关的「引用环境」组合而成的实体。

这个定义中,包含了两个核心的要素:

  1. 函数:一段可以执行的代码
  2. 引用环境:生成闭包时的词法环境

观察以下这段 JavaScript 代码:

1
2
3
4
5
6
7
8
9
10
11
function outer() {
  var _private = 'private'

  return function inner() {
    return _private
  }
}

var getPrivate = outer()
console.log(getPrivate.name) // 'inner'
getPrivate() // 'private'

函数 outer 调用后,会将在其内部定义的函数 inner 暴露出来并赋值给 getPrivate,在下一行代码可以确认这点。
再继续下一行,我们调用 getPrivate,发现成功得到了 outer 的本地变量 _private,尽管此时 outer 已经返回(我们学习过的编程知识告诉我们,函数执行时在栈上分配的变量会在离开函数执行环境时被销毁)。这个例子的实际结果说明 outer 的本地变量 _private 被保存在某个地方(堆上分配)继续可用。

根据这些表现,我们可以确认这就是一个典型的闭包。

实际上,这个闭包在 outer 被调用的时候创建。它在某个地方保存了函数 inner 所需要的引用环境,使得离开了创建闭包的环境(outer)时,对自由变量的引用依旧有效(直至闭包的生命周期结束才一并被回收)。

注:自由变量是指在函数之外的定义的变量,它既不是本地变量,也不是参数。

READ MORE...

DOM (Level 2) 事件流

闲暇无事复习下 DOM 事件流,顺便做做笔记记录一些重点。

先看一段简单的 HTML 代码:

1
2
3
4
5
6
7
8
9
10
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Document</title>
</head>
<body>
  <div id="div"></div>
</body>
</html>

我们在点击 div 的时候,而事件流是这样传播的:

1. 捕获阶段

最先开始的是捕获阶段。

按照 DOM Level 2 标准要求,事件会从 document 开始向内部传播,途径 htmlbody,最终在事件目标 (event target) div 之前停止。从而有机会在事件流到达目标之前捕获。

然而实际上是这样的:

  1. IE9, Safari, Chrome, Opera, Firefox 会从 window 开始捕获事件。
  2. IE9,Safari,Chrome,Firefox,Opera 9.5+ 等浏览器,捕获阶段不会在事件目标前停止,会继续传播到事件目标。
READ MORE...

JavaScript 中的多行文本

有时候,我们需要使用 JavaScript 构造一些 HTML 代码,如下:

1
2
3
4
5
<div id="outer">
    <div id="inner">
        内容
    </div>
</div>

用 JavaScript 字符串来表示就是:

1
const html = '<div id="outer"><div id="inner">内容</div></div>'

为了可读性和避免写错,就有强烈的写成带缩进的多行文本的需求。 现阶段,JavaScript 的多行文本,大家马上能想到的就是用 ES6 的字符串模板字面量来创建。

1
2
3
4
5
6
7
const html = `
<div id="outer">
    <div id="inner">
        内容
    </div>
</div>
`

忆苦思甜,以前都是怎么做的呢?

可能大家还有印象,最笨拙的字符串拼接方法:

1
2
3
4
5
6
var html = ''
+ '<div id="outer">'
+     '<div id="inner">'
+       '内容'
+     '</div>'
+ '</div>'

还有利用反斜杠的方式:

1
2
3
4
5
6
7
var html = '\
<div id="outer">\
    <div id="inner">\
        内容\
    </div>\
</div>\
'

利用数组的方式:

1
2
3
4
5
6
7
var html = [
'<div id="outer">',
'    <div>',
'        内容',
'    </div>',
'</div>'
].join('')

不走寻常路的利用读取函数内容的方式:

1
2
3
4
5
6
7
8
9
10
11
function hereDoc(docFn) {
    return docFn.toString().replace(/^[^\/]+\/\*!?\s?/, '').replace(/\*\/[^\/]+$/, '')
}

var html = hereDoc(function(){/*!
<div id="outer">
    <div id="inner">
        内容
    </div>
</div>
*/})

在浏览器环境中,也可以直接在 HTML 中写模板代码,然后 JavaScript 读取内容:

1
2
3
4
5
6
7
<script id="template" type="text/template">
    <div id="outer">
        <div id="inner">
            内容
        </div>
    </div>
</script>
1
var html = document.getElementById('template').textContent

讲了这么多种书写多行字符串的方法,有什么用处呢?
其实什么用都没,大部分场景,无脑使用 ES6 的模板字面量即可。


全文完

READ MORE...

WebSocket

WebSocket 是一个比较新的浏览器 API,有着诸多激动人心的特性。

参考 RFC6455 规范。

概述

WebSocket 可以用来实现客户端和服务器的实时通讯,并且比较其他实时通讯技术,例如轮询,有着独特的优势,如实时性更好、网络开销更低等等。

WebSocket 使用 HTTP 协议建立连接,但并不是使用 HTTP 传输数据,而是在建立连接后,会切换到一个自定义协议,体现在 URL 上为:ws://wss://( 加密版,类似 HTTP 与 HTTPS 的关系 )。

使用自定义协议带来了一个好处是,每次通讯可以发送更少的数据。

而使用 HTTP 请求的时候,单单首部字段带会引入很多流量消耗。一个主要的原因是,HTTP 请求是无状态的,服务器不会记住请求是谁发送的,每次请求都需要携带识别用的信息。

而 WebSocket 是可以记住状态的,于是可以使用自定义协议仅发送真正有意义的数据,从而节省大量的流量开销。

这在移动应用上特别重要。

WebSocket 分为握手和传输两部分。

其中握手阶段,是使用 HTTP 请求的,并有个协议升级的过程,可以从 HTTP 请求头中观察到这个过程,下面详细说明。

READ MORE...