# JavaScript

2023年10月13日44分钟阅读

- FormalSciences/ComputerScience
- Webdevelopment/JavaScript

网页的心跳，使静态的网页获得了动态的脉搏。

## JavaScript

JavaScript 是一种动态的、解释的编程语言，广泛应用于 Web 开发中。JavaScript 通过 BOM（浏览器对象模型）和 DOM（文档对象模型）与浏览器进行交互。

BOM 提供了访问和操作浏览器窗口的能力，包括窗口大小、导航、定时器、对话框等。DOM 则是对网页内容的抽象，它将网页文档结构化为一个节点树，允许开发者查询和操作页面内容。

简单来说，JavaScript 是语言，DOM 是这个语言可以操作的对象之一（网页内容），BOM 也是这个语言可以操作的对象之一（浏览器窗口及其相关属性和方法）。

    graph TB
        A[JavaScript] -- Interacts with --> B[BOM]
        A -- Interacts with --> C[DOM]
        B -- Manages --> D[Browser Window]
        C -- Manages --> E[Web Page Content]

这个图展示了 JavaScript、BOM、DOM 之间的关系。其中，JavaScript 与 BOM 和 DOM 进行交互，BOM 管理浏览器窗口，DOM 管理网页内容。

### 基础语法

JavaScript 的基础语法相对简单,主要包含变量、数据类型、运算符、语句与注释等。

#### 变量

JavaScript 通过 `var`、`let` 和 `const` 关键词来声明变量，它们之间存在明显的区别：

- `var` 是函数作用域的变量，也就是说，如果你在一个函数内部声明了一个 `var` 变量，那么它只能在这个函数内部被访问。
- `let` 和 `const` 都是块级作用域的变量，它们只能在声明它们的代码块或者更小的作用域内被访问。

`var` 存在变量提升的特性，即在声明之前使用变量不会报错，但是值为 `undefined`。而 `let` 和 `const` 不存在变量提升，它们具有所谓的暂时性死区特性。

    // var 声明的变量可以重复声明,会发生变量提升
    var name = 'John';
    var name = 'Peter';

    // let 声明的变量不可以重复声明,有块级作用域
    let age = 25;
    let age = 30; // 报错

    // const 声明的是常量,初始化后值不可修改
    const pi = 3.14;
    pi = 3; // 报错

#### 数据类型

JavaScript 中有七种数据类型:

数据类型 | 说明 | 示例
--- | --- | ---
Number | 包括整数和浮点数 | 123, 3.14
String | 字符串,使用单引号或双引号包裹 | ’hello’, “world”
Boolean | true 和 false | true, false
Null | 表示无值 | null
Undefined | 表示值未定义 | undefined
Symbol | 表示独一无二的值 | Symbol(‘id’)
BigInt | 可表示任意大的整数 | 9007199254740991n

JavaScript 中还有一种复杂数据类型 Object，表示由多个键值对组成的对象。可以使用 `typeof` 来判断一个变量的类型：

    let n = 123;
    let s = 'Hello';
    let b = true;

    typeof n; // 'number'
    typeof s; // 'string'
    typeof b; // 'boolean'

#### 运算符

JavaScript 支持多种运算符，包括：

运算符类型 | 运算符 | 说明 | 示例
--- | --- | --- | ---
算术运算符 | + - * / % ++ — | 用于执行算术运算 | x + y
赋值运算符 | = += -= *= /= %= **= | 用于给变量赋值 | x += 1
比较运算符 | = != !== > < >= ⇐ | 用于比较两个值 | x == y
逻辑运算符 | && || ! | 用于组合条件 | x > 0 && y < 0
条件运算符 | ? : | 根据条件选择值 | condition ? val1 : val2
位运算符 | & | ^ ~ << >> >>> | 用于位运算 | x & y

    let a = 1 + 2; // 3
    let b = 'Hello' + ' ' + 'World!'; // 'Hello World!'

    console.log(a == b); // false
    console.log(a != b); // true

    console.log(a > 0 && b.includes('Hello')); // true

#### 语句

JavaScript 提供了多种控制结构，包括条件语句、循环语句、分支语句和跳转语句。

##### 条件语句

使用 `if/else` 语句进行条件判断：

    if (condition) {
      // 条件为真时执行
    } else {
      // 条件为假时执行
    }

`if/else` 语句可以进行多重条件判断：

    if (n > 0) {
      // n 大于 0
    } else if (n < 0) {
      // n 小于 0
    } else {
      // n 等于 0
    }

##### 循环语句

使用 `for` 循环和 `while` 循环进行迭代操作：

    // for 循环
    for (let i = 0; i < 5; i++) {
      console.log(i);
    }

    // while 循环
    let j = 0;
    while (j < 5) {
      console.log(j);
      j++;
    }

##### 分支语句

使用 `switch` 语句进行多分支判断：

    switch(fruit) {
      case 'apple':
        console.log('苹果');
        break;
      case 'banana':
        console.log('香蕉');
        break;
      default:
        console.log('其他水果');
    }

##### 跳转语句

`break` 用于退出循环，`continue` 用于跳过当前循环：

    for (let i = 0; i < 10; i++) {
      if (i % 2 === 0) {
        continue; // 跳过偶数
      }
      console.log(i);

      if (i > 5) {
        break; // 当 i 大于 5 时退出循环
      }
    }

另外，JavaScript 支持使用注释来增加代码的可读性：

    // 这是单行注释

    /*
    这是多行注释，可以注释一段代码
    */

