1 JavaScript 简介


JavaScript 是互联网上最流行的脚本语言,这门语言可用于 HTML 和 web,更可广泛用于服务器、PC、笔记本电脑、平板电脑和智能手机等设备。


1.1 JavaScript 是脚本语言

JavaScript 是一种轻量级的编程语言。

JavaScript 是可插入 HTML 页面的编程代码。

JavaScript 插入 HTML 页面后,可由所有的现代浏览器执行。

JavaScript 很容易学习。


1.2 JavaScript:直接写入 HTML 输出流

1.3 实例

1
2
3
4
5
6
7
8
9
10
document.write("<h1>这是一个标题</h1>"); 
document.write("<p>这是一个段落。</p>");
````

您只能在 HTML 输出中使用 document.write。如果您在文档加载后使用该方法,会覆盖整个文档。

## 1.4 JavaScript:对事件的反应

**实例**


1
2
3
4
5
6
7
8
9
10
11

alert() 函数在 JavaScript 中并不常用,但它对于代码测试非常方便。

onclick 事件只是您即将在本教程中学到的众多事件之一。

## 1.5 JavaScript:改变 HTML 内容

使用 JavaScript 来处理 HTML 内容是非常强大的功能。

**实例**


x=document.getElementById(“demo”); //查找元素 x.innerHTML=”Hello JavaScript”;
//改变内容
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41

您会经常看到 **`document.getElementById("_some id_")`** 。这个方法是 HTML DOM 中定义的。

`DOM (Document Object Model)`(文档对象模型)是用于访问 HTML 元素的正式 W3C 标准。

您将在本教程的多个章节中学到有关 HTML DOM 的知识。
## 1.6 JavaScript:改变 HTML 图像

本例会动态地改变 HTML `<image>` 的来源(src):


### 1.6.1 点亮灯泡

点击以下灯泡查看效果:

