鼠标移出窗口外释放的事件触发现象

mouseup 之类的事件,鼠标移出窗口(或 iframe)时,释放点击,不会触发,除非事件绑定在 documentElement、document、window 上面。

例如:

window.addEventListener('mouseup', () => { console.log('on mouse up(window, capture)') }, true)
document.addEventListener('mouseup', () => { console.log('on mouse up(doc, capture)') }, true)
document.documentElement.addEventListener('mouseup', () => { console.log('on mouse up(html, capture)') }, true)
document.body.addEventListener('mouseup', () => { console.log('on mouse up(body, capture)') }, true)
window.addEventListener('mouseup', () => { console.log('on mouse up(window)') }, false)
document.addEventListener('mouseup', () => { console.log('on mouse up(doc)') }, false)
document.documentElement.addEventListener('mouseup', () => { console.log('on mouse up(html)') }, false)
document.body.addEventListener('mouseup', () => { console.log('on mouse up(body)') }, false)
  1. 当鼠标在页面中点击,然后直接释放点击时,控制台输出
on mouse up(window, capture)
on mouse up(doc, capture)
on mouse up(html, capture)
on mouse up(body, capture)
on mouse up(body)
on mouse up(html)
on mouse up(doc)
  1. 当鼠标在页面中点击,然后移动到窗口外释放点击时,控制台输出
on mouse up(window, capture)
on mouse up(doc, capture)
on mouse up(html, capture)
on mouse up(html)
on mouse up(doc)

该行为可能对自己实现一些 drag & drop 逻辑时有一定的影响。

READ MORE...

使用 Vue 实现一个虚拟列表

大列表问题

因为 DOM 性能瓶颈,大型列表存在难以克服的性能问题。 因此,就有了 “局部渲染” 的优化方案,这就是虚拟列表的核心思想。

虚拟列表的实现,需要重点关注的问题一有以下几点:

  1. 可视区域的计算方法
  2. 可视区域的 DOM 更新方案
  3. 事件的处理方案

下面逐一分解说明。

可视区域计算

可视区域的计算,就是使用当前视口的高度、当前滚动条滚过的距离,得到一个可视区域的坐标区间。 算出可视区域的坐标区间之后,在去过滤出落在该区间内的列表项,这个过程,列表项的坐标也是必须能算出的。

思考以下情况,

  1. 我们的视口高度为 100px
  2. 我们当前已经滚动了 100px
  3. 我们的列表项,每一项高度为 20px

根据这些条件,我们可以计算出,当前可视区域为第 11 项至第 20 项。

01 - 05,可视区域上方
+----+-----------+--------
| 06 | 100 ~ 120 |
+----+-----------+
| 07 | 120 ~ 140 |
+----+-----------+
| 08 | 140 ~ 160 | 可视区域
+----+-----------+
| 09 | 160 ~ 180 |
+----+-----------+
| 10 | 180 ~ 200 |
+----+-----------+--------
11 - N,可视区域下方

这是因为列表项高度是固定的,我们可以通过简单的四则运算算出已经滚动过去的 100px 中,已经滚动走了 5 个列表项,因此可视区域是从第 6 项开始,而视口高度为 100px,能容纳 100 / 20 即 5 个条目。

上面例子的情况非常简单,也不存在性能问题,因此实际上并没有展开讨论的价值。 而还有另一种复杂很多的情况,那就是,列表项高度不固定,根据内容决定高度。

此时,我们就没办法直接使用四则运算一步到位算出可视区域对应的条目了。

而必须实现一种机制,记录所有列表项的坐标,再通过检查列表项是否落在视口内。

下面重点讨论该问题。

READ MORE...

JavaScript 中的 “相等” 算法

整理笔记本时,看到以往一些对 JavaScript 中 “相等” 关系的记录。 尽管这是一个被说烂了的话题,但写一篇资料整理总结,当作复习下也不错。

JavaScript 的 “相等” 比较算法,按严格程度来排,有以下几种:

  1. == 双等号
  2. === 三等号
  3. SameValueZero 算法
  4. SameValue 算法

下面逐个介绍它们的特点。

READ MORE...

JavaScript 中的柯里化

不过多介绍柯里化的定义和应用场景,直接看实现。


柯里化实现

如果我们有以下加法函数:

const add = (a, b) => a + b

要怎么实现柯里化呢?


手工柯里化

我们可以选择直接手写柯里化的 add:

// 手工柯里化
const curryingAdd = a => b => a + b
// 使用
const inc = curryingAdd(1)
inc(1) // 2

通过闭包的嵌套,我们可以实现每次只接受一个参数的手工柯里化的函数。

但是这种方式,对于每个需要柯里化的函数,都需要侵入其实现,非常麻烦。因此并没有什么实用价值。我们需要能一种通用的方案来自动化做这个事情。


自动柯里化

柯里化的实现,核心在于根据参数数量选择返回接受剩余参数的新函数,或者返回最终结果。

在 JavaScript 中,具备实现这一切的条件,我们可以通过函数的 length 得知形参的数量,通过 arguments 对象得知实参的信息,通过闭包返回新函数也不在话下。下面开始尝试实现,先从简单的两个参数的情况开始:

READ MORE...

λ 演算(λ-calculus)笔记

文中涉及到代码的地方,除了 λ 表达式,都尽量提供 Scheme 以及 JavaScript 代码做对照,以便加深理解。

先从基本概念开始。λ 演算包含构建 λ 项和对 λ 项执行操作。

λ 项 (lambda terms) 构建语法

  • 变量 ( variable )

    expression -> variable

  • 应用 ( application )

    expression -> expression expression

  • 抽象化 ( abstraction )

    expression -> λ variable . expression

为了不引起歧义,可以适当使用括号进行分组 ( grouping ):

expression -> (expression)

变量

可以理解为编程语言中的变量、标识符(一些用来绑定值的名称),例如 a , b, c 等等。

应用

应用是指将函数应用 ( applying ) 在实参 ( argument ) 上面。注连续多个应用是左结合的。

举例说明:

READ MORE...