#### 函数

函数是 JavaScript 中组织代码的基本单元，可以封装一段可以重复执行的代码块：

    // 函数声明
    function add(x, y) {
      return x + y;
    }

    // 函数表达式
    const subtract = function(x, y) {
      return x - y;
    };

    // 箭头函数（ES6 新增）
    const multiply = (x, y) => {
      return x * y;
    };

函数可以有参数，也可以有返回值。在 JavaScript 中，函数也是一种对象，可以被赋值给变量，也可以作为其他函数的参数或返回值。

#### 数组

数组是一种特殊的对象，用于存储一系列的值：

    let nums = [1, 2, 3];
    let fruits = ['Apple', 'Banana', 'Orange'];

    nums.push(4); // 添加元素
    nums.pop(); // 删除最后一个元素
    nums.includes(2); // 检查是否包含某个元素

在 JavaScript 中，数组是动态的，可以随时添加或删除元素。数组的长度可以通过其 `length` 属性获取。

#### 对象

对象是 JavaScript 的核心概念，它是由一组键值对构成的数据集合：

    let person = {
      name: 'John',
      age: 20,
      sayHi: function() {
        console.log('Hi!');
      }
    };

    console.log(person.name); // 访问属性
    person.sayHi(); // 调用方法

**在 JavaScript 中，几乎所有的东西都是对象。**函数、数组、日期和正则表达式等都是特殊的对象。对象的属性和方法可以被动态创建和删除，非常灵活。

#### 其他特性

JavaScript 还有许多其他高级特性，例如原型和继承、闭包、异步处理和模块等。

##### 原型和继承

JavaScript 是基于原型的语言，每个对象都有一个指向它的原型（prototype）的链接。当试图访问一个对象的属性时，JavaScript 会首先在对象本身的属性中搜索，如果找不到，就会在它的原型对象中搜索。

    function Person(name) {
      this.name = name;
    }

    Person.prototype.sayName = function() {
      console.log(this.name);
    };

    let person1 = new Person('John');
    person1.sayName(); // 'John'

JavaScript 的继承机制是基于原型的。我们可以通过设置一个类型的原型为另一个类型的实例，来实现继承。

    function Student(name, grade) {
      Person.call(this, name); // 继承属性
      this.grade = grade;
    }

    Student.prototype = Object.create(Person.prototype); // 继承方法
    Student.prototype.constructor = Student;

    let student1 = new Student('Tom', 5);
    console.log(student1.name); // Tom
    console.log(student1.grade); // 5
    student1.sayName(); // Tom

##### 闭包

闭包（closure）是 JavaScript 中一个重要的概念。在 JavaScript 中，函数可以形成闭包。闭包是指有权访问另一个函数作用域中的变量的函数。

    function createCounter() {
      let count = 0;
      return function() {
        return ++count;
      };
    }

    let counter = createCounter();
    console.log(counter()); // 1
    console.log(counter()); // 2

闭包的一个常见应用是使函数有私有变量。在上面的例子中，变量 `count` 就像 `createCounter` 函数的私有变量，外部无法访问，只能通过闭包来操作。

##### 异步处理

JavaScript 是单线程的，但是它可以通过事件和回调函数来处理异步操作。例如，我们可以使用 `setTimeout` 函数来延迟执行一段代码：

    console.log('Hello');
    setTimeout(function() {
      console.log('World');
    }, 1000);
    console.log('!');

这段代码会首先输出 ‘Hello’ 和 ’!’，然后等待一秒钟后，再输出 ‘World’。虽然 `setTimeout` 函数在 ’!’ 之前调用，但是它不会阻塞代码的执行。

在 ES6 中，JavaScript 引入了 Promise 和 async/await，使得异步编程更加方便和强大。

##### 模块

在现代的 JavaScript 开发中，我们通常会把代码分成多个模块（module），每个模块负责一部分功能。模块可以导入（import）其他模块，也可以导出（export）自己的接口给其他模块使用。

    // math.js
    export function add(x, y) {
      return x + y;
    }

    // main.js
    import { add } from './math.js';
    console.log(add(1, 2)); // 3

模块可以使我们的代码更加结构化和可维护。

### BOM 模型

**浏览器对象模型（Browser Object Model，BOM）是用于与浏览器交互的模型。**它提供了一种方式，使开发者能够通过 JavaScript 访问和操作浏览器的窗口。

BOM 主要由一系列的对象组成，这些对象提供了许多方法和属性，使得开发者能够处理浏览器窗口和文档的加载、大小、位置等各种情况，以及进行网络通信等操作。

BOM 的核心对象是 `window`，它代表了浏览器的窗口，也是 JavaScript 的全局对象。除此之外，BOM 还包括了其他一些对象，例如：