```html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>菜鸟教程(runoob.com)</title>
</head>
<body>
<script>
function changeImage()
{
element=document.getElementById('myimage')
if (element.src.match("bulbon"))
{
element.src="[https://www.runoob.com/images/pic_bulboff.gif](https://www.runoob.com/images/pic_bulboff.gif)";
}
else
{
element.src="[https://www.runoob.com/images/pic_bulbon.gif](https://www.runoob.com/images/pic_bulbon.gif)";
}
}
</script>
<img id="myimage" onclick="changeImage()" src="[https://www.runoob.com/images/pic_bulboff.gif](https://www.runoob.com/images/pic_bulboff.gif)" width="100" height="180">
<p>点击灯泡就可以打开或关闭这盏灯</p>
</body>
</html>

以上实例中代码 element.src.match(“bulbon”) 的作用意思是:检索 < img id=”myimage” onclick=”changeImage()” src=”/images/pic_bulboff.gif” width=”100” height=”180”> 里面 src 属性的值有没有包含 bulbon 这个字符串,如果存在字符串 bulbon ,图片 src

更新为 bulboff.gif ,若匹配不到 bulbon 字符串,src 则更新为 bulbon.gif

JavaScript 能够改变任意 HTML 元素的大多数属性,而不仅仅是图片。


1.7 JavaScript:改变 HTML 样式

改变 HTML 元素的样式,属于改变 HTML 属性的变种。

实例

1
2
x=document.getElementById("demo") //找到元素
x.style.color="#ff0000"; //改变样式

1.8 JavaScript:验证输入

JavaScript 常用于验证用户的输入。

实例

1
2
3
if isNaN(x) {
alert("不是数字");
}

以上实例只是普通的验证,如果要在生产环境中使用,需要严格判断,如果输入的空格,或者连续空格 isNaN

是判别不出来的。可以添加正则来判断(后续章节会说明):

实例

1
2
3
4
if(isNaN(x)||x.replace(/(^\s*)|(\s*$)/g,"")=="")
{
alert("不是数字");
}


注意

JavaScript 与 Java 是两种完全不同的语言,无论在概念上还是设计上。

Java(由 Sun 发明)是更复杂的编程语言。

ECMA-262 是 JavaScript 标准的官方名称。

JavaScript 由 Brendan Eich 发明。它于 1995 年出现在 Netscape 中(该浏览器已停止更新),并于 1997 年被

ECMA(一个标准协会)采纳。


1.9 ECMAScript 版本

JavaScript 已经由 ECMA(欧洲电脑制造商协会)通过 ECMAScript 实现语言的标准化。

年份 名称 描述
1997 ECMAScript 1 第一个版本
1998 ECMAScript 2 版本变更
1999 ECMAScript 3 添加正则表达式
添加 try/catch
ECMAScript 4 没有发布
2009 ECMAScript 5 添加 “strict mode”,严格模式
添加 JSON 支持
2011 ECMAScript 5.1 版本变更
2015 ECMAScript 6 添加类和模块
2016 ECMAScript 7 增加指数运算符 (**)
增加 Array.prototype.includes

ECMAScript 6 也称为 ECMAScript 2015。

ECMAScript 7 也称为 ECMAScript 2016。

2 JavaScript 用法


HTML 中的 Javascript 脚本代码必须位于 <script></script> 标签之间。

Javascript 脚本代码可被放置在 HTML 页面的 <body><head> 部分中。


2.1 <script> 标签

如需在 HTML 页面中插入 JavaScript,请使用<script> 标签。

<script></script> 会告诉 JavaScript 在何处开始和结束。

<script></script>之间的代码行包含了 JavaScript:

<script> alert("我的第一个 JavaScript"); </script>

您无需理解上面的代码。只需明白,浏览器会解释并执行位于 <script></script>之间的 JavaScript 代码

那些老旧的实例可能会在 <script> 标签中使用 type=”text/javascript”。现在已经不必这样做了。

JavaScript 是所有现代浏览器以及 HTML5 中的默认脚本语言。


2.2 <body>中的 JavaScript

在本例中,JavaScript 会在页面加载时向 HTML 的 <body> 写文本:

2.3 实例

1
2
3
4
5
6
7
8
9
10
<!DOCTYPE html>
<html>
<body>
<script>
document.write("<h1>这是一个标题</h1>");
document.write("<p>这是一个段落</p>");
</script>
</body>
</html>


2.4 JavaScript 函数和事件

上面例子中的 JavaScript 语句,会在页面加载时执行。

通常,我们需要在某个事件发生时执行代码,比如当用户点击按钮时。

如果我们把 JavaScript 代码放入函数中,就可以在事件发生时调用该函数。


2.5 在 <head> 或者<body>的JavaScript

您可以在 HTML 文档中放入不限数量的脚本。

脚本可位于 HTML 的 <body><head>部分中,或者同时存在于两个部分中。

通常的做法是把函数放入 <head> 部分中,或者放在页面底部。这样就可以把它们安置到同一处位置,不会干扰页面的内容。


2.6 <head> 中的 JavaScript 函数

在本例中,我们把一个 JavaScript 函数放置到 HTML 页面的 <head> 部分。

该函数会在点击按钮时被调用:

实例

JavaScript

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!DOCTYPE html>
<html>
<head>
<script>
function myFunction()
{
document.getElementById("demo").innerHTML="我的第一个 JavaScript 函数";
}
</script>
</head>
<body>
<h1>我的 Web 页面</h1>
<p id="demo">一个段落</p>
<button type="button" onclick="myFunction()">尝试一下</button>
</body>
</html>

2.7 <body>中的 JavaScript 函数

在本例中,我们把一个 JavaScript 函数放置到 HTML 页面的 <body> 部分。

该函数会在点击按钮时被调用:

实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!DOCTYPE html>
<html>
<body>
<h1>我的 Web 页面</h1>
<p id="demo">一个段落</p>
<button type="button" onclick="myFunction()">尝试一下</button>
<script>
function myFunction()
{
document.getElementById("demo").innerHTML="我的第一个 JavaScript 函数";
}
</script>
</body>
</html>

2.8 外部的 JavaScript

也可以把脚本保存到外部文件中。外部文件通常包含被多个网页使用的代码。

外部 JavaScript 文件的文件扩展名是 .js。

如需使用外部文件,请在 <script> 标签的 “src” 属性中设置该 .js 文件:

2.9 实例

1
2
3
4
5
6
7
<!DOCTYPE html> 
<html>
<body>
<script src="myScript.js">
</script>
</body>
</html>

你可以将脚本放置于 <head>或者 <body>中,放在 <script>标签中的脚本与外部引用的脚本运行效果完全一致。

myScript.js 文件代码如下:

1
2
3
4
function myFunction() 
{
document.getElementById("demo").innerHTML="我的第一个 JavaScript 函数";
}

外部脚本不能包含 <script> 标签。

3 Chrome 浏览器中执行 JavaScript

本章节为大家介绍如何在 Chrome 浏览器上进行 JavaScript 代码的运行与调试。

Chrome 是由 Google 开发的免费网页浏览器,对于前端开发来说(尤其是调试代码)非常方便。

Chrome 官网地址:https://www.google.com/intl/zh-CN/chrome/。

我们在 Chrome 浏览器中可以通过按下 F12 按钮或者右击页面,选择”检查”来开启开发者工具。

也可以在右上角菜单栏选择 “更多工具”=》”开发者工具” 来开启:

3.1 1、Console 窗口调试 JavaScript 代码

打开开发者工具后,我们可以在 Console 窗口调试 JavaScript代码,如下图:

上图中我们在 > 符号后输入我们要执行的代码 console.log(“runoob”),按回车后执行。

我们也可以在其他地方复制一段代码过来执行,比如复制以下代码到 Console 窗口,按回车执行:

1
2
console.log("runoob-1")
console.log("runoob-2")

清空 Console 窗口到内容可以按以下按钮:


3.2 2、Chrome snippets 小脚本

我们也可以在 Chrome 浏览器中创建一个脚本来执行,在开发者工具中点击 Sources 面板,选择 Snippets 选项卡,在导航器中右击鼠标,然后选择 Create new snippet 来新建一个脚本文件:

如果你没看到 Snippets ,可以点下面板上到 >> 就能看到了。

点击 Create new snippet 后,会自动创建一个文件,你只需在右侧窗口输入以下代码,然后按 Command+S(Mac)或 Ctrl+S(Windows 和 Linux)保存更改即可。

1
2
console.log("runoob-1")
console.log("runoob-2")

保存后,右击文件名,选择 “Run” 执行代码:

4 嵌入方式

4.1 内嵌式

理论上js可以插入任何一个地方,但是习惯上写在head标签或body下

JavaScript

1
2
3
<script>
alert('x')
</script>

4.2 外联式

JavaScript

1
<script type="text/javascript" src="文件路径"></script>

4.3 行内式

直接写在标签上,是一个简写的事件,所以又称为事件属性

JavaScript

1
<input type="button" value="点我" onclick="alert('xss');"/>

5 语句

1.在编程语言中,这些编程指令被称为语句

2.js由以下语句构成:

值、运算符、表达式、关键词和注释。

3.用分号(;)分割js语句

6 注释

单行注释: // 注释语句

多行注释: /注释语句/

注意:多行注释不能嵌套使用。

7 js保留关键字

Javascript 的保留关键字不可以用作变量、标签或者函数名。有些保留关键字是作为 Javascript 以后扩展使用。

关键字
abstract arguments boolean break byte
case catch char class const
continue debugger default delete const
double else enum eval do
extends false final finally export
for function goto if float
import in instanceof int interface
let long native new newnull
package private protected public return
short static super switch synchronized
this throw throws transient true
try typeof var void volatile
while with yield

标记的关键字是 ECMAScript5 中新添加的。

8 数据类型

JavaScript 共有八种数据类型,可以分为原始数据类型引用数据类型

  • 原始数据类型(栈 Stack)UndefinedNullBooleanNumberString,以及 ES6 新增的 SymbolBigInt
    • 特点:直接存储在栈中的简单数据段,占据空间小、大小固定,属于被频繁使用数据,所以放入栈中存储。
  • 引用数据类型(堆 Heap)Object(包括对象、数组和函数等)。
    • 特点:存储在堆中的对象,占据空间大、大小不固定。引用数据类型在栈中只存储了一个指针,该指针指向堆中该实体的起始地址。当解释器寻找引用值时,会先检索其在栈中的地址,再从堆中获得实体。(如果全存在栈中,会严重影响程序运行性能)。

补充:操作系统中的堆栈概念
栈区内存由编译器自动分配释放(如函数的参数值、局部变量);堆区内存一般由开发者分配释放,若不释放,程序结束时由垃圾回收机制回收。


8.1 数字类型 (Number & BigInt)

JavaScript 没有整型和浮点型,只有一种数字类型,即 Number 类型(凡是数字都是数值型,不区分整数和小数)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
var x = 10;
var y = 20;
````

**类型转换与特殊值:**

- `parseInt(...)`:将某值转化为数字(整数),不成功便是 `NaN`
- `parseFloat(...)`:将某值转化为浮点数,不成功便是 `NaN`
- **NaN (Not a Number)**:非数字。它是一个“警戒值”,用于指出数字类型中的错误情况(即执行数学运算失败)。
- `typeof NaN` 的结果是 `"number"`
- `NaN` 是唯一一个非自反的值,即 `NaN === NaN` 不成立,`NaN !== NaN``true`
- **判断方法**:`isNaN()` 会尝试进行隐式类型转换,传字符串也会返回 true,不够准确;推荐使用 ES6`Number.isNaN()`,它只有在参数真的是 NaN 时才返回 true
- **Infinity**:无穷大。可用 `isFinite()` 来判断。

**进阶:为什么 0.1 + 0.2 !== 0.3?如何让其相等?**

计算机底层通过二进制存储数据。`Number` 类型遵循 IEEE 754 标准,使用 64 位双精度浮点数。它的有效数字最多只能保留 53 位,剩余的需要根据“01入”原则舍去。因为 0.10.2 的二进制是无限循环的,相加并截断后再转化为十进制就变成了 `0.30000000000000004`,导致精度丢失。

解决方案:引入机器精度(误差范围)。ES6 提供了 `Number.EPSILON`(值为 2 的 -52 次方),只要两者之差的绝对值小于它,即可判定相等:


function numberepsilon(arg1, arg2){
return Math.abs(arg1 - arg2) < Number.EPSILON;
}
console.log(numberepsilon(0.1 + 0.2, 0.3)); // true
1
2
3
4
5
6
7
8
9
10
11
12

**进阶:BigInt 类型**

因为 Number 最大安全数字是 `9007199254740991` (Number.MAX_SAFE_INTEGER),超过这个范围就会出现精度丢失。官方提出 `BigInt` 来表示任意精度的整数,安全存储超大整数。

---

## 8.2 字符串类型 (String)

表示一系列文本字符数据。由 Unicode 字符、数字、标点组成的序列。首尾由一对单引号或双引号包裹(凡是引号包裹的都是字符串)。



console.log(“\u6B22\u8FCEl来到\”Javascript 世界\””)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102

**进阶:包装类型**

基本类型本没有属性和方法,但为了便于操作,JS 会隐式地将其转换为对象(包装类型)。比如访问 `'abc'.length` 时,后台会自动执行 `String('abc')`。

---

## 8.3 布尔类型 (Boolean)

布尔类型仅有两个值:`true` 和 `false`。底层逻辑中 true 常用 1 表达,false 常用 0 表达。

**其他值到布尔类型的转换规则(假值列表):**

以下 6 个值在转为布尔值时会被强制判定为 `false`(即假值):

`undefined`、`null`、`false`、`+0 / -0`、`NaN`、`""` (空字符串)。

从逻辑上说,除了假值列表以外的所有值,转换结果都是 `true`。

---

## 8.4 Null 与 Undefined

这两个基本数据类型分别都只有一个值,就是 `undefined` 和 `null`。

- **Undefined(未定义)**:只做声明,未初始化。一般变量声明了但还没定义时会返回 `undefined`。
- 安全获取:`undefined` 在 JS 中不是保留字,如果被当作变量名赋值会影响判断。使用 `void 0` 可以获得百分百安全的 undefined,因为 `void` 表达式不改变结果,但强制不返回值。
- **Null(空对象)**:代表的含义是空,主要用于赋值给一些可能会返回对象的变量,作为初始化。
- 历史遗留 Bug:**`typeof null` 的结果是 `"object"`**。因为在 JS 初代版本中,所有值存在 32 位单元中,低位用类型标签标识(1-3 bits)。其中对象类型标签是 `000`,而 null 的机器码 NULL 指针全是 0,导致它的标签也是 `000`,所以被误判为 Object。

> **比较**:`null == undefined` 会返回 `true`(隐式转换),而 `null === undefined` 会返回 `false`。

---

## 8.5 对象与数组 (Object)

对象类型包括普通对象、数组、函数等集合。

**1. 判断数组的方式有哪些?**

- `Object.prototype.toString.call(obj).slice(8,-1) === 'Array'`(最严谨准确)

- `Array.isArray(obj)`(ES6 推荐方法)

- `obj instanceof Array`

- `obj.__proto__ === Array.prototype`

- `Array.prototype.isPrototypeOf(obj)`


**2. 如何判断一个对象是空对象?**

- JSON 转换法:`if(JSON.stringify(Obj) == '{}') { console.log('空对象'); }`

- ES6 推荐法:`if(Object.keys(Obj).length === 0) { console.log('空对象'); }`


**3. 深拷贝与浅拷贝**

`Object.assign({}, outObj)` 和 扩展运算符 `{...outObj}` **都是浅拷贝**。

它们只复制对象的第一层属性,如果对象的属性还是个对象,则只会复制其内存地址(指针)。

---

## 8.6 类型转换核心机制

**1. 隐式类型转换 (ToPrimitive)**

JS 每个值都有自带的 `ToPrimitive` 方法,用于将对象转换为基本类型:

- 期望转为 **Number** 时:先调用对象的 `valueOf()`,如果是原始值则返回;否则调 `toString()`;再不行报错。
- 期望转为 **String** 时:先调用对象的 `toString()`,如果是原始值则返回;否则调 `valueOf()`;再不行报错。
**2. 运算符场景下的转换规则**

- **`+` 操作符**:只要有一方是字符串(或可被转为字符串),就执行**字符串拼接**;否则执行**数字加法**。
- **`-`、`*`、`/` 操作符**:会将操作数全部转为数字再计算(比如 `1 * '23' // 23`)。
- **`==` 操作符**:两边类型不一致时,会尽量都转为 Number 再比较。比如 `'0' == false` 返回 true(两者转数字都是 0)。
- **`<` 和 `>` 比较符**:如果两边都是字符串,则按字母表顺序比较;否则转成数字比较。

