## JavaScript 事件

**事件**是用户或浏览器自身执行的某种动作，比如点击、鼠标移动、键盘输入、页面加载等。JavaScript 可以监听这些事件并执行相应的代码。

事件的常见类型：鼠标事件、键盘事件、表单事件、窗口/文档事件



绑定事件的方式

#### HTML 事件

HTML 事件可以是浏览器行为，也可以是用户行为。 

以下是 HTML 事件的实例：

 

HTML 页面完成加载

HTML input 字段改变时

HTML 按钮被点击

通常，当事件发生时，你可以做些事情。

 

在事件触发时 JavaScript 可以执行一些代码。

 

HTML 元素中可以添加事件属性，使用 JavaScript 代码来添加 HTML 元素。

 

单引号/双引号:


在以下实例中，按钮元素中添加了 onclick 属性 (并加上代码):

JavaScript代码通常是几行代码。比较常见的是通过事件属性来调用：
```js
<!-- 直接在 HTML 属性中写 JavaScript 代码 -->
<button onclick="alert('点击了')">点击我</button>

<!-- 调用 JavaScript 函数 -->

<button onclick="displayDate()">现在的时间是?</button>
<script>
function displayDate(){
	document.getElementById("demo").innerHTML=Date();
}
</script>
```





注意，当在 JS 文件中为相关元素设置事件时，其写法与 HTML 事件属性写法相同，例如：

```
<button id="test" onclick="changeContent()">更换内容</button>
```

在 JS 中则需要这样写：

```
var test = document.getElementById("test");
test.onclick = function changeContent(){
    //......
}
```

注意：在为元素添加事件句柄或者删除元素事件句柄的过程中，不要将 event 参数设置为 onclick，而必须写成 click，去掉事件名称中的 on 即可。

添加事件句柄函数原型:

```
element.addEventListener(event, function, [useCapture])
```

删除事件句柄的函数原型:

```
element.removeEventListener(event, function, [useCapture])
```

 

不推荐使用 HTML 元素中可以添加事件属性 的方式来添加属性。

例子：

```
<button onclick="getElementById('demo').innerHTML=Date()">现在的时间是?</button>
```

因为遵从**“高内聚，低耦合”**的编程原则。

高内聚是说模块内部要高度聚合，低耦合是说模块与模块之间的藕合度要尽量低。前者是说模块内部的关系，后者是说模块与模块间的关系。



常见的HTML事件

下面是一些常见的HTML事件的列表:

 

| 事件        | 描述                                 |
| :---------- | :----------------------------------- |
| onchange    | HTML 元素改变                        |
| onclick     | 用户点击 HTML 元素                   |
| onmouseover | 鼠标指针移动到指定的元素上时发生     |
| onmouseout  | 用户从一个 HTML 元素上移开鼠标时发生 |
| onkeydown   | 用户按下键盘按键                     |
| onload      | 浏览器已完成页面的加载               |