- `location` 对象：提供了当前窗口中加载的文档的 URL 信息，以及用于导航的方法。
- `navigator` 对象：提供了当前浏览器的信息，比如版本、名称等。
- `history` 对象：提供了浏览器历史的信息，以及用于导航的方法。
- `document` 对象：提供了当前加载在窗口中的 HTML 文档的访问和操作的方式。

    classDiagram
        Window --|> Document: "document"
        Window --|> Navigator: "navigator"
        Window --|> History: "history"
        Window --|> Location: "location"
        Window --|> Screen: "screen"
        Window --|> Frames: "frames"
        Window --|> Performance: "performance"
        Window --|> localStorage: "localStorage"
        Window --|> sessionStorage: "sessionStorage"
        Window --|> console: "console"
        class Window {
            +open()
            +close()
            +getComputedStyle()
            +setTimeout()
            +setInterval()
            +clearTimeout()
            +clearInterval()
            +fetch()
        }
        class Document {
            +getElementById(id)
            +getElementsByClassName(name)
            +getElementsByTagName(name)
            +querySelector(selector)
            +querySelectorAll(selector)
            +createElement(tagName)
            +createTextNode(data)
        }
        class Navigator {
            +appName
            +appVersion
            +userAgent
            +platform
            +language
            +onLine
            +geolocation
            +cookieEnabled
            +javaEnabled()
        }
        class History {
            +back()
            +forward()
            +go(n)
            +pushState()
            +replaceState()
        }
        class Location {
            +href
            +protocol
            +host
            +hostname
            +port
            +pathname
            +search
            +hash
            +reload()
            +assign(URL)
            +replace(URL)
        }
        class Screen {
            +availWidth
            +availHeight
            +width
            +height
            +colorDepth
            +pixelDepth
        }
        class Frames
        class Performance
        class localStorage {
            +setItem()
            +getItem()
            +removeItem()
            +clear()
        }
        class sessionStorage {
            +setItem()
            +getItem()
            +removeItem()
            +clear()
        }
        class console {
            +log()
            +info()
            +warn()
            +error()
        }

需要注意的是，**BOM 并没有一个标准的定义，不同的浏览器可能实现了不同的 BOM。**但是，现代的浏览器已经在很大程度上实现了 BOM 的兼容性。

#### 窗口对象

窗口对象（Window Object）是 BOM 的核心对象，它代表了浏览器的窗口。所有的全局变量，函数都是它的方法和属性。例如，我们可以获取窗口的尺寸：

    let width = window.innerWidth;
    let height = window.innerHeight;

我们也可以使用 `setTimeout` 和 `setInterval` 方法来执行定时操作：

    // 打印 "Hello, world!" 然后 2 秒后关闭窗口
    window.setTimeout(function() {
        console.log("Hello, world!");
        window.close();
    }, 2000);

以下是 `window` 对象的一些常用属性和方法的表格形式：

类型 | 名称 | 描述
--- | --- | ---
属性 | `window.innerWidth` | 浏览器窗口的视口（viewport）的宽度（包括滚动条）
属性 | `window.innerHeight` | 浏览器窗口的视口（viewport）的高度（包括滚动条）
属性 | `window.outerWidth` | 浏览器窗口的外部宽度
属性 | `window.outerHeight` | 浏览器窗口的外部高度
属性 | `window.location` | 当前窗口的 `Location` 对象，包含了当前页面的 URL 信息
属性 | `window.document` | 当前窗口加载的 `Document` 对象，即 DOM（文档对象模型）
属性 | `window.history` | 当前窗口的 `History` 对象，提供了浏览历史的访问和操作
属性 | `window.screen` | 当前屏幕的 `Screen` 对象，提供了屏幕的信息
方法 | `window.alert()` | 显示一个警告对话框
方法 | `window.prompt()` | 显示一个提示对话框，并请求用户输入
方法 | `window.confirm()` | 显示一个确认对话框，并请求用户确认
方法 | `window.open()` | 打开一个新的浏览器窗口或标签页
方法 | `window.close()` | 关闭当前的浏览器窗口或标签页
方法 | `window.setTimeout()` | 设置一个定时器，在指定的毫秒数后执行一个函数或代码
方法 | `window.setInterval()` | 设置一个定时器，每隔指定的毫秒数重复执行一个函数或代码
方法 | `window.clearTimeout()` | 清除由 `setTimeout` 设置的定时器
方法 | `window.clearInterval()` | 清除由 `setInterval` 设置的定时器

#### 文档对象

文档对象（Document Object）是 HTML 文档的根节点，用于访问和操作网页内容。`document` 对象允许你访问和操作页面的内容，包括元素节点、属性节点和文本节点。

以下是 `document` 对象的一些常见属性和方法：

类型 | 名称 | 描述
--- | --- | ---
属性 | `document.documentElement` | 返回文档的根元素（在 HTML 中，通常是 `<html>` 元素）
属性 | `document.body` | 返回文档的 `<body>` 元素
属性 | `document.head` | 返回文档的 `<head>` 元素
属性 | `document.title` | 获取或设置文档的标题
方法 | `document.getElementById(id)` | 返回具有指定 ID 的元素
方法 | `document.getElementsByClassName(name)` | 返回所有具有指定类名的元素的集合
方法 | `document.getElementsByTagName(name)` | 返回所有具有指定标签名的元素的集合
方法 | `document.querySelector(selector)` | 返回匹配指定 CSS 选择器的第一个元素
方法 | `document.querySelectorAll(selector)` | 返回匹配指定 CSS 选择器的所有元素的集合
方法 | `document.createElement(tagName)` | 创建一个新的元素节点
方法 | `document.createTextNode(data)` | 创建一个新的文本节点

这些只是 `document` 对象的基本属性和方法。实际上，DOM 提供了非常多的 API 来查询和操作文档，包括添加、删除和修改元素，添加事件监听器，修改 CSS 样式等等。