---

## 8.7 逻辑与比较运算符进阶

**1. `||` 和 `&&` 返回的是什么?**

它们返回的**不是 true 或 false**,而是其中一个操作数的**实际值**。

- `||` (或):左侧判断为 true,直接返回左侧的值;左侧为 false,返回右侧的值。
- `&&` (与):左侧判断为 true,返回右侧的值;左侧为 false,返回左侧的值。

**2. `Object.is()` 与 `===`、`==` 的区别?**

- `==`:类型不同会先做强制类型转换,再比较。
- `===`(全等):类型不同直接返回 false,不转换。
- `Object.is()`:基本等同于 `===`,但修复了两个特殊情况:让 `-0` 和 `+0` 不相等,让 `NaN` 和 `NaN` 相等。

**3. `instanceof` 的实现原理**

作用是判断右侧构造函数的 `prototype` 属性是否出现在左侧对象的原型链上。


function myInstanceof(left, right) {
let proto = Object.getPrototypeOf(left) // 获取对象的原型
let prototype = right.prototype; // 获取构造函数的原型
while (true) {
if (!proto) return false; // 找完了都没找到
if (proto === prototype) return true; // 匹配成功
proto = Object.getPrototypeOf(proto); // 沿着原型链向上找
}
}
1
2
3
4
5
6
7
8
9
# 9 内置函数