更多事件列表: [JavaScript 参考手册 - HTML DOM 事件](https://www.runoob.com/jsref/dom-obj-event.html)。





#### 方式2：DOM 属性赋值



```html
<button id="myBtn">点击我</button>

<script>
document.getElementById('myBtn').onclick = function() {
    alert('点击了');
};
</script>
```



#### 方式3：事件监听器（推荐）

```html
<button id="myBtn">点击我</button>

<script>
const btn = document.getElementById('myBtn');

// 添加事件监听
btn.addEventListener('click', function() {
    alert('点击了');
});

// 可以添加多个监听器
btn.addEventListener('click', function() {
    console.log('第二个处理函数');
});

// 移除事件监听
// btn.removeEventListener('click', handler);
</script>
```

## 事件对象

## 事件流：捕获与冒泡





## JavaScript 字符串

字符串可以存储一系列字符，如 "John Doe"。

 

字符串可以是插入到引号中的任何字符。你可以使用单引号或双引号：

 

实例

var carname = "Volvo XC60";

var carname = 'Volvo XC60';

你可以使用索引位置来访问字符串中的每个字符：

 

实例

var character = carname[7];

字符串的索引从 0 开始，这意味着第一个字符索引值为 [0]，第二个为 [1]，以此类推。

 

实例

const name = "RUNOOB";

let letter = name[2];

 

document.getElementById("demo").innerHTML = letter;

 

尝试一下 »

你可以在字符串中使用引号，字符串中的引号不要与字符串的引号相同:

 

实例

var answer = "It's alright";

var answer = "He is called 'Johnny'";

var answer = 'He is called "Johnny"';

你也可以在字符串添加转义字符来使用引号：

 在 JavaScript 中，字符串写在单引号或双引号中。

因为这样，以下实例 JavaScript 无法解析：

如何解决以上的问题呢？可以使用反斜杠 (\) 来转义 "Vikings" 字符串中的双引号，如下:

` "We are the so-called \"Vikings\" from the north."`

 反斜杠是一个**转义字符**。 转义字符将特殊字符转换为字符串字符：

转义字符 (\) 可以用于转义撇号，换行，引号，等其他特殊字符。

实例

```js
var x = 'It\'s alright';

var y = "He is called \"Johnny\"";
```



 字符串长度

可以使用内置属性 **length** 来计算字符串的长度：

var txt = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";

var sln = txt.length;

字符串可以是对象

通常， JavaScript 字符串是原始值，可以使用字符创建： **var firstName = "John"**

但我们也可以使用 new 关键字将字符串定义为一个对象： **var firstName = new String("John")**



```js
var x = "John";
var y = new String("John");
typeof x //  返回 String
typeof y // 返回 Object
```





字符串属性和方法

原始值字符串，如 "John", 没有属性和方法(因为他们不是对象)。

原始值可以使用 JavaScript 的属性和方法，因为 JavaScript 在执行方法和属性时可以把原始值当作对象。

**字符串方法我们将在下一章节中介绍。**

------

字符串属性

| 属性        | 描述                       |
| :---------- | :------------------------- |
| constructor | 返回创建字符串属性的函数   |
| length      | 返回字符串的长度           |
| prototype   | 允许您向对象添加属性和方法 |

------

字符串方法

更多方法实例可以参见：[JavaScript String 对象](https://www.runoob.com/jsref/jsref-obj-string.html)。





不要创建 String 对象。它会拖慢执行速度，并可能产生其他副作用：



 **JavaScript 模板字符串**

JavaScript 中的模板字符串是一种方便的字符串语法，允许你在字符串中嵌入表达式和变量。

模板字符串使用反引号 **``** 作为字符串的定界符分隔的字面量。

模板字面量是用反引号（**`**）分隔的字面量，允许多行字符串、带嵌入表达式的字符串插值和一种叫带标签的模板的特殊结构。



### 语法

```
`string text`

`string text line 1
 string text line 2`

`string text ${expression} string text`

tagFunction`string text ${expression} string text`
```

**参数**

- **string text**：将成为模板字面量的一部分的字符串文本。几乎允许所有字符，包括换行符和其他空白字符。但是，除非使用了标签函数，否则无效的转义序列将导致语法错误。
- **expression**：要插入当前位置的表达式，其值被转换为字符串或传递给 tagFunction。
- **tagFunction**：如果指定，将使用模板字符串数组和替换表达式调用它，返回值将成为模板字面量的值。





模板字符串中可以同时使用单引号和双引号：

```js
let text = `He's often called "Runoob"`;
```



 模板字符串还支持多行文本，而无需使用特殊的转义字符：

```js
const multiLineText = `

 This is

 a multi-line

 text.

`;
```





尝试一下 »

若要转义模板字面量中的反引号（`），需在反引号之前加一个反斜杠（\）。

 ```js
 `\`` === "`"; // true
 ```

模板字面量用反引号（`）括起来，而不是双引号（"）或单引号（'）。


除了普通字符串外，模板字面量还可以包含占位符——一种由美元符号和大括号分隔的嵌入式表达式：${expression}。


字符串和占位符被传递给一个函数（要么是默认函数，要么是自定义函数）。默认函数（当未提供自定义函数时）只执行字符串插值来替换占位符，然后将这些部分拼接到一个字符串中。

 

模板字符串中允许我们使用变量：

```js
const name = 'Runoob';

const age = 30;

const message = `My name is ${name} and I'm ${age} years old.`;
```


尝试一下 »

以上实例中，`${name} 和 ${age} `是模板字符串的表达式部分，它们被包含在 ${} 内部，并在运行时求值。

 

模板字符串允许你在字符串中引用变量、执行函数调用和进行任意的JavaScript表达式。

 

模板字符串中允许我们使用表达式：

 

```js
let price = 10;

let VAT = 0.25;

let total = `Total: ${(price * (1 + VAT)).toFixed(2)}`;
```

模板字符串当作 HTML 模板使用：

 

实例

```js
let header = "";

let tags = ["RUNOOB", "GOOGLE", "TAOBAO"];

let html = `<h2>${header}</h2><ul>`;

for (const x of tags) {

 html += `<li>${x}</li>`;

}

html += `</ul>`;
```



```
// 定义标签函数
function tagFunction(strings, ...values) {
    // strings是模板字符串中不包含变量部分的数组
    // values是插值中的所有变量
    return strings.reduce((accumulator, str, i) => {
        // 对插值变量进行处理
        const value = values[i];

        // 处理插值变量
        const processedValue = (typeof value === 'string')
            ? `[${value.toUpperCase()}]`
            : (typeof value === 'number')
                ? `[${value * 2}]`
                : '';

        return accumulator + str + processedValue;
    }, '');
}

// 使用标签函数
const xm = 'Fitten';
const ll = 5; 

const result = tagFunction`Hello, my name is ${xm} and I am ${ll} years old.`;
console.log(result); // 输出: Hello, my name is [FITTEN] and I am [10] years old.
```

在这个例子中，`tagFunction`被用来处理模板字符串并将变量部分中字符串变量转换为大写，将数字乘以2，同时加上方括号标记。









# JavaScript 运算符

运算符 = 用于赋值。

运算符 + 用于加值。

运算符 = 用于给 JavaScript 变量赋值。



\+ 运算符用于把文本值或字符串变量加起来（连接起来）。

如需把两个或多个字符串变量连接起来，请使用 **+** 运算符。

两个数字相加，返回数字相加的和，如果数字与字符串相加，返回字符串

取模运算的结果符号只与左边值的符号有关：

```
var x = 7 % 3; // 结果为 1
var y = 7 % (-3); // 结果为 1
var z = (-7) % 3; // 结果为 -1
```

-  如果 % 左边的操作数是正数，则模除的结果为正数或零；
-  如果 % 左边的操作数是负数，则模除的结果为负数或零。



# JS语句

**条件语句**

通常在写代码时，您总是需要为不同的决定来执行不同的动作。您可以在代码中使用条件语句来完成该任务。

在 JavaScript 中，我们可使用以下条件语句：

- **if 语句** - 只有当指定条件为 true 时，使用该语句来执行代码
- **if...else 语句** - 当条件为 true 时执行代码，当条件为 false 时执行其他代码
- **if...else if....else 语句**- 使用该语句来选择多个代码块之一来执行
- **switch 语句** - 使用该语句来选择多个代码块之一来执行

**JavaScript switch 语句**

请使用 switch 语句来选择要执行的多个代码块之一。

**语法**

```js
switch(n)
{
    case 1:
        执行代码块 1
        break;
    case 2:
        执行代码块 2
        break;
    default:
        与 case 1 和 case 2 不同时执行的代码
}
```



工作原理：首先设置表达式 *n*（通常是一个变量）。随后表达式的值会与结构中的每个 case 的值做比较。如果存在匹配，则与该 case 关联的代码块会被执行。请使用 **break** 来阻止代码自动地向下一个 case 运行。

请使用 default 关键词来规定匹配不存在时做的事情：

#### 不同类型的循环

JavaScript 支持不同类型的循环：

for - 循环代码块一定的次数

for/in - 循环遍历对象的属性

while - 当指定的条件为 true 时循环指定的代码块

do/while - 同样当指定的条件为 true 时循环指定的代码块

For 循环

for 循环是您在希望创建循环时常会用到的工具。

 

下面是 for 循环的语法：

for (*语句 1*; *语句 2*; *语句 3*)
{
  *被执行的代码块*
}

**语句 1** （代码块）开始前执行

**语句 2** 定义运行循环（代码块）的条件

**语句 3** 在循环（代码块）已被执行之后执行



```js
for (var i=0; i<5; i++) {      x=x + "该数字为 " + i + "<br>"; }
```

通常我们会使用语句 1 初始化循环中所用的变量 (var i=0)。

语句 1 是可选的，也就是说不使用语句 1 也可以。

您可以在语句 1 中初始化任意（或者多个）值：

for (var i=0,len=cars.length; i<len; i++) {     document.write(cars[i] + "<br>"); }

同时您还可以省略语句 1（比如在循环开始前已经设置了值时）：

var i=2,len=cars.length;

通常语句 2 用于评估初始变量的条件。

语句 2 同样是可选的。

如果语句 2 返回 true，则循环再次开始，如果返回 false，则循环将结束。如果您省略了语句 2，那么必须在循环内提供 *break*。否则循环就无法停下来。这样有可能令浏览器崩溃



通常语句 3 会增加初始变量的值。

语句 3 也是可选的。

语句 3 有多种用法。增量可以是负数 (i--)，或者更大 (i=i+15)。

语句 3 也可以省略（比如当循环内部有相应的代码时）：

```js


var i=0,len=cars.length; for (; i<len; ) {     document.write(cars[i] + "<br>");    i++; }
```

For/In 循环
JavaScript for/in 语句循环遍历对象的属性


```js


实例
var person={fname:"Bill",lname:"Gates",age:56}; 
 
for (x in person)  // x 为属性名
{
    txt=txt + person[x];
}
```

**for 循环除了使用 in 方式来循环数组，还提供了一个方式： of , 遍历数组时更加方便。**

**for...of** 是 ES6 新引入的特性。它既比传统的for循环简洁，同时弥补了forEach和for-in循环的短板。

**for-of的语法：**

```js
for (var value of myArray) {
  console.log(value);
}
```





循环一个 DOM collection

循环一个DOM collections，比如NodeList，之前我们讨论过如何循环一个NodeList，现在方便了，可以直接使用for-of循环：

```
// Note: This will only work in platforms that have
// implemented NodeList.prototype[Symbol.iterator]
let articleParagraphs = document.querySelectorAll("article > p");

for (let paragraph of articleParagraphs) {
  paragraph.classList.add("read");
}
```

循环一个拥有enumerable属性的对象

for–of循环并不能直接使用在普通的对象上，但如果我们按对象所拥有的属性进行循环，可使用内置的Object.keys()方法：

```
for (var key of Object.keys(someObject)) {
  console.log(key + ": " + someObject[key]);
}
```

循环一个生成器(generators)

我们可循环一个生成器(generators):

```
function* fibonacci() { // a generator function
  let [prev, curr] = [0, 1];
  while (true) {
    [prev, curr] = [curr, prev + curr];
    yield curr;
  }
}

for (let n of fibonacci()) {
  console.log(n);
  // truncate the sequence at 1000
  if (n >= 1000) {
    break;
  }
}
```





JavaScript while 循环

------

只要指定条件为 true，循环就可以一直执行代码块。

------

while 循环

while 循环会在指定条件为真时循环执行代码块。

## do/while 循环

do/while 循环是 while 循环的变体。该循环会在检查条件是否为真之前执行一次代码块，然后如果条件为真的话，就会重复这个循环。

```js


do
{
  *需要执行的代码
*}
while (*条件*);
```



for...in 循环会自动跳过那些没被赋值的元素，而 for 循环则不会，它会显示出 undefined。

```js
    var array = new Array();
    var x;
    var txt=""
    array[0] = 1;
    array[3] = 2;
    array[4] = 3;
    array[10] = 4;
    for( x in array ){
        alert(array[x]);     // 依次显示出 1 2 3 4
    } 
    alert(array.length);    // 结果是11
    for( var i=0 ; i<4 ; i++){
        alert(array[i]);     // 依次显示出 1 undefined undefined 2 
    }
```



break 语句

我们已经在本教程之前的章节中见到过 break 语句。它用于跳出 switch() 语句。

break 语句可用于跳出循环。

break 语句跳出循环后，会继续执行该循环之后的代码（如果有的话）：

**continue 语句**中断当前的循环中的迭代，然后继续循环下一个迭代。 以下例子在值为 3 时，直接跳过：



#### **JavaScript 标签**

正如您在 switch 语句那一章中看到的，可以对 JavaScript 语句进行标记。

如需标记 JavaScript 语句，请在语句之前加上冒号：

label: statements

break 和 continue 语句仅仅是能够跳出代码块的语句。

语法:

break labelname; continue labelname;

continue 语句（带有或不带标签引用）只能用在循环中。

break 语句（不带标签引用），只能用在循环或 switch 中。



break 的作用是跳出代码块, 所以 break 可以使用于循环和 switch 等

continue 的作用是进入下一个迭代, 所以 continue 只能用于循环的代码块。



代码块: 基本上是｛｝大括号之间



默认标签的情况（除了默认标签情况，其他时候必须要有名标签)

当 break 和 continue 同时用于循环时，没有加标签，此时默认标签为当前"循环"的代码块。

当 break 用于 switch 时，默认标签为当前的 switch 代码块:

意思是，不写标签时，它们只会影响直接包含它们的循环或 switch，不会影响外层代码块。



有名标签的情况





```js
cars=["BMW","Volvo","Saab","Ford"];
list:
{
    document.write(cars[0] + "");
    document.write(cars[1] + "");
    document.write(cars[2] + "");
    break list;
    document.write(cars[3] + "");
    document.write(cars[4] + "");
    document.write(cars[5] + "");
}
上述break list;会跳出list的代码块。如果将break换成continue会有惊喜，违反了明确中的第二点，因为list只是个普通代码块，而不是循环。除非list写成如下形式 list:

list:
for (var i = 0; i < 10; ++i) {
    continue list;  // 跳转到下一次外层循环（这里就是 list 标记的这个循环）
}
```

有了标签，可以使用break和continue在多层循环的时候控制外层循环。

例如下面：

```js
outerloop:
for (var i = 0; i < 10; i++)
{
    innerloop:
    for (var j = 0; j < 10; j++)
    {
        if (j > 3)
        {
            break;
        }
        if (i == 2)
        {
            break innerloop;
        }
        if (i == 4)
        {
            break outerloop;
        }
        document.write("i=" + i + " j=" + j + "");
    }
}
```



## JS类型转换



Number() 转换为数字， String() 转换为字符串， Boolean() 转换为布尔值。



**JavaScript 数据类型**

基本类型（Primitive Types）值类型- 7 种

1. **`string`** - 字符串
2. **`number`** - 数字
3. **`boolean`** - 布尔值
4. **`null`** - 空值
5. **`undefined`** - 未定义
6. **`symbol`** - 符号（ES6 新增）
7. **`bigint`** - 大整数（ES2020 新增）

引用类型（Object Types）对象类型- 1 种基础 + 若干衍生

- **`object`** - 这是唯一的引用类型
  - 普通对象 `{}`
  - 数组 `[]`（本质是对象）
  - 函数 `function`（本质是对象，但 `typeof` 返回 `"function"`）
  - 日期 `Date`
  - 正则 `RegExp`
  - 等等...





请注意：

- NaN 的数据类型是 number
- 数组(Array)的数据类型是 object
- 日期(Date)的数据类型为 object
- null 的数据类型是 object
- 未定义变量的数据类型为 undefined

如果对象是 JavaScript Array 或 JavaScript Date ，我们就无法通过 **typeof** 来判断他们的类型，因为都是 返回 object。



constructor 属性

**constructor** 属性返回所有 JavaScript 变量的构造函数。

```js
"John".constructor                 
// 返回函数 String()  { [native code] }
(3.14).constructor                 
// 返回函数 Number()  { [native code] }
false.constructor                 
// 返回函数 Boolean() { [native code] }
[1,2,3,4].constructor             
// 返回函数 Array()   { [native code] }
{name:'John', age:34}.constructor 
// 返回函数 Object()  { [native code] }
	new Date().constructor            
// 返回函数 Date()    { [native code] }
function () {}.constructor        
// 返回函数 Function(){ [native code] }
```





你可以使用 constructor 属性来查看对象是否为数组 (包含字符串 "Array"):



```js
function isArray(myArray) {
    return myArray.constructor.toString().indexOf("Array") > -1;
}
```





JavaScript 变量可以转换为新变量或其他数据类型：

- 通过使用 JavaScript 函数
- 通过 JavaScript 自身自动转换

**Operator +** 可用于将变量转换为数字：

当 JavaScript 尝试操作一个 "错误" 的数据类型时，会自动转换为 "正确" 的数据类型。

以下输出结果不是你所期望的：

当你尝试输出一个对象或一个变量时 JavaScript 会自动调用变量的 toString() 方法：



NaN 是一个特殊的数值，NaN 即非数值（Not a Number），这个数值用于本来要返回数值的操作数未返回数值的情况。

NaN 与任何值都不相等，包括 NaN 本身。

可以通过 isNaN() 方法来判断某个数值是否是NaN这个特殊的数，使用 isNaN() 方法会将传入的数值如果是非数值的会将其自动转换成数值类型，若能转换成数值类型，那么这个函数返回 false，若不能转换成数值类型，则这个数就是 NaN，即返回 true。

补充一下，通常来讲，使用 instanceof 就是判断一个实例是否属于某种类型，更重要的是 instanceof 可以在继承关系中用来判断一个实例是否属于它的父类型。

用 instanceof 来判断类型只能用于对象层面，不是一个对象则不行：

```js
str = "asd";
if(str instanceof String)//返回false
   document.write("YES");//不会打印YES
str = new String();
if(str instanceof String)//返回true
  document.write("yes");//不会打印yes
```

### JS正则表达式

语法

/正则表达式主体/修饰符(可选)

其中修饰符是可选的。

 

`var patt = /runoob/i`

实例解析：

**/runoob/i** 是一个正则表达式。

**runoob** 是一个**正则表达式主体** (用于检索)。

**i** 是一个**修饰符** (搜索不区分大小写)。

 



使用字符串方法

在 JavaScript 中，正则表达式通常用于两个字符串方法 : search() 和 replace()。

 

search() 方法用于检索字符串中指定的子字符串，或检索与正则表达式相匹配的子字符串，并返回子串的起始位置。

 

replace() 方法用于在字符串中用一些字符串替换(另一些字符串，或替换一个与正则表达式匹配的子串。)

 

search() 方法使用正则表达式

实例

使用正则表达式搜索 "Runoob" 字符串，且不区分大小写：

 ```js
 var str = "Visit Runoob!"; 
 var n = str.search(/Runoob/i);
 输出结果为：
 6
 ```

 

search() 方法使用字符串

search 方法可使用字符串作为参数。字符串参数会转换为正则表达式：

检索字符串中 "Runoob" 的子串：

 ```js
 var str = "Visit Runoob!"; 
 var n = str.search("Runoob");
 输出结果为：
 6
 ```







replace() 方法使用正则表达式

实例

使用正则表达式且不区分大小写将字符串中的 Microsoft 替换为 Runoob :

```js
var str = document.getElementById("demo").innerHTML; 
var txt = str.replace(/microsoft/i,"Runoob");
结果输出为:
Visit Runoob!
```





replace() 方法使用字符串

replace() 方法将接收字符串作为参数：

 

```js
var str = document.getElementById("demo").innerHTML; 

var txt = str.replace("Microsoft","Runoob");
```





## 正则表达式修饰符

**修饰符** 可以在全局搜索中不区分大小写:

| 修饰符 | 描述                                                     |
| :----- | :------------------------------------------------------- |
| i      | 执行对大小写不敏感的匹配。                               |
| g      | 执行全局匹配（查找所有匹配而非在找到第一个匹配后停止）。 |
| m      | 执行多行匹配。                                           |

正则表达式模式

方括号用于查找某个范围内的字符：

| 表达式 | 描述                       |
| :----- | :------------------------- |
| [abc]  | 查找方括号之间的任何字符。 |
| [0-9]  | 查找任何从 0 至 9 的数字。 |
| (x\|y) | 查找任何以 \| 分隔的选项。 |

元字符是拥有特殊含义的字符：

| 元字符 | 描述                                        |
| :----- | :------------------------------------------ |
| \d     | 查找数字。                                  |
| \s     | 查找空白字符。                              |
| \b     | 匹配单词边界。                              |
| \uxxxx | 查找以十六进制数 xxxx 规定的 Unicode 字符。 |

量词:

| 量词 | 描述                                  |
| :--- | :------------------------------------ |
| n+   | 匹配任何包含至少一个 *n* 的字符串。   |
| n*   | 匹配任何包含零个或多个 *n* 的字符串。 |
| n?   | 匹配任何包含零个或一个 *n* 的字符串。 |



使用 RegExp 对象

在 JavaScript 中，RegExp 对象是一个预定义了属性和方法的正则表达式对象。

使用 test()

test() 方法是一个正则表达式方法。

test() 方法用于检测一个字符串是否匹配某个模式，如果字符串中含有匹配的文本，则返回 true，否则返回 false。

以下实例用于搜索字符串中的字符 "e"：

```js
var patt = /e/;
patt.test("The best things in life are free!");
字符串中含有 "e"，所以该实例输出为：
true

你可以不用设置正则表达式的变量，以上两行代码可以合并为一行：
/e/.test("The best things in life are free!")
```





 

使用 exec()

exec() 方法是一个正则表达式方法。

 

exec() 方法用于检索字符串中的正则表达式的匹配。

 

该函数返回一个数组，其中存放匹配的结果。如果未找到匹配，则返回值为 null。

 

以下实例用于搜索字符串中的字母 "e":

 ```js
 /e/.exec("The best things in life are free!");
 
 字符串中含有 "e"，所以该实例输出为:
 
 e
 ```





1. ```
   /**
    * [reg 百度网盘链接匹配]
    * 说明：匹配支持百度分享的两种链接格式
    * 格式一：链接: https://pan.baidu.com/s/15gzY8h3SEzVCfGV1xfkJsQ 提取码: vsuw 复制这段内容后打开百度网盘手机App，操作更方便哦
    * 格式二：http://pan.baidu.com/share/link?shareid=179436&uk=3272055266 提取码: vsuw 复制这段内容后打开百度网盘手机App，操作更方便哦
    * 匹配出下载地址和提取码，并且还支持如果没有提取码，也能匹配出下载链接。
    * @type {正则表达式}
    * @return array 返回匹配成功的链接和地址
    */
   
   function baiduDownLinkArr( string ){
     var reg = /([http|https]*?:\/\/pan\.baidu\.com\/[(?:s\/){0,1}|(share)]*(?:[0-9a-zA-Z?=&])+)(?:.+:(?:\s)*)?([a-zA-Z]{4})?/;
     console.log(reg.exec(string));
   }
   ```

   [千年骚狐](javascript:;)

     千年骚狐

    317***773@qq.com

   6年前 (2019-11-11)

2. 

     orts

    262***2099@qq.com

    [ 参考地址](https://mp.weixin.qq.com/s/viTlTHt3bviZDL0dE16vZw)

   33

   正则捕获，转换时间格式：

   ```
   const str = '20210426141823';
   const s = str.replace(/(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/, (_, ...args) => {
     const [year, month, day, h, d, s] = args;
     return `${year}年${month}月${day} ${h}时${d}分${s}秒`
   });
   
   console.log(s);
   // 2021年04月26 14时18分23秒
   ```

   [orts](javascript:;)

     orts

    262***2099@qq.com

    [ 参考地址](https://mp.weixin.qq.com/s/viTlTHt3bviZDL0dE16vZw)

   4年前 (2022-01-25)

3. 

     jishux2

    jis***2@outlook.com

   8

   **match()、replace()、search()、split()** 方法中的正则表达式参数也可以用字符串代替，这时候，字符串会被隐式转换为正则表达式。例如：

   ```
   var str = "abcdeabc";
   console.log(str.match("abc")); // 输出["abc"]
   console.log(str.replace("abc", "xyz")); // 输出"xyzdeabc"
   console.log(str.search("abc")); // 输出0
   console.log(str.split("abc")); // 输出["", "de", ""]
   ```

   这里，字符串 **"abc"** 被当作一个字面量的正则表达式来处理，它匹配了字符串中的 **"abc"** 部分。

   **但注意，不同方法在转换字符串参数为正则表达式时，有不同的规则：**

   **split()** 方法和 **replace()** 方法会将字符串参数中的所有元字符（如*、+、? 等）都视为普通字符，并在前面加上反斜杠进行转义，以便匹配字符串中的实际字符。

   **match()** 方法和 **search()** 方法则会将字符串参数中的所有元字符都视为元字符自身，并保持其原始含义，以便匹配字符串中的特定模式。

   例如：

   ```
   var str = "a*b*c";
   console.log(str.split("*")); // 相当于str.split(/\*/)，表示匹配一个星号字符。输出['a', 'b', 'c']
   console.log(str.match("*")); // 相当于str.match(/*/)，/*/是一个无效的正则表达式。报错
   console.log(str.search("*")); // 相当于str.search(/*/)。报错
   console.log(str.replace("*", "+")); // 相当于str.replace(/\*/, "+")。输出a+b*c
   ```

   所以，在调用 **match()** 方法和 **search()** 方法时，如果你想在正则表达式参数中表示一个特殊字符，那么需要保证，在字符串转换的正则表达式中，对应的字符前有一个反斜杠进行转义，也就意味着在传入的字符串中，需要使用两个反斜杠进行转义。而调用 **split()** 方法和 **replace()** 方法时，由于其特殊的转换规则，字符串是无法表示出正则表达式中的元字符的，字符串中的元字符前加一个反斜杠或是不加，在正则表达式中都是视为转义字符

   **注：****split("\\")、replace("\\")、match("\\\\")、search("\\\\")** 表示匹配一个反斜杠，相当于 xxx(/\\/)。而 **split("\")、match("\")、match("\\")、match("\\\")** 等都是非法的。例如：

   ```
   var str = "a\\b\\c";
   console.log(str.split("\\")); // 输出["a", "b", "c"]
   console.log(str.replace("\\","+")); // 输出a+b\c
   console.log(str.match("\\\\")); // 输出['\\', index: 1, input: 'a\\b\\c', groups: undefined]
   console.log(str.search("\\\\")); // 输出1
   ```

   另外要注意，字符串中不能指定正则表达式的标志，也不能使用或者引用分组。如果有这方面的需要，就必须使用 RegExp 对象。



 **JavaScript 错误 - throw、try 和 catch**



**try** 语句测试代码块的错误。

**catch** 语句处理错误。

**throw** 语句创建自定义错误。

**finally** 语句在 try 和 catch 语句之后，无论是否有触发异常，该语句都会执行。



 **JavaScript 错误**

当 JavaScript 引擎执行 JavaScript 代码时，会发生各种错误。

可能是语法错误，通常是程序员造成的编码错误或错别字。

可能是拼写错误或语言中缺少的功能（可能由于浏览器差异）。

可能是由于来自服务器或用户的错误输出而导致的错误。

当然，也可能是由于许多其他不可预知的因素。



**JavaScript 抛出（throw）错误**

当错误发生时，当事情出问题时，JavaScript 引擎通常会停止，并生成一个错误消息。

描述这种情况的技术术语是：JavaScript 将**抛出**一个错误。



**JavaScript try 和 catch**

**try** 语句允许我们定义在执行时进行错误测试的代码块。

**catch** 语句允许我们定义当 try 代码块发生错误时，所执行的代码块。

JavaScript 语句 **try** 和 **catch** 是成对出现的。



```js
try {
    ...    //异常的抛出
} catch(e) {
    ...    //异常的捕获与处理
} finally {
    ...    //结束处理
}
```





**finally 语句**

finally 语句不论之前的 try 和 catch 中是否产生异常都会执行该代码块。

 

**Throw 语句**

throw 语句允许我们创建自定义错误。

正确的技术术语是：创建或**抛出异常**（exception）。

如果把 throw 与 try 和 catch 一起使用，那么您能够控制程序流，并生成自定义的错误消息。

```js
function myFunction() {
  var message, x;
  message = document.getElementById("p01");
  message.innerHTML = "";
  x = document.getElementById("demo").value;
  try { 
    if(x == "") throw "值是空的";
    if(isNaN(x)) throw "值不是一个数字";
    x = Number(x);
    if(x > 10) throw "太大";
    if(x < 5) throw "太小";
  }
  catch(err) {
    message.innerHTML = "错误: " + err + ".";
  }
  finally {
    document.getElementById("demo").value = "";
  }
}
```



**语法**

throw *exception*



异常可以是 JavaScript 字符串、数字、逻辑值或对象。

## JS调试

在程序代码中寻找错误叫做代码调试。

调试很难，但幸运的是，很多浏览器都内置了调试工具。

内置的调试工具可以开启或关闭，严重的错误信息会发送给用户。

有了调试工具，我们就可以设置断点 (代码停止执行的位置), 且可以在代码执行时检测变量。

浏览器启用调试工具一般是按下 F12 键，并在调试菜单中选择 "Console" 。

------

console.log() 方法

如果浏览器支持调试，你可以使用 console.log() 方法在调试窗口上打印 JavaScript 值：

设置断点

在调试窗口中，你可以设置 JavaScript 代码的断点。

在每个断点上，都会停止执行 JavaScript 代码，以便于我们检查 JavaScript 变量的值。

在检查完毕后，可以重新执行代码（如播放按钮）。



debugger 关键字

**debugger** 关键字用于停止执行 JavaScript，并调用调试函数。

这个关键字与在调试工具中设置断点的效果是一样的。

如果没有调试可用，debugger 语句将无法工作。

开启 debugger ，代码在第三行前停止执行。



## JavaScript 表单

JavaScript 表单验证

HTML 表单验证可以通过 JavaScript 来完成。

 

以下实例代码用于判断表单字段(fname)值是否存在， 如果不存在，就弹出信息，阻止表单提交：

```js
function validateForm() {
    var x = document.forms["myForm"]["fname"].value;
    if (x == null || x == "") {
        alert("需要输入名字。");
        return false;
    }
}
<form name="myForm" action="demo_form.php" onsubmit="return validateForm()" method="post">
名字: <input type="text" name="fname">
<input type="submit" value="提交">
</form>
```



 **关键点是 `onsubmit="return validateForm()"`**：这行代码给表单的提交事件绑定了一个JavaScript函数。当用户点击提交按钮时，浏览器会先执行 `validateForm()` 函数，并根据其返回值决定是否继续提交。

1. **页面刚打开时**：文本框是空的，此时 `value` 相当于 `""`（空字符串）。
2. **用户输入“张三”后**：浏览器的内存里，这个输入框的 `value` 就变成了 `"张三"`。







JavaScript 验证输入的数字

JavaScript 常用于对输入数字的验证：

 

请输入 1 到 10 之间的数字：

 

 提交

 

尝试一下 »

HTML 表单自动验证

HTML 表单验证也可以通过浏览器来自动完成。

 

如果表单字段 (fname) 的值为空, required 属性会阻止表单提交：

 

实例

<form action="demo_form.php" method="post">

 <input type="text" name="fname" required="required">

 <input type="submit" value="提交">

</form>

 

尝试一下 »

Internet Explorer 9 及更早 IE 浏览器不支持表单自动验证。

 

数据验证

数据验证用于确保用户输入的数据是有效的。

 

典型的数据验证有：

 

必需字段是否有输入?

用户是否输入了合法的数据?

在数字字段是否输入了文本?

大多数情况下，数据验证用于确保用户正确输入数据。

 

数据验证可以使用不同方法来定义，并通过多种方式来调用。

 

服务端数据验证是在数据提交到服务器上后再验证。

 

客户端数据验证是在数据发送到服务器前，在浏览器上完成验证。

1. 使用 HTML5 内置验证

HTML5 提供了一些内置的表单验证功能，例如 required、pattern、min、max 等属性。这些属性可以简单地实现基本的表单验证。

 

实例

<form>

 <label for="email">Email:</label>

 <input type="email" id="email" name="email" required>

 <input type="submit" value="Submit">

</form>

在上面的例子中，required 属性确保用户必须输入电子邮件地址，而 type="email" 会自动验证输入是否为有效的电子邮件格式。

 

2. 使用 JavaScript 自定义验证

下面的函数用来检查用户是否已填写表单中的必填（或必选）项目。假如必填或必选项为空，那么警告框会弹出，并且函数的返回值为 false，否则函数的返回值则为 true（意味着数据没有问题）：

 

function validateForm()

{

 var x=document.forms["myForm"]["fname"].value;

 if (x==null || x=="")

 {

  alert("姓必须填写");

  return false;

 }

}

以上函数在 form 表单提交时被调用:

 

实例

<form name="myForm" action="demo-form.php" onsubmit="return validateForm()" method="post">

姓: <input type="text" name="fname">

<input type="submit" value="提交">

</form>

 

尝试一下 »

 

E-mail 验证

下面的函数检查输入的数据是否符合电子邮件地址的基本语法。

 

意思就是说，输入的数据必须包含 @ 符号和点号(.)。同时，@ 不可以是邮件地址的首字符，并且 @ 之后需有至少一个点号：

 

function validateForm(){

 var x=document.forms["myForm"]["email"].value;

 var atpos=x.indexOf("@");

 var dotpos=x.lastIndexOf(".");

 if (atpos<1 || dotpos<atpos+2 || dotpos+2>=x.length){

  alert("不是一个有效的 e-mail 地址");

  return false;

 }

}

下面是连同 HTML 表单的完整代码：

 

实例

<form name="myForm" action="demo-form.php" onsubmit="return validateForm();" method="post">

  Email: <input type="text" name="email">

  <input type="submit" value="提交">

</form>

 

尝试一下 »

使用正则表达式进行验证

正则表达式（Regular Expression）是一种强大的工具，可以用于匹配复杂的字符串模式。

 

以下是一个使用正则表达式验证电子邮件地址的例子：

 

实例

function validateEmail(email) {

 var regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;

 return regex.test(email);

}

 

var email = "example@example.com";

if (validateEmail(email)) {

 console.log('Valid email address.');

} else {

 console.log('Invalid email address.');

}

**约束验证 DOM 属性**

| **属性**          | **描述**                              |
| ----------------- | ------------------------------------- |
| validity          | 布尔属性值，返回 input 输入值是否合法 |
| validationMessage | 浏览器错误提示信息                    |
| willValidate      | 指定 input 是否需要验证               |



**Validity 属性**

input 元素的 **validity** **属性**包含一系列关于 validity 数据属性:

| **属性**        | **描述**                                                   |
| --------------- | ---------------------------------------------------------- |
| customError     | 设置为 true, 如果设置了自定义的 validity 信息。            |
| patternMismatch | 设置为 true, 如果元素的值不匹配它的模式属性。              |
| rangeOverflow   | 设置为 true, 如果元素的值大于设置的最大值。                |
| rangeUnderflow  | 设置为 true, 如果元素的值小于它的最小值。                  |
| stepMismatch    | 设置为 true, 如果元素的值不是按照规定的 step 属性设置。    |
| tooLong         | 设置为 true, 如果元素的值超过了 maxLength 属性设置的长度。 |
| typeMismatch    | 设置为 true, 如果元素的值不是预期相匹配的类型。            |
| valueMissing    | 设置为 true，如果元素 (required 属性) 没有值。             |
| valid           | 设置为 true，如果元素的值是合法的。                        |

**JavaScript 验证 API**



**约束验证 DOM 方法**

| **Property**        | **Description**                                              |
| ------------------- | ------------------------------------------------------------ |
| checkValidity()     | 如果 input 元素中的数据是合法的返回 true，否则返回 false。   |
| setCustomValidity() | 设置 input 元素的 validationMessage 属性，用于自定义错误提示信息的方法。  使用 setCustomValidity 设置了自定义提示后，validity.customError  就会变成 true，checkValidity 总是会返回 false。如果要重新判断需要取消自定义提示，方式如下：  setCustomValidity('')    setCustomValidity(null)    setCustomValidity(undefined) |

 

 

## JavaScipt数组



数组有四种方式：

```
var arr1 = new Array('a', 'b', 'c');    //这是一个预定义的数组，在创建时初始化
var arr2 = ['a', 'b', 'c' ];       //同样是在创建时初始化，但是这种创建更为简洁直观
var arr3 = new Array( );   var arr4 = [ ];     //这两种是创建空的数组
```

在数组操作中，最值得注意的是下标的使用，容易出错

## JavaScript var, let 和 const

ES2015(ES6) 新增加了两个重要的 JavaScript 关键字: let 和 const。

let 声明的变量只在 let 命令所在的代码块内有效。

const 声明一个只读的常量，一旦声明，常量的值就不能改变。

在 ES6 之前，JavaScript 只有两种作用域： 全局变量 与 函数内的局部变量。

全局变量
在函数外声明的变量作用域是全局的：

实例
var carName = "Volvo";

// 这里可以使用 carName 变量

function myFunction() {
    // 这里也可以使用 carName 变量
}

尝试一下 »
全局变量在 JavaScript 程序的任何地方都可以访问。

局部变量
在函数内声明的变量作用域是局部的（函数内）：

实例
// 这里不能使用 carName 变量

function myFunction() {
    var carName = "Volvo";
    // 这里可以使用 carName 变量
}

// 这里不能使用 carName 变量

尝试一下 »
函数内使用 var 声明的变量只能在函数内访问，如果不使用 var 则是全局变量。

JavaScript 块级作用域(Block Scope)
使用 var 关键字声明的变量不具备块级作用域的特性，它在 {} 外依然能被访问到。

{ 
    var x = 2; 
}
// 这里可以使用 x 变量
在 ES6 之前，是没有块级作用域的概念的。

ES6 可以使用 let 关键字来实现块级作用域。

let 声明的变量只在 let 命令所在的代码块 {} 内有效，在 {} 之外不能访问。

{ 
    let x = 2;
}
// 这里不能使用 x 变量

重新定义变量
使用 var 关键字重新声明变量可能会带来问题。

在块中重新声明变量也会重新声明块外的变量：

实例
var x = 10;
// 这里输出 x 为 10
{ 
    var x = 2;
    // 这里输出 x 为 2
}
// 这里输出 x 为 2

尝试一下 »
let 关键字就可以解决这个问题，因为它只在 let 命令所在的代码块 {} 内有效。

实例
var x = 10;
// 这里输出 x 为 10
{ 
    let x = 2;
    // 这里输出 x 为 2
}
// 这里输出 x 为 10

循环作用域
使用 var 关键字：

实例
var i = 5;
for (var i = 0; i < 10; i++) {
    // 一些代码...
}
// 这里输出 i 为 10

尝试一下 »
使用 let 关键字：

实例
var i = 5;
for (let i = 0; i < 10; i++) {
    // 一些代码...
}
// 这里输出 i 为 5

尝试一下 »
在第一个实例中，使用了 var 关键字，它声明的变量是全局的，包括循环体内与循环体外。

在第二个实例中，使用 let 关键字， 它声明的变量作用域只在循环体内，循环体外的变量不受影响。
局部变量
在函数体内使用 var 和 let 关键字声明的变量有点类似。

它们的作用域都是 局部的:

// 使用 var
function myFunction() {
    var carName = "Volvo";   // 局部作用域
}

// 使用 let
function myFunction() {
    let carName = "Volvo";   //  局部作用域
}
全局变量
在函数体外或代码块外使用 var 和 let 关键字声明的变量也有点类似。

它们的作用域都是 全局的:

// 使用 var
var x = 2;       // 全局作用域

// 使用 let
let x = 2;       // 全局作用域

HTML 代码中使用全局变量
在 JavaScript 中, 全局作用域是针对 JavaScript 环境。

在 HTML 中, 全局作用域是针对 window 对象。

使用 var 关键字声明的全局作用域变量属于 window 对象:

实例
var carName = "Volvo";
// 可以使用 window.carName 访问变量

尝试一下 »
使用 let 关键字声明的全局作用域变量不属于 window 对象:

实例
let carName = "Volvo";
// 不能使用 window.carName 访问变量

尝试一下 »
重置变量
使用 var 关键字声明的变量在任何地方都可以修改：

实例
var x = 2;

// x 为 2

var x = 3;

// 现在 x 为 3

尝试一下 »
在相同的作用域或块级作用域中，不能使用 let 关键字来重置 var 关键字声明的变量:

var x = 2;       // 合法
let x = 3;       // 不合法

{
    var x = 4;   // 合法
    let x = 5   // 不合法
}
在相同的作用域或块级作用域中，不能使用 let 关键字来重置 let 关键字声明的变量:

let x = 2;       // 合法
let x = 3;       // 不合法

{
    let x = 4;   // 合法
    let x = 5;   // 不合法
}
在相同的作用域或块级作用域中，不能使用 var 关键字来重置 let 关键字声明的变量:

let x = 2;       // 合法
var x = 3;       // 不合法

{
    let x = 4;   // 合法
    var x = 5;   // 不合法
}
let 关键字在不同作用域，或不同块级作用域中是可以重新声明赋值的:

let x = 2;       // 合法

{
    let x = 3;   // 合法
}

{
    let x = 4;   // 合法
}

变量提升
JavaScript 中，var 关键字定义的变量可以在使用后声明，也就是变量可以先使用再声明（JavaScript 变量提升）。

实例
// 在这里可以使用 carName 变量

var carName;

尝试一下 »
let 关键字定义的变量则不可以在使用后声明，也就是变量需要先声明再使用。

// 在这里不可以使用 carName 变量  

let carName;


const 关键字


const 用于声明一个或多个常量，声明时必须进行初始化，且初始化后值不可再修改：

实例
const PI = 3.141592653589793;
PI = 3.14;      // 报错
PI = PI + 10;   // 报错

尝试一下 »
const定义常量与使用let 定义的变量相似：

二者都是块级作用域
都不能和它所在作用域内的其他变量或函数拥有相同的名称
两者还有以下两点区别：

const声明的常量必须初始化，而let声明的变量不用
const 定义常量的值不能通过再赋值修改，也不能再次声明。而 let 定义的变量值可以修改。
var x = 10;
// 这里输出 x 为 10
{ 
    const x = 2;
    // 这里输出 x 为 2
}
// 这里输出 x 为 10



并非真正的常量
const 的本质: const 定义的变量并非常量，并非不可变，它定义了一个常量引用一个值。使用 const 定义的对象或者数组，其实是可变的。下面的代码并不会报错：

实例
// 创建常量对象
const car = {type:"Fiat", model:"500", color:"white"};

// 修改属性:
car.color = "red";

// 添加属性
car.owner = "Johnson";


但是我们不能对常量对象重新赋值：

实例
const car = {type:"Fiat", model:"500", color:"white"};
car = {type:"Volvo", model:"EX60", color:"red"};    // 错误

在相同的作用域或块级作用域中，不能使用 const 关键字来重置 var 和 let关键字声明的变量:

var x = 2;         // 合法
const x = 2;       // 不合法
{
    let x = 2;     // 合法
    const x = 2;   // 不合法
}
在相同的作用域或块级作用域中，不能使用 const 关键字来重置 const 关键字声明的变量:

const x = 2;       // 合法
const x = 3;       // 不合法
x = 3;             // 不合法
var x = 3;         // 不合法
let x = 3;         // 不合法

{
    const x = 2;   // 合法
    const x = 3;   // 不合法
    x = 3;         // 不合法
    var x = 3;     // 不合法
    let x = 3;     // 不合法
}
const 关键字在不同作用域，或不同块级作用域中是可以重新声明赋值的:

const x = 2;       // 合法

{
    const x = 3;   // 合法
}

{
    const x = 4;   // 合法
}
变量提升
JavaScript var 关键字定义的变量可以在使用后声明，也就是变量可以先使用再声明（JavaScript 变量提升）。

const 关键字定义的变量则不可以在使用后声明，也就是变量需要先声明再使用。

carName = "Volvo";    // 在这里不可以使用 carName 变量
const carName = "Volvo";

文中说到 const 定义的变量并非不可改变，比如使用const声明对象，可以改变对象值。

那么什么情况能彻底“锁死”变量呢？

可以使用Object.freeze()方法来 冻结变量 ，如：

const obj = {
  name:"1024kb"
}
Object.freeze(obj)
// 此时对象obj被冻结，返回被冻结的对象
需要注意的是，被冻结后的对象不仅仅是不能修改值，同时也

不能向这个对象添加新的属性
不能修改其已有属性的值
不能删除已有属性

| 特性               | `var`                      | `let`                    | `const`                |
| :----------------- | :------------------------- | :----------------------- | :--------------------- |
| **作用域**         | 函数作用域                 | 块级作用域 `{}`          | 块级作用域 `{}`        |
| **全局属性**       | 属于 `window` 对象         | 不属于 `window`          | 不属于 `window`        |
| **重复声明**       | ✅ 允许                     | ❌ 不允许                 | ❌ 不允许               |
| **先使用后声明**   | ✅ 允许（提升为 undefined） | ❌ 不允许（暂时性死区）   | ❌ 不允许（暂时性死区） |
| **可修改性**       | ✅ 可修改                   | ✅ 可修改                 | ❌ 不可修改（常量）     |
| **必须初始化**     | ❌ 可选（默认 undefined）   | ❌ 可选（默认 undefined） | ✅ 必须初始化           |
| **不同作用域重名** | ✅ 允许                     | ✅ 允许                   | ✅ 允许                 |
