0%

学习笔记 2020 11 07

学习笔记 2020-11-07

JavaScript 高级程序设计(第4版) 阅读记录

DOM

节点类型

Node 类型
  1. 操纵节点

    因为所有关系指针都是只读的,所以 DOM 提供了一些操纵节点的方法。

    • appendChild():用于在 childNodes 列表末尾添加节点。

      let returnedNode = someNode.appendChild(newNode);
      console.log(returnedNode == newNode); // true
      console.log(someNode.lastChild == newNode); // true

      已存在的节点传给该方法,会使得该节点被转移到新位置。

    • insertBefore():用于把节点放到指定位置。接收两个参数,要插入的节点和参照节点。

      // 作为最后一个子节点插入
      returnedNode = someNode.insertBefore(newNode, null);
      console.log(newNode == someNode.lastChild); // true
      // 作为新的第一个子节点插入
      returnedNode = someNode.insertBefore(newNode, someNode.firstChild);
      console.log(returnedNode == newNode); // true
      console.log(newNode == someNode.firstChild); // true
      // 插入最后一个子节点前面
      returnedNode = someNode.insertBefore(newNode, someNode.lastChild);
      console.log(newNode == someNode.childNodes[someNode.childNodes.length - 2]); // true
    • replaceChild():替换节点。接收两个参数,要插入的节点和要替换的节点。返回要替换的节点。

      // 替换第一个子节点
      let returnedNode = someNode.replaceChild(newNode, someNode.firstChild);
      // 替换最后一个子节点
      returnedNode = someNode.replaceChild(newNode, someNode.lastChild);
    • removeChild():移除节点。接收要被移除的节点。返回被移除的节点。

      // 删除第一个子节点
      let formerFirstChild = someNode.removeChild(someNode.firstChild);
      // 删除最后一个子节点
      let formerLastChild = someNode.removeChild(someNode.lastChild);

    以上所有方法都用于操纵某节点的子元素。

  2. 其他方法

    所有节点类型还共享了两个方法:

    • cloneNode():返回与调用它的节点一模一样的节点,接收一个布尔值参数,表示是否深复制。深复制会复制节点以及整个子 DOM 树,浅复制只会复制调用该方法的节点。该方法不会复制添加到 DOM 节点的 JS 属性,例如事件处理程序。
    • normalize():处理文档子树中的文本节点。在节点上调用该方法会检测这个节点的所有后代,从中搜索上述两种情形。删除空文本节点。合并两个相邻的同胞节点。
Document 类型

Document 类型是 JavaScript 中表示文档节点的类型。在浏览器中,文档对象 document 是 HTMLDocument 的实例 ( HTMLDocument 继承 Document ),表示整个 HTML 页面。document 是 window 对象的属性,是一个全局对象。Document 类型的节点有以下特征:

  • nodeType 为 9;
  • nodeName 值为 #document
  • nodeValue 值为 null ;
  • parentNode 值为 null ;
  • ownerDocument 值为 null;
  • 子节点可以是 DocumentType ( 最多一个 )、Element ( 最多一个 )、ProcessingInstruction 或 Comment 类型。

