学习笔记 2020-11-12
JavaScript 高级程序设计(第4版) 阅读记录
DOM 扩展
HTML5
插入标记
内存与性能问题
如果被移除的子树元素中之前有关联的事件处理程序或其他 JavaScript 对象。那它们之间的绑定关系会滞留在内存中。在使用 innerHTML 、outerHTML 和 insertAdjacentHTML() 之前,最好手动删除要被替换的元素上关联的事件处理程序和 JavaScript 对象。
HTML 解析器会解析设置给 innerHTML 的值,解析器在浏览器中是底层代码,比 JavaScript 快得多。
跨站点脚本
innerHTML 不会执行自己创建的
<script>
标签,但仍然向恶意用户暴露了很大的攻击面。可以通过它创建元素并执行 onclick 之类的属性。
scrollIntoView()
scrollIntoView() 方法存在于所有 HTML 元素上,可以滚动浏览器窗口或容器元素以便包含元素进入视口。方法参数如下:
- alignToTop ,布尔值。
- true 表示窗口滚动后元素顶部与视口顶部对齐。
- false 表示窗口滚动后元素底部与视口底部对齐。
- scrollIntoViewOptions,可选对象。
- behavior,定义过渡动画,可取的值为 smooth 和 auto,默认为 auto 。
- block,定义垂直方向的对齐,可取的值为 start、center、end 和 nearest,默认为 start。
- inline,定义水平方向的对齐,可取的值为 start、center、end 和 nearest,默认为 nearest。
- 不传参数等同于第一个参数为 true。
专有扩展
children 属性
IE9 之前的版本与其他浏览器在处理空白文本节点上的差异导致了 children 属性的出现。children 属性是一个 HTMLCollection ,只包含元素的 Element 类型的子节点。
contains() 方法
IE 首先引入了 contains() 方法,在要搜索的祖先元素上调用,参数是待确定的目标节点。
使用 DOM Level 3 的 compareDocumentPosition() 方法也可以确定节点间的关系。这个方法会返回表示两个节点关系的位掩码。
掩 码 | 节点关系 |
---|---|
0x1 | 断开(传入的节点不在文档中) |
0x2 | 领先(传入的节点在 DOM 树中位于参考节点之前) |
0x4 | 随后(传入的节点在 DOM 树中位于参考节点之后) |
0x8 | 包含(传入的节点是参考节点的祖先) |
0x10 | 被包含(传入的节点是参考节点的后代) |
插入标记
innerText
该属性对应元素中包含的所有文本内容,无论文本在子树中哪个层级。在用于读取值时,innerText 会按照深度优先的顺序将子树中所有文本节点的值拼接起来。在用于写入值时,innerText 会移除元素的所有后代并插入一个包含该值的文本节点。
outerText
类似于 innerText,只不过作用范围包含调用它的节点。在读取值时,两者表现相似。在写入值时,outerText 不止会移除所有后代节点,还会替换整个元素。
DOM2 和 DOM3
DOM1 主要定义了 HTML 和 XML 文档的底层结构。DOM2 和 DOM3 在这些结构之上加入更多交互能力,提供了更高级的 XML 特性。
- DOM Core::在 DOM1 核心部分的基础上,为节点增加方法和属性。
- DOM Views:定义基于样式信息的不同试图。
- DOM Events:定义通过事件实现 DOM 文档交互。
- DOM Style:定义以编程方式访问和修改 CSS 样式的接口。
- DOM Traversal and Range:新增遍历 DOM 文档及选择文档内容的接口。
- DOM HTML:在 DOM1 HTML 部分的基础上,增加属性、方法和新接口。
- DOM Mutation Observers:定义基于 DOM 变化触发回调的接口。这个模块是 DOM4 级模块,用于取代 Mutation Events。
DOM 的演进
DOM2 和 DOM3 Core 模块的目标是扩展 DOM API,满足 XML 的所有需求并提供更好的错误处理和特性检测。
XML 命名空间
现代 JavaScript 教程
递归和堆栈
任务
对数字求和到给定值
编写一个函数
sumTo(n)
计算1 + 2 + ... + n
的和。举个例子:
sumTo(1) = 1 sumTo(2) = 2 + 1 = 3 sumTo(3) = 3 + 2 + 1 = 6 sumTo(4) = 4 + 3 + 2 + 1 = 10 ... sumTo(100) = 100 + 99 + ... + 2 + 1 = 5050
用三种方式实现:
- 使用循环。
- 使用递归,对
n > 1
执行sumTo(n) = n + sumTo(n-1)
。 - 使用 等差数列 求和公式.
结果示例:
function sumTo(n) { /*... 你的代码 ... */ } alert( sumTo(100) ); // 5050
P.S. 哪种解决方式最快?哪种最慢?为什么?
P.P.S. 我们可以使用递归来计算
sumTo(100000)
吗?function sumTo(n) { let sum = 0; while (n) { sum += n; n--; } return sum; } function sumTo(n) { if (n === 0) return n; return n + sumTo(n - 1); } function sumTo(n) { return ((n + 1) * n) / 2; }
计算阶乘
自然数的 阶乘 是指,一个数乘以
数字减去 1
,然后乘以数字减去 2
,以此类推直到乘以1
。n
的阶乘被记作n!
。我们可以将阶乘的定义写成这样:
n! = n * (n - 1) * (n - 2) * ...*1
不同
n
的阶乘的值:1! = 1 2! = 2 * 1 = 2 3! = 3 * 2 * 1 = 6 4! = 4 * 3 * 2 * 1 = 24 5! = 5 * 4 * 3 * 2 * 1 = 120
任务是编写一个函数
factorial(n)
使用递归调用计算n!
。alert( factorial(5) ); // 120
P.S. 提示:
n!
可以被写成n * (n-1)!
,比如3! = 3*2! = 3*2*1! = 6
。function factorial(n) { if (n === 1) return n; return n * factorial(n - 1); }
斐波那契数
斐波那契数 序列有这样的公式:
Fn = Fn-1 + Fn-2
。换句话说,下一个数字是前两个数字的和。前两个数字是
1
,然后是2(1+1)
,然后3(1+2)
,5(2+3)
等:1, 1, 2, 3, 5, 8, 13, 21...
。斐波那契数与 黄金比例 以及我们周围的许多自然现象有关。
编写一个函数
fib(n)
返回第n
个斐波那契数。工作示例:
function fib(n) { /* 你的代码 */ } alert(fib(3)); // 2 alert(fib(7)); // 13 alert(fib(77)); // 5527939700884757
P.S. 函数运行速度要快,对
fib(77)
的调用不应该超过几分之一秒。// 参考解法 function fib(n) { let a = 1; let b = 1; for (let i = 3; i <= n; i++) { let c = a + b; a = b; b = c; } return b; }
输出一个单链表
假设我们有一个单链表(在 递归和堆栈 那章有讲过):
let list = { value: 1, next: { value: 2, next: { value: 3, next: { value: 4, next: null } } } };
编写一个可以逐个输出链表元素的函数
printList(list)
。使用两种方式实现:循环和递归。
哪个更好:用递归还是不用递归的?
function printList(list) { let tmp = list; while (tmp) { console.log(tmp.value); tmp = tmp.next; } } function printList(list) { if (list) { console.log(list.value); printList(list.next); } }
反向输出单链表
反向输出前一个任务 输出一个单链表 中的单链表。
使用两种解法:循环和递归。
function printList(list) { if (list) { printList(list.next); console.log(list.value); } } function printList(list) { let tmp = list; const arr = []; while (tmp) { arr.push(tmp.value); tmp = tmp.next; } while (arr.length) { console.log(arr.pop()); } }