例如，我们可以使用 `document.getElementById()` 来获取具有特定 ID 的元素，或者使用 `document.createElement()` 来创建新的元素：

    // 获取 ID 为 "myElement" 的元素
    let myElement = document.getElementById("myElement");

    // 创建一个新的 <p> 元素
    let newElement = document.createElement("p");
    newElement.textContent = "This is a new paragraph.";
    document.body.appendChild(newElement);

#### 导航对象

导航对象（Navigator Object）提供了关于浏览器的信息。下面是一些常见的 `navigator` 对象的属性：

类型 | 名称 | 描述
--- | --- | ---
属性 | `navigator.appName` | 返回浏览器的名称
属性 | `navigator.appVersion` | 返回浏览器的版本信息
属性 | `navigator.userAgent` | 返回用户代理头的字符串表示（包含浏览器名称、版本、操作系统等信息）
属性 | `navigator.platform` | 返回运行浏览器的操作系统平台
属性 | `navigator.language` | 返回浏览器的默认语言
属性 | `navigator.onLine` | 返回浏览器的在线状态，如果浏览器在线则返回 `true`，否则返回 `false`

另外，`navigator` 对象还包含一些用于特定功能的方法和对象，例如：

- `navigator.geolocation`：用于获取用户的地理位置信息。
- `navigator.cookieEnabled`：返回一个布尔值，表示是否启用了 cookie。
- `navigator.javaEnabled()`：返回一个布尔值，表示是否启用了 Java。

请注意，`navigator` 对象的一些属性和方法可能会因浏览器的不同而有所差异。而且，由于隐私和安全的考虑，某些信息可能需要用户的许可才能访问。

    console.log(navigator.userAgent);

#### 历史对象

历史对象（History Object）提供了浏览器历史记录的操作方法。然而，由于隐私原因，您不能获取到用户的浏览历史具体细节，但可以用 `history` 对象来操作当前窗口的浏览历史。

以下是 `history` 对象的一些常见方法：

类型 | 名称 | 描述
--- | --- | ---
方法 | `history.back()` | 和点击浏览器后退按钮一样，加载历史列表中的前一个 URL
方法 | `history.forward()` | 和点击浏览器前进按钮一样，加载历史列表中的下一个 URL
方法 | `history.go(n)` | 加载历史列表中的某个具体页面，通过参数 `n` 指定。`n` 为正数时前进，`n` 为负数时后退，`n` 为 0 时刷新当前页面
方法 | `history.pushState()` | 在历史记录中添加一个状态，但不触发页面刷新
方法 | `history.replaceState()` | 在历史记录中替换当前状态，但不触发页面刷新

注意，`pushState()` 和 `replaceState()` 方法可以改变 URL 并添加历史记录，但并不会重新加载页面。这对于创建 AJAX 驱动的动态页面非常有用。

还有一个 `history.length` 属性，它返回当前窗口历史堆栈中的 URL 数量。由于安全和隐私原因，您无法访问堆栈中的实际 URL。

例如，我们可以使用 `history.back()` 和 `history.forward()` 方法来模拟用户点击浏览器的后退和前进按钮：

    // 后退到上一个页面
    history.back();

    // 前进到下一个页面
    history.forward();

#### 位置对象

位置对象（Location Object）提供了当前窗口或标签的 URL 信息，以及一些方法来操作 URL。

以下是 `location` 对象的一些常见属性和方法：

类型 | 名称 | 描述
--- | --- | ---
属性 | `location.href` | 获取或设置整个 URL
属性 | `location.protocol` | 返回 URL 的协议部分（如：“http:” 或 “https:“）
属性 | `location.host` | 返回 URL 的主机名和端口（如果有的话）
属性 | `location.hostname` | 返回 URL 的主机名
属性 | `location.port` | 返回 URL 的端口号
属性 | `location.pathname` | 返回 URL 的路径名
属性 | `location.search` | 返回 URL 的查询字符串（包括 ”?“）
属性 | `location.hash` | 返回 URL 的片段标识符（包括 ”#“）
方法 | `location.reload()` | 重新加载当前页面
方法 | `location.assign(URL)` | 加载新的页面
方法 | `location.replace(URL)` | 替换当前页面

这些属性和方法使我们可以从 JavaScript 中读取和操作 URL 的各个部分。例如，我们可以使用 `location.href` 来获取当前页面的 URL，或者使用 `location.assign()` 来加载一个新的页面。还要注意的是，使用 `location.replace()` 方法不会在历史记录中留下记录，所以用户不能使用 ” 后退 ” 按钮返回。

例如，我们可以使用 `location.href` 来获取或设置当前页面的 URL，或者使用 `location.reload()` 来重载当前页面：

    // 打印当前页面的 URL
    console.log(location.href);

    // 重载当前页面
    location.reload();

#### 屏幕对象

屏幕对象（Screen Object）提供了关于用户的屏幕信息。这些信息包括屏幕的宽度、高度、颜色深度和像素深度等。以下是 `screen` 对象的一些常见属性：

类型 | 名称 | 描述
--- | --- | ---
属性 | `screen.availWidth` | 返回屏幕的可用宽度，以像素为单位（减去界面特性，如 Windows 任务栏）
属性 | `screen.availHeight` | 返回屏幕的可用高度，以像素为单位（减去界面特性，如 Windows 任务栏）
属性 | `screen.width` | 返回屏幕的总宽度，以像素为单位
属性 | `screen.height` | 返回屏幕的总高度，以像素为单位
属性 | `screen.colorDepth` | 返回目标设备或缓冲器上的颜色深度，以位为单位
属性 | `screen.pixelDepth` | 返回屏幕的像素深度