Document 类型可以表示 HTML 页面或其他 XML 文档,但最常用的还是通过 HTMLDocument 的实例取得 document 对象。document 对象可用于获取关于页面的信息以及操纵其外观和底层结构。

  1. 文档子节点

    Document 节点有两个访问子节点的快捷方式:

    • documentElement 属性,指向 HTML 页面的 <html> 元素。

      let html = document.documentElement; // 取得对<html>的引用
      console.log(html === document.childNodes[0]); // true
      console.log(html === document.firstChild); // true
    • body 属性,指向 <body> 元素。

    Document 类型另一种可能的子节点是 DocumentType 。

    <!doctype> 标签是文档中独立的部分,其信息可以通过 doctype 属性来访问。

    文档类型是只读的,只能有一个 Element 类型的子节点,即 <html>

  2. 文档信息

    document 作为 HTMLDocument 的实例,还有一些标准 Document 对象上所没有的属性。

    • title,包含 <title> 元素中的文本。通过这个属性可以读写页面的标题,但修改该元素不会改变 <title> 元素。
    • URL:包含当前页面的完整 URL 。
    • domain:包含页面的域名。
    • referrer:包含链接到当前页面的那个页面的 URL 。

    以上三个属性,只有 domain 是可以设置的,且设置的值受限。

    // 页面来自 p2p.wrox.com
    document.domain = 'wrox.com'; // 成功
    document.domain = 'nczonline.net'; // 出错!

    当页面中包含来自不同子域的窗格或内嵌窗格时,设置 document.domain 是有用的,可以避开跨域通信,使得彼此可以访问 JavaScript 对象。

    浏览器对 domain 属性还有一个限制,这个属性一旦放松就不能再收紧。

    // 页面来自 p2p.wrox.com
    document.domain = 'wrox.com'; // 放松,成功
    document.domain = 'p2p.wrox.com'; // 收紧,错误!
  3. 定位元素

    • getElementById() 接收一个参数,即要获取元素的 ID 。返回第一个匹配的元素或 null 。
    • getElementsByTagName() 接收一个参数,即要获取元素的标签名。返回包含零个或多个元素的 NodeList 。在 HTML 文档中,这个方法返回一个 HTMLCollection 对象。HTMLCollection 对象与 NodeList 很相似。它有一个额外的方法 namedItem() ,可通过标签的 name 属性取得某一项的引用。对于有 name 属性的元素,还可以直接使用中括号来获取。
    • getElementsByName() 接收一个参数,元素的 name 属性。
  4. 特殊集合

    • document.anchors 包含文档中所有带 name 属性的 <a> 元素。
    • document.applets 包含文档中所有 <applet> 元素。
    • document.forms 包含文档中所有 <form> 元素。
    • document.images 包含文档中所有 <img> 元素。
    • document.links 包含文档中所有带 href 属性的 <a> 元素。
  5. DOM 兼容性检测

    DOM Level 1 在 document.implementation 上只定义了一个方法,即 hasFeature() 。这个方法接收两个参数:特性名称和 DOM 版本。

    let hasXmlDom = document.implementation.hasFeature("XML", "1.0");
    特性 支持的版本 说明
    Core 1.0、2.0、3.0 定义树形文档结构的基本 DOM 。
    XML 1.0、2.0、3.0 Core 的 XML 拓展,增加了对 CDATA 区块】处理指令和实体的支持。
    HTML 1.0、2.0 XML 的 HTML 拓展,增加了 HTML 特点的元素和实体。
    Views 2.0 文档基于某些样式的实现格式。
    StyleSheets 2.0 文档的相关样式表
    CSS 2.0 Cascading Style Sheets Level 1
    CSS2 2.0 Cascading Style Sheets Level 2
    Events 2.0、3.0 通用 DOM 事件
    UIEvents 2.0、3.0 用户界面事件
    TextEvents 3.0 文本输入设备触发的事件
    MouseEvents 2.0、3.0 鼠标导致的事件
    MutationEvents 2.0、3.0 DOM 树变化时触发的事件
    MutationNameEvents 3.0 DOM 元素或元素属性被重命名时触发的事件
    HTMLEvents 2.0 HTML 4.01 事件
    Range 2.0 在 DOM 树中操作一定范围的对象和方法
    Traversal 2.0 遍历 DOM 树的方法
    LS 3.0 文件与 DOM 树之间的同步加载与保存
    LS-Async 3.0 文件与 DOM 树之间的异步加载与保存
    Validation 3.0 修改 DOM 树并保证其继续有效的方法
    XPath 3.0 访问 XML 文档不同部分的语言

    目前该方法已被废弃。

  6. 文档写入

    • write()
    • writeln()
    • open()
    • close()

    write() 和 writeln() 都接收一个字符串参数,将这个字符串写入网页。

    在页面加载过程中,两个方法会向文档中输出内容,在页面加载完后调用会重写整个页面。

    open() 和 close() 用于打开和关闭网页输出流。

Element 类型

Element 表示 XML 或 HTML 元素,对外暴露出访问元素标签名、子节点和属性的能力。该类型的节点具有以下特征:

  • nodeType 等于 1;
  • nodeName 值为元素的标签名;
  • nodeValue 值为 null ;
  • parentNode 值为 Document 或 Element 对象;
  • 子节点可以是 Element、Text、Comment、ProcessingInstruction、CDATASection、EntityReference 类型。

可以通过 nodeName 或 tagName 属性来获取元素的标签名。这两个属性返回同样的值。

在 HTML 中,元素标签名始终以全大写表示。

现代 JavaScript 教程

任务

  1. 存储 “unread” 标识

    这里有一个 messages 数组:

    let messages = [
      {text: "Hello", from: "John"},
      {text: "How goes?", from: "John"},
      {text: "See you soon", from: "Alice"}
    ];

    你的代码可以访问它,但是 message 是由其他人的代码管理的。该代码会定期添加新消息,删除旧消息,但是你不知道这些操作确切的发生时间。

    现在,你应该使用什么数据结构来保存关于消息“是否已读”的信息?该结构必须很适合对给定的 message 对象给出“它读了吗?”的答案。

    P.S. 当一个消息被从 messages 中删除后,它应该也从你的数据结构中消失。

    P.S. 我们不能修改 message 对象,例如向其添加我们的属性。因为它们是由其他人的代码管理的,我们修改该数据可能会导致不好的后果。

    let messages = [
      { text: 'Hello', from: 'John' },
      { text: 'How goes?', from: 'John' },
      { text: 'See you soon', from: 'Alice' }
    ];
    
    let readMessages = new WeakSet();
    
    // 两个消息已读
    readMessages.add(messages[0]);
    readMessages.add(messages[1]);
    // readMessages 包含两个元素
    
    // ……让我们再读一遍第一条消息!
    readMessages.add(messages[0]);
    // readMessages 仍然有两个不重复的元素
    
    // 回答:message[0] 已读?
    console.log('Read message 0: ' + readMessages.has(messages[0])); // true
    
    messages.shift();
    // 现在 readMessages 有一个元素(技术上来讲,内存可能稍后才会被清理)
  2. 保存阅读日期

    这儿有一个和 上一个任务 类似的 messages 数组。场景也相似。

    let messages = [
      {text: "Hello", from: "John"},
      {text: "How goes?", from: "John"},
      {text: "See you soon", from: "Alice"}
    ];

    现在的问题是:你建议采用什么数据结构来保存信息:“消息是什么时候被阅读的?”。

    在前一个任务中我们只需要保存“是/否”。现在我们需要保存日期,并且它应该在消息被垃圾回收时也被从内存中清除。

    P.S. 日期可以存储为内建的 Date 类的对象,稍后我们将进行介绍。

    let messages = [
      { text: 'Hello', from: 'John' },
      { text: 'How goes?', from: 'John' },
      { text: 'See you soon', from: 'Alice' }
    ];
    
    let readMap = new WeakMap();
    
    readMap.set(messages[0], new Date(2017, 1, 1));