JavaScript 提供了一些全局可用的内置函数,可以直接在代码中使用,无需任何额外导入。

## 9.1 打印/输出函数 :

### 9.1.1 文字输出



var s = ‘My name is LiHua’
document.write(s)
1
2
3
4
5
6

**注意**:在文档加载后使用 `document.write` 会覆盖整个文档。

### 9.1.2 控制台输出



console.log(s)
1
2
3
4
5

## 9.2 弹出函数

### 9.2.1 警告框


alert(“中二病也要谈恋爱”);
1
2
3
4
5

显示一个带有指定消息的警告框。

### 9.2.2 确认框


var flag = confirm(“你喜欢我吗?”);
console.log(flag);
1
2
3
4
5

显示一个带有确认和取消按钮的确认框,返回布尔值,代表用户的选择

### 9.2.3 输入框


var flag = prompt(“你喜欢我吗?”);
console.log(flag);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

显示一个带有输入框的对话框,用于获取用户输入,并返回用户输入的字符串。

## 9.3 类型转换函数:

`parseInt()`:将字符串转换为整数。

`parseFloat()`:将字符串转换为浮点数。

`String()`:将其他数据类型转换为字符串。

`Number()`:将其他数据类型转换为数字。

`Boolean()`: 将其他数据类型转换为布尔类型。