这些属性可以帮助你了解用户的屏幕信息，并据此调整网页的布局或设计。例如，你可以根据 `screen.width` 和 `screen.height` 来调整网页的大小，以最佳地适应用户的屏幕。

注意，`screen` 对象的属性是只读的，你不能修改它们的值。我们可以使用 `screen.width` 和 `screen.height` 来获取屏幕的宽度和高度：

    console.log("Screen width: " + screen.width);
    console.log("Screen height: " + screen.height);

### DOM 模型

**文档对象模型（Document Object Model，DOM）是一种编程接口，它允许程序和脚本动态地访问和更新文档的内容、结构和样式。**DOM 将文档解析为一个由节点和对象（包括属性和方法）构成的结构集合，这样就可以使用脚本语言（如 JavaScript）来操作这些节点和对象。

在 Web 中，最常见的文档类型是 HTML 或 XML 文档。对于这种类型的文档，DOM 表示它们为一个树状结构，其中每个节点都是一个对象，代表了文档的一部分。例如，HTML 文档中的每个元素、属性和文本都会被表示为一个 DOM 节点。

一个简单的 HTML DOM 结构可能会像下面这样：

    <!DOCTYPE html>
    <html>
      <head>
        <title>My Page</title>
      </head>
      <body>
        <h1>Welcome to My Page</h1>
        <p>Content here</p>
      </body>
    </html>

在这个 HTML 文档中，`<html>`、`<head>`、`<title>`、`<body>`、`<h1>` 和 `<p>` 元素都是 DOM 节点。`My Page` 和 `Welcome to My Page` 以及 `Content here` 这些文本也是 DOM 节点，它们是元素节点的子节点。

    classDiagram
        Document --|> HTMLElement: "elements"
        HTMLElement --|> HTMLDivElement
        HTMLElement --|> HTMLSpanElement
        HTMLElement --|> HTMLAnchorElement
        HTMLElement --|> HTMLImageElement
        HTMLElement --|> HTMLParagraphElement
        HTMLElement --|> HTMLInputElement
        HTMLElement --|> HTMLButtonElement
        HTMLElement --|> HTMLCanvasElement
        HTMLElement --|> HTMLTableElement
        class Document {
            +getElementById(id)
            +getElementsByClassName(name)
            +getElementsByTagName(name)
            +querySelector(selector)
            +querySelectorAll(selector)
            +createElement(tagName)
            +createTextNode(data)
        }
        class HTMLElement {
            +innerHTML
            +outerHTML
            +textContent
            +id
            +className
            +setAttribute(name, value)
            +getAttribute(name)
            +removeAttribute(name)
            +addEventListener(type, listener)
            +removeEventListener(type, listener)
        }
        class HTMLDivElement
        class HTMLSpanElement
        class HTMLAnchorElement {
            +href
        }
        class HTMLImageElement {
            +src
            +alt
        }
        class HTMLParagraphElement
        class HTMLInputElement {
            +value
            +checked
        }
        class HTMLButtonElement {
            +disabled
        }
        class HTMLCanvasElement
        class HTMLTableElement

开发者可以使用 DOM API 来获取、创建、修改或删除这些节点，从而实现对网页内容和结构的动态控制。这是现代网页交互性的基础。

#### 获取元素

方法 | 描述 | 示例
--- | --- | ---
`document.getElementById(id)` | 通过元素 ID 获取元素。此方法返回一个对象，表示带有指定 ID 的元素。 | `document.getElementById('myId')`
`document.getElementsByClassName(name)` | 通过类名获取元素。此方法返回一个 HTMLCollection 对象，包含了文档中所有具有指定类名的元素。 | `document.getElementsByClassName('myClass')`
`document.getElementsByTagName(name)` | 通过标签名获取元素。此方法返回一个 HTMLCollection 对象，包含了文档中所有具有指定标签名的元素。 | `document.getElementsByTagName('div')`
`document.querySelector(selector)` | 返回文档中匹配指定 CSS 选择器的第一个元素。 | `document.querySelector('.myClass')`
`document.querySelectorAll(selector)` | 返回文档中匹配指定 CSS 选择器的所有元素。此方法返回一个 NodeList 对象。 | `document.querySelectorAll('.myClass')`
`document.getElementsByName(name)` | 通过元素的 Name 属性获取元素。此方法返回一个 NodeList 对象，包含了文档中所有具有指定 Name 属性的元素。 | `document.getElementsByName('myName')`
`Element.querySelector(selector)` | 返回元素内部匹配指定 CSS 选择器的第一个元素。 | `document.getElementById('myId').querySelector('.myClass')`
`Element.querySelectorAll(selector)` | 返回元素内部匹配指定 CSS 选择器的所有元素。此方法返回一个 NodeList 对象。 | `document.getElementById('myId').querySelectorAll('.myClass')`

请注意，`Element.querySelector` 和 `Element.querySelectorAll` 与 `document.querySelector` 和 `document.querySelectorAll` 类似，但它们只在指定元素的后代中查找，而不是在整个文档中查找。

例如:

    // 获取 id 为 title 的元素
    const title = document.getElementById('title');

    // 获取所有 <p> 元素
    const ps = document.getElementsByTagName('p');

    // 获取所有 class 为 content 的元素
    const contents = document.getElementsByClassName('content');

    // 获取第一个 class 为 content 的元素
    const content = document.querySelector('.content');