## 9.4 代码执行函数



var s = “document.write(“My name is AJEST”)”;
eval(s);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41

将字符串作为 JavaScript 代码执行。应该谨慎使用,因为它可能导致安全问题。

## 9.5 检查函数

`isNaN()`:检查一个值是否为 NaN (Not a Number)。

`typeof()`:返回变量的数据类型。

## 9.6 编码解码函数

`encodeURI()` 和 `decodeURI()`:用于编码和解码 URI。

`JSON.stringify()`:将 JavaScript 对象转换为 JSON 字符串。`JSON.parse()`:将 JSON 字符串转换为 JavaScript 对象。

## 9.7 定时器函数

`setTimeout()` 和 `setInterval()`:用于设置定时器。 `clearTimeout()` 和 `clearInterval()`: 用于取消定时器。

# 10 运算符

## 10.1 算数运算符

|**算数运算符**|**含义**|
|---|---|
|+|加法|
|-|减法|
|*|乘法|
|/|除法|
|%|求余数|
|++|自增|
|--|自减|

> **注意**:
>
> a++与++a:a++是先使用a,再自加;++a是先自加,再使用。

## 10.2 赋值运算符

JavaScript


var a = 10;
a+=3
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40

## 10.3 关系运算符

|**关系运算符**|**说明**|
|---|---|
|>|大于|
|<|小于|
|>=|大于等于|
|==|等于|
|!=|不等于|
|===|全等|
|!==|不全等|

# 11 js函数

## 11.1 字符串函数 (String 对象的方法)

字符串对象提供了许多用于处理字符串的函数:

### 11.1.1 截取函数:

#### 11.1.1.1 slice(start, end)

提取字符串的某个部分,并以新的字符串返回被提取的部分。

- `start`: 必需。
- 指定提取部分的起始索引位置(包括该位置的元素)。
- 如果为负数,则从字符串或数组的末尾开始计算(例如,-1 表示最后一个元素,-2 表示倒数第二个元素)。
- `end`: 可选。
- 指定提取部分的结束索引位置(不包括该位置的元素)。
- 如果省略,则提取到字符串或数组的末尾。
- 如果为负数,则从字符串或数组的末尾开始计算

#### 11.1.1.2 substring(start, end)

提取字符串中两个指定的索引号之间的字符。

- 将所有负数的参数值都视为 `0`
- 如果 `start` 大于 `end`,则 `substring()` 会交换这两个参数,相当于使用了 `substring(end, start)`。