#### 改变元素

方法 | 描述 | 示例
--- | --- | ---
`element.setAttribute(name, value)` | 设置元素的属性。此方法会设置或更新元素的属性。 | `element.setAttribute('class', 'myClass')`
`element.getAttribute(name)` | 获取元素的属性值。此方法返回指定属性的值，如果该属性不存在，则返回 `null`。 | `element.getAttribute('class')`
`element.removeAttribute(name)` | 删除元素的属性。此方法会完全移除元素的指定属性。 | `element.removeAttribute('class')`
`element.classList.add(class1, class2, ...)` | 向元素添加一个或多个类。此方法会向元素的类列表添加一个或多个类名。如果指定的类名已存在，则不会添加。 | `element.classList.add('myClass')`
`element.classList.remove(class1, class2, ...)` | 从元素中删除一个或多个类。此方法会从元素的类列表中删除一个或多个类名。 | `element.classList.remove('myClass')`
`element.classList.toggle(class, true/false)` | 切换元素的类。如果类名存在，则删除它；如果类名不存在，则添加它。 | `element.classList.toggle('myClass')`
`element.style.cssText` | 设置或返回元素的样式属性。此属性返回一个 CSSStyleDeclaration 对象，代表元素的 `style` 属性。 | `element.style.cssText = 'color: red; background: blue;'`
`element.style.property` | 设置或返回元素的指定样式属性。此属性会设置或返回元素的指定样式属性。 | `element.style.color = 'red'`
`element.innerHTML` | 设置或返回元素的 HTML 内容。此属性会设置或返回元素的内部 HTML。 | `element.innerHTML = '<b>Hello</b>'`
`element.outerHTML` | 设置或返回元素的外部 HTML。此属性会设置或返回元素的开头和结尾标签，包括元素的所有后代。 | `console.log(element.outerHTML)`
`element.textContent` | 设置或返回元素的文本内容。此属性会设置或返回元素的所有文本内容，包括它的所有后代。 | `element.textContent = 'Hello'`

请注意，每个属性和方法可能有其适用性和局限性。在使用它们的时候，需要考虑到你的需求和它们的特性。

    // 改变内容
    title.innerHTML = 'New title';

    // 改变样式
    title.style.color = 'red';

    // 显示/隐藏
    content.style.display = 'none';

    // 添加类
    content.classList.add('hidden');

    // 删除类
    content.classList.remove('hidden');

#### 创建元素

方法 | 描述 | 示例
--- | --- | ---
`document.createElement(tagName)` | 创建一个新元素。此方法创建一个具有指定标签名的新元素。 | `let div = document.createElement('div')`
`document.createTextNode(data)` | 创建一个新的文本节点。此方法创建一个包含指定文本的新 Text 节点。 | `let text = document.createTextNode('Hello')`
`Node.appendChild(child)` | 向元素添加一个新的子节点。此方法将指定的节点添加到指定节点的子节点列表的末尾。 | `div.appendChild(text)`
`Node.insertBefore(newNode, referenceNode)` | 在参考节点之前插入一个新节点。此方法将指定的节点插入到参考节点之前。如果参考节点为 `null`，则新节点将被添加到子节点的末尾。 | `document.body.insertBefore(div, document.body.firstChild)`
`Element.insertAdjacentHTML(position, text)` | 解析指定的文本作为 HTML 或 XML，并将生成的节点插入到 DOM 树的指定位置。此方法不会重新解析它正在使用的元素，因此它不会破坏元素内的现有元素。 | `div.insertAdjacentHTML('beforeend', '<span>Hello</span>')`
`Element.insertAdjacentText(position, text)` | 将指定的文本插入到 DOM 树的指定位置。此方法不会重新解析它正在使用的元素，因此它不会破坏元素内的现有元素。 | `div.insertAdjacentText('beforeend', 'Hello')`
`Element.insertAdjacentElement(position, element)` | 将指定的元素插入到 DOM 树的指定位置。此方法不会重新解析它正在使用的元素，因此它不会破坏元素内的现有元素。 | `let span = document.createElement('span'); div.insertAdjacentElement('beforeend', span)`
`Node.cloneNode(deep)` | 创建指定节点的副本。此方法创建一个新的节点，这个节点是调用此方法的节点的副本。如果 deep 参数为 true，则复制包括整个子 DOM 树，否则只复制节点本身。 | `let divClone = div.cloneNode(true)`
`Node.replaceChild(newChild, oldChild)` | 替换一个子节点。此方法将 oldChild 替换为 newChild。 | `div.replaceChild(newSpan, oldSpan)`

这些方法为您提供了创建和操纵 DOM 元素的强大工具。在实际开发中，您可能会经常使用它们来动态地更新页面内容。

    // 创建新元素
    const div = document.createElement('div');

    // 设置属性
    div.innerHTML = 'Hello World';
    div.style.color = 'blue';

    // 插入到文档中
    document.body.appendChild(div);

#### 事件处理