let str = “Hello, World!”;
let sub = str.substr(7, 5); // 从索引 7 开始,提取 5 个字符
console.log(sub); // 输出 “World”
1
2
3
4
5
6
7
8
9

#### 11.1.1.3 substr(start, length)

从字符串中提取子字符串,它从指定的起始索引位置开始,提取指定数量的字符。

- `start`: 必需。要提取的子串的起始下标。必须是数值。如果是负数,那么该参数声明从字符串的尾部开始算起的位置。
- `length`: 可选。子串中的字符数。必须是数值。如果省略了该参数,那么返回直到字符串结尾的子串。



let str=’Hello,world’
console.log(substr(0,5))//输出Hello
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88

|**特性**|**slice(start, end)**|**substring(start,length)**|**substr(start, length)**|
|---|---|---|---|
|第二个参数|结束索引(不包含)|起始索引, 结束索引 (不包含)|要提取的字符数|
|负数索引|从末尾计算|将所有负数的参数值都视为 `0`|`start` 从末尾计算,`length` 负数返回空字符串|
|省略第二个参数|提取到末尾|省略 `end`,提取从 `start` 到字符串末尾的所有字符。|提取到末尾|
|`start` > `end`|返回空字符串|如果 `start` 是正数且大于或等于字符串长度,则返回空字符串。|行为取决于 `length` 的值|

### 11.1.2 查找/定位函数

#### 11.1.2.1 indexOf()

返回字符串中指定子字符串第一次出现的索引。

#### 11.1.2.2 lastIndexOf()

返回字符串中指定子字符串最后一次出现的索引

#### 11.1.2.3 charAt()

返回指定索引位置的字符

### 11.1.3 连接/分割函数

`concat()`:连接两个或多个字符串。

`split()`:将字符串分割成子字符串数组。

### 11.1.4 大小写转化函数

`toLowerCase()`:将字符串转换为小写。
`toUpperCase()`:将字符串转换为大写。
`trim()`:去除字符串两端的空白字符。

## 11.2 数组函数 (Array 对象的方法)

数组对象提供了许多用于处理数组的函数:

- `push()`:向数组末尾添加一个或多个元素。
- `pop()`:删除数组末尾的元素并返回该元素。
- `shift()`:删除数组的第一个元素并返回该元素。
- `unshift()`: 向数组的开头添加一个或更多元素,并返回新的长度。
- `concat()`:连接两个或多个数组。
- `join()`:将数组的所有元素连接成一个字符串。
- `slice()`:提取数组的一部分并返回一个新数组。
- `splice()`:从数组中添加或删除元素。
- `indexOf()`:在数组中查找指定元素第一次出现的索引。
- `lastIndexOf()`:在数组中查找指定元素最后一次出现的索引。
- `forEach()`: 为数组的每个元素调用一次函数。
- `map()`: 通过对每个元素调用函数来创建新数组。
- `filter()`: 创建一个新数组,其中包含通过测试的数组元素。
- `reduce()`: 针对数组的每个元素执行一个提供的 reducer 函数,将其汇总为单个返回值。
- `sort()`: 对数组的元素进行排序。

## 11.3 数字函数 (Math 对象的方法)

Math 对象提供了用于处理数字的函数

- `Math.random()`: 返回 0 到 1 之间的随机数。
- `Math.abs()`:返回数字的绝对值。
- `Math.round()`:将数字四舍五入到最接近的整数。
- `Math.ceil()`:将数字向上舍入到最接近的整数。
- `Math.floor()`:将数字向下舍入到最接近的整数。
- `Math.max()`:返回一组数字中的最大值。
- `Math.min()`:返回一组数字中的最小值。
- `Math.pow()`: 返回 x 的 y 次幂。
- `Math.sqrt()`:返回数字的平方根。

## 11.4 日期函数 (Date 对象的方法)

Date 对象提供了用于处理日期和时间的函数。

- `Date()`: 创建日期对象,例如获取当前时间、设置指定日期等
- `getFullYear()`: 以四位数字返回年份。
- `getMonth()`: 返回月份(0-11)。
- `getDate()`: 返回月份的某一天 (1-31)。
- `getHours()`: 返回小时 (0-23)。
- `getMinutes()`: 返回分钟 (0-59)。
- `getSeconds()`: 返回秒数 (0-59)。
- `getMilliseconds()`: 返回毫秒数 (0-999)。
- `getTime()`: 返回 1970 年 1 月 1 日至今的毫秒数。

## 11.5 自定义函数

开发者根据需求定义的函数,使用 `function` 关键字声明。

示例


function myFunction(param1, param2)
{//函数体
return result;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80

## 11.6 DOM 操作函数 (在浏览器环境中)

当 JavaScript 在浏览器环境中运行时,可以使用 DOM API 与 HTML 文档进行交互:

- `getElementById()`:通过 id 获取元素。
- `getElementsByClassName()`: 通过 class 名称获取元素。
- `getElementsByTagName()`:通过标签名称获取元素。
- `querySelector()`:返回匹配指定 CSS 选择器的第一个元素。
- `querySelectorAll()`:返回匹配指定 CSS 选择器的所有元素。
- `createElement()`: 创建一个新的 HTML 元素。
- `appendChild()`:将一个元素添加到另一个元素的子元素列表中。
- `removeChild()`:从父元素中删除一个子元素。
- `getAttribute()`: 获取元素的指定属性的值。
- `setAttribute()`: 设置元素的指定属性的值。
- `innerHTML`: 获取或设置 HTML 元素的 HTML 内容。
- `addEventListener()`: 向指定元素添加事件处理程序。


# 12 ES6 核心新特性

---

## 12.1 let、const、var 的区别

**(1) 块级作用域**
块作用域由 `{}` 包括,`let` 和 `const` 具有块级作用域,`var` 不存在块级作用域。块级作用域解决了 ES5 中的两个问题:
* 内层变量可能覆盖外层变量。
* 用来计数的循环变量泄露为全局变量。

**(2) 变量提升**
`var` 存在变量提升,`let` 和 `const` 不存在变量提升,即变量只能在声明之后使用,否则会报错。

**(3) 给全局添加属性**
浏览器的全局对象是 `window`,Node 的全局对象是 `global`。`var` 声明的变量为全局变量,并且会将该变量添加为全局对象的属性,但是 `let` 和 `const` 不会。

**(4) 重复声明**
`var` 声明变量时,可以重复声明变量,后声明的同名变量会覆盖之前声明的变量。`const` 和 `let` 不允许重复声明变量。

**(5) 暂时性死区**
在使用 `let`、`const` 命令声明变量之前,该变量都是不可用的。这在语法上称为**暂时性死区**。使用 `var` 声明的变量不存在暂时性死区。

**(6) 初始值设置**
在变量声明时,`var` 和 `let` 可以不用设置初始值。而 `const` 声明变量必须设置初始值。

**(7) 指针指向**
`let` 和 `const` 都是 ES6 新增的用于创建变量的语法。`let` 创建的变量是可以更改指针指向(可以重新赋值)。但 `const` 声明的变量是不允许改变指针的指向。

**总结对比表:**

| 区别 | var | let | const |
| :--- | :---: | :---: | :---: |
| 是否有块级作用域 | × | ✔️ | ✔️ |
| 是否存在变量提升 | ✔️ | × | × |
| 是否添加全局属性 | ✔️ | × | × |
| 能否重复声明变量 | ✔️ | × | × |
| 是否存在暂时性死区 | × | ✔️ | ✔️ |
| 是否必须设置初始值 | × | × | ✔️ |
| 能否改变指针指向 | ✔️ | ✔️ | × |

---

## 12.2 const 对象的属性可以修改吗?

`const` 保证的并不是变量的值不能改动,而是**变量指向的那个内存地址不能改动**。
* 对于**基本类型的数据**(数值、字符串、布尔值),其值就保存在变量指向的那个内存地址,因此等同于常量。
* 对于**引用类型的数据**(主要是对象和数组)来说,变量指向数据的内存地址保存的只是一个指针,`const` 只能保证这个指针是固定不变的,至于它指向的数据结构是不是可变的,就完全不能控制了。

---

## 12.3 箭头函数与普通函数的区别

**(1) 箭头函数比普通函数更加简洁**
* 如果没有参数,就直接写一个空括号即可。
* 如果只有一个参数,可以省去参数的括号。
* 如果有多个参数,用逗号分割。
* 如果函数体的返回值只有一句,可以省略大括号。
* 如果函数体不需要返回值,且只有一句话,可以给这个语句前面加一个 `void` 关键字。常见于调用一个函数:
```javascript
let fn = () => void doesNotReturn();

(2) 箭头函数没有自己的 this 箭头函数不会创建自己的 this,它只会在自己作用域的上一层继承 this。所以箭头函数中 this 的指向在它在定义时已经确定了,之后不会改变。可以理解为它是捕获其所在上下文的 this 值作为自己的 this

(3) 箭头函数继承来的 this 指向永远不会改变

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var id = 'GLOBAL';
var obj = {
id: 'OBJ',
a: function(){
console.log(this.id);
},
b: () => {
console.log(this.id);
}
};
obj.a(); // 'OBJ'
obj.b(); // 'GLOBAL'
new obj.a() // undefined
new obj.b() // Uncaught TypeError: obj.b is not a constructor

_注:定义对象的大括号 {} 是无法形成一个单独的执行环境的,它依旧是处于全局执行环境中。_

(4) call()、apply()、bind() 等方法不能改变箭头函数中 this 的指向

1
2
3
4
5
6
var id = 'Global';
let fun1 = () => { console.log(this.id) };
fun1(); // 'Global'
fun1.call({id: 'Obj'}); // 'Global'
fun1.apply({id: 'Obj'}); // 'Global'
fun1.bind({id: 'Obj'})(); // 'Global'

(5) 箭头函数不能作为构造函数使用(不能被 new) new 操作符的执行步骤中,有一步是将构造函数中的 this 指向新创建的对象。但由于箭头函数没有自己的 this,且不能改变指向,所以不能当作构造函数使用。

(6) 箭头函数没有自己的 arguments 在箭头函数中访问 arguments 实际上获得的是它外层函数的 arguments 值。可以使用 ...rest 参数代替。

(7) 箭头函数没有 prototype

(8) 箭头函数不能用作 Generator 函数,不能使用 yield 关键字


12.4 扩展运算符 (…) 的作用及场景

扩展运算符 (...) 用于取出参数对象或数组中的所有可遍历属性,拷贝到当前对象之中。注意:扩展运算符的拷贝属于浅拷贝。

(1) 对象扩展运算符

1
2
3
let bar = { a: 1, b: 2 };
let baz = { ...bar }; // { a: 1, b: 2 }
// 等价于 Object.assign({}, bar);

如果自定义属性放在扩展运算符后面,则同名属性会被覆盖掉,常用于方便地修改对象部分属性:

1
2
let bar = {a: 1, b: 2};
let baz = {...bar, ...{a:2, b: 4}}; // {a: 2, b: 4}

(2) 数组扩展运算符 可以将一个数组转为用逗号分隔的参数序列,且每次只能展开一层数组。常见应用场景:

  • 将数组转换为参数序列
1
2
3
function add(x, y) { return x + y; }
const numbers = [1, 2];
add(...numbers) // 3
  • 复制数组
1
2
const arr1 = [1, 2];
const arr2 = [...arr1];
  • 合并数组

    1
    2
    const arr1 = ['two', 'three'];
    const arr2 = ['one', ...arr1, 'four', 'five']; // ["one", "two", "three", "four", "five"]
  • 与解构赋值结合生成新数组 (扩展运算符只能放在参数最后一位)

    1
    const [first, ...rest] = [1, 2, 3, 4, 5]; // first: 1, rest: [2, 3, 4, 5]
  • 将字符串转为真正的数组

    1
    [...'hello'] // [ "h", "e", "l", "l", "o" ]
  • 使用 Math 函数获取数组极值

    1
    2
    const numbers = [9, 4, 7, 1];
    Math.min(...numbers); // 1

12.5 rest 参数

扩展运算符被用在函数形参上时(称为 rest 参数),可以把一个分离的参数序列整合成一个数组。常用于获取函数的多余参数,或处理参数个数不确定的情况:

1
2
3
4
5
6
7
8
9
function mutiple(...args) {
console.log(args) // [1, 2, 3, 4]
let result = 1;
for (var val of args) {
result *= val;
}
return result;
}
mutiple(1, 2, 3, 4) // 24

12.6 对象与数组的解构赋值

解构是 ES6 提供的一种新的提取数据的模式。

(1) 数组的解构 以元素的位置为匹配条件来提取数据,可以使用空占位符跳过元素:

JavaScript

1
2
const [a, b, c] = [1, 2, 3];
const [x, , y] = [1, 2, 3]; // x=1, y=3

(2) 对象的解构 以属性的名称为匹配条件。严格以属性名作为定位依据,位置调换不影响:

1
2
3
const stu = { name: 'Bob', age: 24 };
const { name, age } = stu;
const { age, name } = stu; // 结果一样

(3) 提取高度嵌套的对象属性 通过 冒号 + {目标属性名} 的形式,一层层剥开解构:

1
2
3
4
5
6
7
8
const school = { 
classes: {
stu: { name: 'Bob', age: 24 }
}
}
// 一行代码直接提取深层级的 name
const { classes: { stu: { name } } } = school;
console.log(name) // 'Bob'

12.7 模板语法与字符串处理

(1) 模板字符串 (`) ES6 允许使用反引号包裹字符串,并通过 ${} 的方式嵌入变量。优势:

  • 保留格式:空格、缩进、换行都会被保留,非常适合书写 HTML 模板。
  • 支持表达式:可以在 ${} 里完成计算或调用函数。
1
2
3
4
5
6
7
var name = 'css', career = 'coder';
var finalString = `my name is ${name}, I work as a ${career}`;

function add(a, b) {
console.log(`${a} + ${b} = ${a+b}`);
}
add(1, 2) // '1 + 2 = 3'

(2) 字符串新增方法

  • 存在性判定(返回布尔值):
    • includes():判断字符串与子串的包含关系。
    • startsWith():判断是否以某个字符开头。
    • endsWith():判断是否以某个字符结尾。
  • 自动重复
    • repeat(n):将同一个字符串连续复制输出 n 次。