方法 | 描述 | 示例
--- | --- | ---
`element.addEventListener(event, function, useCapture)` | 为元素添加事件监听器。此方法在指定元素上添加一个事件处理程序。 | `element.addEventListener('click', myFunction)`
`element.removeEventListener(event, function, useCapture)` | 从元素中移除事件监听器。此方法从指定元素中移除事件处理程序。 | `element.removeEventListener('click', myFunction)`
`element.onclick` | 为元素设置 click 事件处理程序。此属性返回当前元素的 onclick 事件处理程序代码，或设置一个新的 onclick 事件处理程序代码。 | `element.onclick = myFunction`
`event.preventDefault()` | 取消事件的默认动作。此方法阻止事件的默认动作。例如，点击链接的默认动作是跳转到链接的 URL，当调用此方法后，点击链接不会跳转。 | `event.preventDefault()`
`event.stopPropagation()` | 阻止事件冒泡。此方法阻止事件向上冒泡到 DOM 树。 | `event.stopPropagation()`
`event.stopImmediatePropagation()` | 阻止剩余的事件处理程序被执行并阻止事件冒泡到 DOM 树。 | `event.stopImmediatePropagation()`
`event.target` | 返回触发事件的元素。此属性返回触发事件的元素。 | `let targetElement = event.target`
`event.type` | 返回事件的名称。此属性返回表示事件名称的字符串。 | `let eventType = event.type`
`event.clientX` and `event.clientY` | 返回鼠标事件发生时鼠标指针的位置。这两个属性返回鼠标指针相对于浏览器窗口的水平和垂直坐标。 | `let x = event.clientX; let y = event.clientY`

这些都是最常用的事件处理方法和属性。实际上，还有更多的事件类型和与事件相关的方法和属性，它们可以用来处理键盘事件、触摸事件、拖放事件等等。请查阅相关文档以获取更多信息。

    // 点击事件
    btn.addEventListener('click', () => {
      // 点击时运行的代码
    });

    // 加载事件
    window.addEventListener('load', () => {
      // 页面加载完成后运行的代码
    });

### 事件处理

JavaScript 可以对各种事件做出响应。

#### 鼠标事件

常见的鼠标事件:

事件类型 | 描述 | 示例
--- | --- | ---
`click` | 鼠标点击一个元素时触发。 | `element.addEventListener('click', myFunction)`
`dblclick` | 鼠标双击一个元素时触发。 | `element.addEventListener('dblclick', myFunction)`
`mousedown` | 鼠标按钮被按下时触发。 | `element.addEventListener('mousedown', myFunction)`
`mouseup` | 鼠标按钮被松开时触发。 | `element.addEventListener('mouseup', myFunction)`
`mousemove` | 鼠标被移动时触发。 | `element.addEventListener('mousemove', myFunction)`
`mouseover` | 鼠标指针移动到元素上时触发。 | `element.addEventListener('mouseover', myFunction)`
`mouseout` | 鼠标指针移动出元素时触发。 | `element.addEventListener('mouseout', myFunction)`
`mouseenter` | 鼠标指针移动到元素上时触发，不冒泡。 | `element.addEventListener('mouseenter', myFunction)`
`mouseleave` | 鼠标指针移动出元素时触发，不冒泡。 | `element.addEventListener('mouseleave', myFunction)`
`contextmenu` | 当尝试在元素上打开上下文菜单时（通常通过右键点击）触发。 | `element.addEventListener('contextmenu', myFunction)`
`mousewheel` / `wheel` | 当鼠标滚轮被滚动时触发。 | `element.addEventListener('wheel', myFunction)`

在这些事件处理程序中，你可以使用 `event` 对象来访问事件的相关信息，比如触发事件的元素 (`event.target`)，鼠标的位置 (`event.clientX` 和 `event.clientY`)，以及鼠标按钮的状态 (`event.button`) 等等。

例如:

    // 点击事件
    btn.addEventListener('click', () => {
      console.log('Button clicked');
    })

    // 移动事件
    window.addEventListener('mousemove', (event) => {
      console.log('鼠标坐标:' + event.clientX + ', ' + event.clientY);
    })

#### 键盘事件

常见的键盘事件:

事件类型 | 描述 | 示例
--- | --- | ---
`keydown` | 当用户按下键盘按键时触发。 | `window.addEventListener('keydown', myFunction)`
`keyup` | 当用户释放键盘按键时触发。 | `window.addEventListener('keyup', myFunction)`
`keypress` | 当用户按下并释放键盘按键时触发。 | `window.addEventListener('keypress', myFunction)`

在这些事件处理程序中，你可以使用 `event` 对象来访问事件的相关信息，例如：

属性 | 描述 | 示例
--- | --- | ---
`event.key` | 返回被按下的键的字符。 | `let key = event.key`
`event.code` | 返回被按下的物理键的代码。 | `let code = event.code`
`event.altKey` | 如果 Alt 键被按下时返回 `true`，否则返回 `false`。 | `let altKeyPressed = event.altKey`
`event.ctrlKey` | 如果 Ctrl 键被按下时返回 `true`，否则返回 `false`。 | `let ctrlKeyPressed = event.ctrlKey`
`event.shiftKey` | 如果 Shift 键被按下时返回 `true`，否则返回 `false`。 | `let shiftKeyPressed = event.shiftKey`
`event.metaKey` | 如果 Meta 键（在 Windows 键盘上通常是 Windows 键，在 Apple 键盘上通常是 Command 键）被按下时返回 `true`，否则返回 `false`。 | `let metaKeyPressed = event.metaKey`

请注意，键盘事件通常在 `window` 对象上监听，而不是特定的元素上，因为键盘输入通常是全局的。例如:

    // 按下任意键时打印
    document.addEventListener('keydown', (event) => {
      console.log('Pressed key: ' + event.key);
    });

#### 其他事件

还有一些其他常用的事件:

事件类型 | 描述 | 示例
--- | --- | ---
`load` | 当页面完全加载后（包括所有图像、脚本文件、CSS 文件等）触发。 | `window.addEventListener('load', myFunction)`
`unload` | 当页面即将被卸载（或关闭）时触发。 | `window.addEventListener('unload', myFunction)`
`beforeunload` | 当窗口即将卸载新的页面时触发。这个事件可以用来提示用户是否真的要离开当前页面（例如，如果有未保存的更改）。 | `window.addEventListener('beforeunload', myFunction)`
`resize` | 当浏览器窗口被调整大小时触发。 | `window.addEventListener('resize', myFunction)`
`scroll` | 当用户滚动指定的元素或页面时触发。 | `window.addEventListener('scroll', myFunction)`
`focus` | 当元素获得焦点时触发。 | `element.addEventListener('focus', myFunction)`
`blur` | 当元素失去焦点时触发。 | `element.addEventListener('blur', myFunction)`
`input` | 当 `<input>`、`<select>` 或 `<textarea>` 元素的值改变时触发。 | `element.addEventListener('input', myFunction)`
`change` | 当用户改变 `<input>`、`<select>` 或 `<textarea>` 元素的值并且失去焦点时触发。 | `element.addEventListener('change', myFunction)`
`submit` | 当表单提交时触发。 | `element.addEventListener('submit', myFunction)`
`touchstart` | 当用户触摸屏幕时触发。 | `element.addEventListener('touchstart', myFunction)`
`touchmove` | 当用户在屏幕上移动触摸点时触发。 | `element.addEventListener('touchmove', myFunction)`
`touchend` | 当用户停止触摸屏幕时触发。 | `element.addEventListener('touchend', myFunction)`

这只是可能发生的事件类型的一部分。实际上，HTML DOM API 定义了很多不同的事件类型，可以用来处理各种各样的用户交互和页面生命周期事件。

### ES6+ 新特性

ES6 开始 JavaScript 引入了许多新特性,极大地丰富了语言本身。

#### Let 和 Const

- let 声明的变量有块级作用域，这意味着 let 声明的变量只在其声明的块或子块中可用，而在外部不可访问。这与 var 不同，var 声明的变量是函数作用域或全局作用域。
- const 声明的是不可修改的常量，一旦声明，其值就不能改变。这并不意味着它指向的对象不可改变，例如，const 声明的对象，我们仍然可以修改其属性。

#### 箭头函数

箭头函数提供了一种更简洁的函数定义方式，它有几个特点：

- 更简洁的语法
- 不绑定自己的 this，它会捕获其所在的上下文的 this 值作为自己的 this 值
- 没有 arguments 对象，如果你需要类似的功能，可以使用 rest 参数代替
- 不能作为构造函数，也就是说，不能使用 new 命令

    // 传统函数
    function add(a, b) {
      return a + b;
    }

    // 箭头函数
    const add = (a, b) => a + b;

#### 模板字符串

模板字符串使用反引号 (“)，可以内嵌变量和表达式，也支持多行字符串。

    const name = 'John';
    console.log(`Hello ${name}!`) // Hello John!

#### 对象解构

对象解构允许我们从对象中提取属性作为变量，这使得从对象中获取数据变得非常方便。解构也可以用于数组，可以让我们更快速地获取数组中的元素。

    const person = {
      name: 'John',
      age: 20
    };

    // 解构
    const { name, age } = person;

#### 模块化

ES6 引入了模块化的概念，可以使用 import 和 export 实现模块的导入和导出。这个特性使得 JavaScript 代码更加模块化，便于组织和管理。

    // foo.js
    export const name = 'John';

    // main.js
    import { name } from './foo.js';

等等众多特性,极大地推动了 JavaScript 的发展。

### 异步编程

JavaScript 的异步编程主要基于 Promise 和 async/await。

#### Promise

Promise 是 JavaScript 中处理异步操作的一种方式，是一种代表未来将要发生事件的对象。Promise 有三种状态：pending（进行中）、fulfilled（已成功）和 rejected（已失败）。Promise 的状态一旦改变，就不会再变。

Promise 可以链式调用，这意味着我们可以在一个 Promise 完成后，返回一个新的 Promise，这使得我们可以以同步的方式编写异步代码。

    function fetchData() {
      return new Promise((resolve, reject) => {
        // 异步操作
        setTimeout(() => {
          const data = 'Hello World';
          resolve(data);
        }, 3000);
      });
    }

    fetchData().then(data => {
      // 成功时调用
      console.log(data);
    });

#### async/await

async/await 是基于 Promise 实现的，它是一种更人性化的处理异步的方式。它让异步代码看起来更像同步代码，大大提高了代码的可读性和可维护性。

    // 用 async 声明一个异步函数
    async function fetchData() {
      // 使用 await 获取 Promise 的结果
      const data = await new Promise((resolve) => {
        setTimeout(() => {
          resolve('Hello World');
        }, 3000);
      });

      return data;
    }

    // 调用异步函数
    fetchData().then(data => {
      console.log(data);
    });

使用 async 关键字声明的函数称为异步函数，它会隐式返回一个 Promise。await 关键字可以用于等待一个 Promise 完成并获取其结果。注意，await 关键字只能在 async 函数内部使用。

async/await 与 Promise 一样，有很好的错误处理机制。我们可以使用 try…catch 语句来捕获和处理 Promise 中发生的错误。