JavaScript教程

一、JS基础

1.1 引入JS的方式

  1. 写在button按钮的onclick属性里面,点击按钮,执行JS代码
1
<button onclick="console.log('点击按钮')">点击按钮</button>
  1. 写在超链接的href属性中,点击超链接,执行JS代码(不推荐使用)
1
2
3
4
<a href="javascript:alert('让你点你就点!');">你也点我一下</a>
<!-- 没有javascript限定符,点击超链接会显示一串英文 -->
<a href="javascript:;">你也点我一下</a>
<!-- 点击没有任何反应,只是用于JS处理一些功能 -->
  1. 写在script标签里面【内部方式】
1
2
3
<script>
console.log("写在script标签里面的JS代码");
</script>
  1. 写在外部的JS文件中,通过script标签引入(推荐使用)【外部方式】

​ 此时不能在标签内部写代码,即使编写,浏览器也会忽略

1
<script src="./js/script.js"></script>

1.2 注释

1.2.1 单行注释

1
//单行注释

1.2.2 多行注释

1
2
3
/*
多行注释
*/

1.3 结束符

;代表一段代码的结束,可以省略不写,使用回车替代

1.4 输入和输出

1.4.1 输出

1
2
alert()
document.wirte()

1.4.2 输入

prompt() 输入任意内容会以弹窗形式出现在浏览器中,一般提示用户输入一些内容

1
prompt()

1.5 变量

1.5.1 声明

声明(定义)变量有两部分构成:声明关键字、变量名(标识)

关键字是letvar

1.5.2 赋值

声明(定义)变量相当于创造了一个空的“容器”,通过赋值向这个容器中添加数据

1
2
let age;
age = 18;

1.5.3 关键字

以下是使用 let 时的注意事项:

  1. 允许声明和赋值同时进行
  2. 不允许重复声明
  3. 允许同时声明多个变量并赋值
  4. JavaScript 中内置的一些关键字不能被当做变量名

以下是使用 var 时的注意事项:

  1. 允许声明和赋值同时进行
  2. 允许重复声明
  3. 允许同时声明多个变量并赋值

1.5.4 变量命名规则

  1. 只能是字母、数字、下划线、$,且不能以数字开头
  2. 区分大小写
  3. JavaScript 内部已占用于单词(关键字或保留字)不允许使用
  4. 见名知意

1.6 常量

使用 const 声明的变量称为“常量”

使用场景:当某个变量永远不会改变的时候,就可以使用 const 来声明,而不是let

命名规范:和变量一致

注意: 常量不允许重新赋值,声明的时候必须赋值(初始化)

1.7 数据类型

通过typeof关键字检测数据类型

1.7.1 数值类型

JavaScript 中的数值类型与数学中的数字是一样的,分为正数、负数、小数等

1.7.2 字符串类型

通过单引号( '' ) 、双引号("")或反引号(````)包裹的数据都叫字符串

单引号和双引号没有本质上的区别,推荐使用单引号

注意事项:

  1. 无论单引号或是双引号必须成对使用
  2. 单引号/双引号可以互相嵌套,但是不以自已嵌套自已
  3. 必要时可以使用转义符\,输出单引号或双引号

1.7.3 布尔类型

表示肯定或否定时在计算机中对应的是布尔类型数据,它有两个固定的值 true 和 false , 表示肯定的数据用

true ,表示否定的数据用 false

1.7.4 undefined

未定义是比较特殊的类型,只有一个值undefined,只声明变量,不赋值的情况下,变量的默认值为undefined,

一般很少直接为某个变量赋值undefined

1.8 类型转换

1.8.1 隐式转换

某些运算符被执行时,系统内部自动将数据类型进行转换,这种转换称为隐式转换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<script>
let num = 13 // 数值
let num2 = '2' // 字符串
// 结果为 132
// 原因是将数值 num 转换成了字符串,相当于 '13'
// 然后 + 将两个字符串拼接到了一起
console.log(num + num2)
// 结果为 11
// 原因是将字符串 num2 转换成了数值,相当于 2
// 然后数值 13 减去 数值 2
console.log(num - num2)
let a = prompt('请输入一个数字')
let b = prompt('请再输入一个数字')
alert(a + b);
</script>

1.8.2 显式转换

通过Number()显式转换成数值类型,当转换失败时结果为NaN(Not a Number)

1.9 运算符

1.9.1 算术运算符

计算失败的时候,显示的结果是NaN

1.9.2 赋值运算符

1.9.3 自增/自减运算符

  • ++、– 可以在变量前面也可以在变量后面,在单独使用时并没有差别
  • ++在后(后缀式)我们会使用更多
  • 只有变量能够使用自增和自减运算符

1.9.4 比较运算符

1.9.5 逻辑运算符

1.9.6 运算符优先级

逻辑运算符优先级: !> && > ||

1.10 表达式

1.10.1 表达式和语句

表达式:可以被求值的代码,并将其计算出一个结果

语句:一段可以执行的代码,是一个行为

1.10.2 分支和语句

分支语句可以根据条件判定真假,来选择性的执行想要的代码

if分支语句 - 范围判断

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
if(条件表达式) {
// 满足条件要执行的语句
}

if (条件表达式){
// 满足条件要执行的语句
} else {
// 不满足条件要执行的语句
}

if (score >= 90) {
alert('成绩优秀,宝贝,你是我的骄傲')
} else if (score >= 70) {
alert('成绩良好,宝贝,你要加油哦~~')
} else if (score >= 60) {
alert('成绩及格,宝贝,你很危险~')
} else {
alert('成绩不及格,宝贝,我不想和你说话,我只想用鞭子和你说话~')
}

三元运算符

1
条件 ? 表达式1 : 表达式2

switch语句 - 确定值、必须是全等(数据类型一致)、注意break

1
2
3
4
5
6
7
8
9
10
11
switch (表达式) {
case1:
代码1
break
case2:
代码2
break
...
default:
代码n
}

当分支比较少时,if…else语句执行效率高

当分支比较多时,switch语句执行效率高,而且结构更清晰

内部生成一个跳表,每个常量对应一个内存地址,通过二分查找,锁定常量,跳转至指定位置

1.10.3 循环语句

while循环 - 不确定循环次数

1
2
3
while (条件表达式) {
//循环体
}
  • break 中止整个循环
  • continue 中止本次循环

无限循环

1
2
3
4
5
6
7
while(true) {

}

for(;;) {

}

for循环 - 确定循环次数

1
2
3
for(起始值; 终止条件; 变化量) {
//要重复执行的代码
}

:::success
注意: for 的语法结构更简洁,故 for 循环的使用频次会更多

:::

循环嵌套

1
2
3
4
5
for(外部声明记录循环次数的变量; 循环条件; 变化量) {
for(内部声明记录循环次数的变量; 循环条件; 变化量) {
//循环体
}
}

1.11 数组

1.11.1 数组的概念

数组:(Array)是一种可以按顺序保存数据的数据类型(属于对象)

1.11.2 数组的基本使用

定义数组和数组单元

1
2
3
4
5
6
7
<script>
// 1. 语法,使用 [] 来定义一个空数组
// 定义一个空数组,然后赋值给变量 classes
// let classes = [];
// 2. 定义非空数组
let classes = ['小明', '小刚', '小红', '小丽', '小米']
</script>

访问数组和数组索引

1
2
1. 访问数组,语法格式为:变量名[索引值]
2. 通过索引值还可以为数组单元重新赋值

**数据单元值类型 **

  • 数组做为数据的集合,它的单元值可以是任意数据类型

**数组长度属性 **

  • 数组对应着一个 length 属性,它的含义是获取数组的长度

数组map方法

1
2
3
4
5
6
7
8
9
10
<script>
const arr = ['red', 'blue', 'pink']
// 1. 数组 map方法 处理数据并且 返回一个数组
const newArr = arr.map(function (ele, index) {
// console.log(ele) // 数组元素
// console.log(index) // 索引号
return ele + '颜色'
})
console.log(newArr)
</script>

数组join方法

1
2
3
4
5
6
7
8
<script>
// 数组join方法 把数组转换为字符串
// 小括号为空则逗号分割
console.log(newArr.join()) // red颜色,blue颜色,pink颜色
// 小括号是空字符串,则元素之间没有分隔符
console.log(newArr.join('')) //red颜色blue颜色pink颜色
console.log(newArr.join('|')) //red颜色|blue颜色|pink颜色
</script>

1.11.3 操作数组

  1. push 动态向数组的尾部添加一个单元
  2. unshit 动态向数组头部添加一个单元
  3. pop 删除最后一个单元
  4. shift 删除第一个单元
  5. splice 动态删除任意单元
  • 删除数组
1
2
3
let arr = ['red', 'green', 'blue']
arr.splice(1,1) // 删除green元素
console.log(arr) // ['red, 'blue']
  • 添加元素
1
2
3
4
5
let arr = ['red', 'green', 'blue']
//arr.splice(1, 0, 'pink') // 在索引号是1的位置添加 pink
//console.log(arr) // ['red', 'pink', 'green', 'blue']
arr.splice(1, 0, 'pink', 'hotpink') // 在索引号是1的位置添加 pink hotpink
console.log(arr) // ['red', 'pink', 'hotpink', 'green', 'blue']

以上方法都是对原数组进行操作

1.12 函数

1.12.1 声明和调用

函数可以把具有相同或相似逻辑的代码“包裹”起来,通过函数调用执行这些被“包裹”的代码逻辑

这么做的优势是有利于精简代码、方便复用

声明(定义)
声明(定义)一个完整函数包括关键字、函数名、形式参数、函数体、返回值5个部分

调用

  • 声明(定义)的函数必须调用才会真正被执行,使用函数名()调用函数
  • 函数名的命名规则与变量是一致的,并且尽量保证函数名的语义

参数

  • 声明(定义)函数时的形参没有数量限制,当有多个形参时使用 , 分隔
  • 调用函数传递的实参要与形参的顺序一致
  • 形参和实参
    • 形参:声明函数时写在函数名右边小括号里的叫形参(形式上的参数)
    • 实参:调用函数时写在函数名右边小括号里的叫实参(实际上的参数)

返回值

要想获得函数内部逻辑的执行结果,需要通过 return 这个关键字,将内部执行结果传递到函数外部,这个被传递到外部的结果就是返回值

  • 在函数体中使用return 关键字能将内部的执行结果交给函数外部使用
  • 函数内部只能出现1 次 return,并且 return 下一行代码不会再被执行,所以return 后面的 数据不要换行写
  • return会立即结束当前函数
  • 函数可以没有return,这种情况默认返回值为 unde

作用域

通常来说,一段程序代码中所用到的名字并不总是有效和可用的,而限定这个名字的可用性的代码范围就是这个名字的作用域

作用域的使用提高了程序逻辑的局部性,增强了程序的可靠性,减少了名字冲突

  • 全局作用域
    • 作用于所有代码执行的环境(整个 script 标签内部)或者一个独立的 js 文件
    • 处于全局作用域内的变量,称为全局变量
  • 局部作用域
    • 作用于函数内的代码环境,就是局部作用域。 因为跟函数有关系,所以也称为函数作用域
    • 处于局部作用域内的变量称为局部变量

如果函数内部,变量没有声明,直接赋值,也当全局变量看,但是强烈不推荐

但是有一种情况,函数内部的形参可以看做是局部变量

1.12.2 匿名函数

函数可以分为具名函数和匿名函数

匿名函数:没有名字的函数,无法直接使用

函数表达式

1
2
3
4
5
6
// 声明
let fn = function() {
console.log('函数表达式')
}
// 调用
fn()

立即执行函数

1
2
(function(){ xxx })();
(function(){xxxx}());
  • 无需调用,立即执行
  • 多个立即执行函数之间用分号隔开

1.13 对象

1.13.1 语法

1
2
3
// 声明对象类型变量,使用一对花括号
// user 便是一个对象了,目前它是一个空对象
let user = {}

1.13.2 属性和访问

  1. 属性都是成对出现的,包括属性名和值,它们之间使用英文:分隔
  2. 多个属性之间使用英文 ,分隔
  3. 属性就是依附在对象上的变量
  4. 属性名可以使用""'' ,一般情况下省略,除非名称遇到特殊符号如空格、中横线等

可以使用.[]获得对象中属性对应的值,称之为属性访问

可以动态为对象添加属性,动态添加与直接定义是一样的

1.13.3 方法和调用

数据行为性的信息称为方法,其本质是函数

  1. 方法是由方法名和函数两部分构成,它们之间使用:分隔
  2. 多个方法之间使用英文,分隔
  3. 方法是依附在对象中的函数
  4. 方法名可以使用""'',一般情况下省略,除非名称遇到特殊符号如空格、中横线等
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<script>
// 方法是依附在对象上的函数
let person = {
name: '小红',
age: 18,
// 方法是由方法名和函数两部分构成,它们之间使用 : 分隔
singing: function () {
console.log('两只老虎,两只老虎,跑的快,跑的快...')
},
run: function () {
console.log('我跑的非常快...')
}
}
</script>

可以使用.[]调用对象中的函数,称之为方法调用

可以动态为对象添加方法,动态添加与直接定义是一样的

1
2
3
4
5
6
7
8
9
<script type="text/javascript">
let student = {
run: function() {
console.log('跑步中~');
}
}
student.run()
student['run']()
</script>

无论是属性或是方法,同一个对象中出现名称一样的,后面的会覆盖前面的

1.13.4 null

null 也是 JavaScript 中数据类型的一种,通常只用它来表示不存在的对象

通过typeof检测其数据类型,结果是Object

1.13.5 遍历对象

既遍历对象中的属性,也遍历对象中的方法

1
2
3
4
5
6
7
let obj = {
uname: 'pink'
}
for(let k in obj) {
// k是属性名,数据类型是字符串
// obj[k] 属性值
}

1.14 内置对象

1.14.1 Math

属性

Math.PI 获取圆周率

方法

Math.random 生成 0 到 1 间的随机数(包含 0 不包含 1)

Math.ceil 数字向上取整

Math.floor 数字向下取整

Math.round 四舍五入取整

Math.max在一组数中找出最大的

Math.min 在一组数中找出最小的

Math.pow 幂方法

Math.sqrt 平方根

二、Web API

2.1 概念

ESJS的关系

DOM(Document Object Model)是将整个 HTML 文档的每一个标签元素视为一个对象,这个 对象下包含了

许多的属性和方法,通过操作这些属性或者调用这些方法实现对 HTML 的动态 更新,为实现网页特效以及用户交

互提供技术支撑

2.1.1 DOM树

2.1.2 DOM节点

节点是文档树的组成部分,每一个节点都是一个 DOM 对象,主要分为

元素节点:HTML标签

属性节点:HTML标签中的属性

文本节点:HTML标签中的文本内容

根节点:`标签

2.1.3 Docment

document 是 JavaScript 内置的专门用于 DOM 的对象

1
2
3
4
5
6
7
8
9
10
<script>
// document 是内置的对象
// console.log(typeof document);
// 1. 通过 document 获取根节点
console.log(document.documentElement); // 对应 html 标签
// 2. 通过 document 节取 body 节点
console.log(document.body); // 对应 body 标签
// 3. 通过 document.write 方法向网页输出内容
document.write('Hello World!');
</script>

2.2 获取DOM对象

  • querySelector获取满足条件的第一个元素(写选择器)
  • querySelectorAll获取满足条件的所有元素,返回伪数组(写选择器)
  • getElementById通过id获取元素节点(直接写ID名称)
  • getElementByClassName通过class获取元素节点(直接写类名)

任意 DOM 对象都包含 nodeType 属性,用来检检测节点类型

2.3 操作元素内容

修改DOM的文本内容

innerText 将文本内容添加/更新到任意标签位置,文本中包含的标签不会被解析

innerHTML 将文本内容添加/更新到任意标签位置,文本中包含的标签会被解析

2.4 操作元素属性

2.4.1 常用属性修改

1
2
3
4
5
6
7
8
<script>
// 1. 获取 img 对应的 DOM 元素
const pic = document.querySelector('.pic')
// 2. 修改属性
pic.src = './images/lion.webp'
pic.width = 400;
pic.alt = '图片不见了...'
</script>

2.4.2 控制样式属性

  • 修改行内样式 style 属性
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>练习 - 修改样式</title>
</head>
<body>
<div class="box">随便一些文本内容</div>
<script>
// 获取 DOM 节点
const box = document.querySelector('.intro')
box.style.color = 'red'
box.style.width = '300px'
// css 属性的 - 连接符与 JavaScript 的 减运算符
// 冲突,所以要改成驼峰法
box.style.backgroundColor = 'pink'
</script>
</body>
</html>

CSS属性若含有 - ,应该用小驼峰命名法

background-color 改为 backgroundColor

  • 操作类名 className
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>练习 - 修改样式</title>
<style>
.pink {
background: pink;
color: hotpink;
}
</style>
</head>
<body>
<div class="box">随便一些文本内容</div>
<script>
// 获取 DOM 节点
const box = document.querySelector('.intro')
box.className = 'pink'
</script>
</body>
</html>

className的方式是新值换旧值

  • 通过 classList 操作类
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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
div {
width: 200px;
height: 200px;
background-color: pink;
}
.active {
width: 300px;
height: 300px;
background-color: hotpink;
margin-left: 100px;
}
</style>
</head>
<body>
<div class="one"></div>
<script>
// 1.获取元素
// let box = document.querySelector('css选择器')
let box = document.querySelector('div')
// add是个方法 添加 追加
// box.classList.add('active')
// remove() 移除 类
// box.classList.remove('one')
// 切换类
box.classList.toggle('one')
//查看是否有类
box.classList.contains('aaa')
</script>
</body>
</html>

2.4.3 操作表单元素属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<body>
<input type="text" value="请输入">
<button disabled>按钮</button>
<input type="checkbox" name="" id="" class="agree">
<script>
// 1. 获取元素
let input = document.querySelector('input')
// 2. 取值或者设置值 得到input里面的值可以用 value
// console.log(input.value)
input.value = '小米手机'
input.type = 'password'
// 2. 启用按钮
let btn = document.querySelector('button')
// disabled 不可用 = false 这样可以让按钮启用
btn.disabled = false
// 3. 勾选复选框
let checkbox = document.querySelector('.agree')
checkbox.checked = false
</script>
</body>

2.4.4 自定义属性

HTML5 专有的 data- 自定义属性

在DOM对象上一律以 dataset 对象方式获取

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div data-id="1"> 自定义属性 </div>
<script>
// 1. 获取元素
let div = document.querySelector('div')
// 2. 获取自定义属性值
console.log(div.dataset.id)
</script>
</body>
</html>

2.5 间歇函数

setInterval 是 JavaScript 中内置的函数,它的作用是间隔固定的时间自动重复执行另一个 函数,也叫定时器函数

1
2
3
4
5
6
7
8
9
<script>
// 1. 定义一个普通函数
function repeat() {
console.log('不知疲倦的执行下去....')
}
// 2. 使用 setInterval 调用 repeat 函数
// 间隔 1000 毫秒,重复调用 repeat
setInterval(repeat, 1000)
</script>

2.6 事件

事件是用来描述程序的行为或状态的,一旦行为或状态发生改变, 便立即调用一个函数

2.6.1 事件监听

addEventListener 是 DOM 对象专门用来添加事件监听的方法,它的两个参数分别为【事件类型】和【事件回调】

完成事件监听的三个步骤:

  1. 获取 DOM 元素
  2. 通过 addEventListener 方法为 DOM 节点添加事件监听
  3. 等待事件触发,如用户点击了某个按钮时便会触发 click 事件类型
  4. 事件触发后,相对应的回调函数会被执行

2.6.2 事件类型

click

dblclick

2.6.3 事件处理程序

事件处理程序决定了事件触发后应该执行的逻辑

2.7 事件类型

2.7.1 鼠标事件

鼠标事件是指跟鼠标操作相关的事件,如单击、双击、移动等

mouseenter

mouseleave

2.7.2 键盘事件

keydown

keyup

2.7.3 焦点事件

focus

blur

2.7.4 文本框输入事件

input

2.8 事件对象

任意事件类型被触发时与事件相关的信息会被以对象的形式记录下来,我们称这个对象为事件对象

事件回调函数的【第1个参数】即所谓的事件对象,通常习惯性的将这个对象命名为 event 、 ev 、 e

ev.type 当前事件的类型

ev.clientX/Y 光标相对浏览器窗口的位置

ev.offsetX/Y 光标相于当前 DOM 元素的位置

在事件回调函数内部通过 window.event 同样可以获取事件对象

2.9 环境对象

环境对象指的是函数内部特殊的变量 this ,它代表着当前函数运行时所处的环境

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<script>
// 声明函数
function sayHi() {
// this 是一个变量
console.log(this);
}
// 声明一个对象
let user = {
name: '张三',
sayHi: sayHi // 此处把 sayHi 函数,赋值给 sayHi 属性
}
let person = {
name: '李四',
sayHi: sayHi
}
// 直接调用
sayHi() // window
window.sayHi() // window
// 做为对象方法调用
user.sayHi()// user
person.sayHi()// person
</script>
  • this 本质上是一个变量,数据类型为对象

  • 函数的调用方式不同 this 变量的值也不同

  • 【谁调用 this 就是谁】是判断 this 值的粗略规则

  • 函数直接调用时实际上 window.sayHi() 所以 this 的值为 window

2.10 回调函数

将函数A作为参数传递给函数B,函数A称为回调函数

  • 回调函数本质还是函数,只不过把它当成参数使用

  • 使用匿名函数做为回调函数比较常见

2.11 事件流

事件流是对事件执行过程的描述

image-20240925201835847

任意事件被触发时总会经历两个阶段:【捕获阶段】和【冒泡阶段】

捕获阶段是【从父到子】的传导过程,冒泡阶段是【从子向父】的传导过程

2.11.1 事件和冒泡

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
<body>
<h3>事件流</h3>
<p>事件流是事件在执行时的底层机制,主要体现在父子盒子之间事件的执行上。</p>
<div class="outer">
<div class="inner">
<div class="child"></div>
</div>
</div>
<script>
// 获取嵌套的3个节点
const outer = document.querySelector('.outer');
const inner = document.querySelector('.inner');
const child = document.querySelector('.child');
// html 元素添加事件
document.documentElement.addEventListener('click', function () {
console.log('html...')
})
// body 元素添加事件
document.body.addEventListener('click', function () {
console.log('body...')
})
// 外层的盒子添加事件
outer.addEventListener('click', function () {
console.log('outer...')
})
// 中间的盒子添加事件
outer.addEventListener('click', function () {
console.log('inner...')
})
// 内层的盒子添加事件
outer.addEventListener('click', function () {
console.log('child...')
})
</script>
</body>

当某个元素的事件被触发时,事件总是会先经过其祖先才能到 达当前元素,然后再由当前元素向祖先传递,事件在流动的过程中遇到相

同的事件便会被触发。

事件的执行顺序是可控制的,即可以在捕获阶段被执行,也可以在冒泡阶段被执行

如果事件是在冒泡阶段执行的,我们称为冒泡模式,它会先执行子盒子事件再去执行父盒子 事件,默认是冒泡模式

如果事件是在捕获阶段执行的,我们称为捕获模式,它会先执行父盒子事件再去执行子盒子 事件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<body>
<h3>事件流</h3>
<p>事件流是事件在执行时的底层机制,主要体现在父子盒子之间事件的执行上。</p>
<div class="outer">
<div class="inner"></div>
</div>
<script>
// 获取嵌套的3个节点
const outer = document.querySelector('.outer')
const inner = document.querySelector('.inner')
// 外层的盒子
outer.addEventListener('click', function () {
console.log('outer...')
}, true) // true 表示在捕获阶段执行事件
// 中间的盒子
outer.addEventListener('click', function () {
console.log('inner...')
}, true)
</script>
</body>

addEventListener 第3个参数决定了事件是在捕获阶段触发还是在冒泡阶段触发

addEventListener 第3个参数为 true 表示捕获阶段触发, false 表示冒泡阶段触发, 默认值为 false

事件流只会在父子元素具有相同事件类型时才会产生影响

绝大部分场景都采用默认的冒泡模式(其中一个原因是早期 IE 不支持捕获)

2.11.2 阻止冒泡

阻止冒泡是指阻断事件的流动,保证事件只在当前元素被执行,而不再去影响到其对应的祖先元素

事件对象中的 ev.stopPropagation 方法,专门用来阻止事件冒泡

鼠标经过事件:

mouseover 和 mouseout 会有冒泡效果

mouseenter 和 mouseleave 没有冒泡效果 (推荐)

2.12 事件委托

1
2
3
4
5
6
7
8
9
10
<script>
// 假设页面中有 10000 个 button 元素
const buttons = document.querySelectorAll('table button');
for(let i = 0; i <= buttons.length; i++) {
// 为 10000 个 button 元素添加了事件
buttons.addEventListener('click', function () {
// 省略具体执行逻辑...
})
}
</script>
1
2
3
4
5
6
7
8
9
<script>
// 假设页面中有 10000 个 button 元素
let buttons = document.querySelectorAll('table button');
// 假设上述的 10000 个 buttom 元素共同的祖先元素是 table
let parents = document.querySelector('table');
parents.addEventListener('click', function () {
console.log('点击任意子元素都会触发事件...');
})
</script>

事件对象中的属性 target 或 srcElement 属性表示真正触发事件的元素,它是一个元素类型的节点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<script>
// 假设页面中有 10000 个 button 元素
const buttons = document.querySelectorAll('table button')
// 假设上述的 10000 个 buttom 元素共同的祖先元素是 table
const parents = document.querySelector('table')
parents.addEventListener('click', function (ev) {
// console.log(ev.target);
// 只有 button 元素才会真正去执行逻辑
if(ev.target.tagName === 'BUTTON') {
// 执行的逻辑
}
})
</script>

2.13 其他事件

2.13.1 页面加载事件

加载外部资源(如图片、外联CSS和JavaScript等)加载完毕时触发的事件

1
2
3
window.addEventListener('load', function() {
// xxxxx
})

2.13.2 元素滚动事件

滚动条在滚动的时候持续触发的事件

1
2
3
window.addEventListener('scroll', function() {
// xxxxx
})

2.13.3 页面尺寸事件

会在窗口尺寸改变的时候触发事件

1
2
3
window.addEventListener('resize', function() {
// xxxxx
})

2.14 元素尺寸与位置

获取元素自身的宽高

offsetWidth

offsetHeight

获取元素的位置

getBoundingClientRect()

**rect.left**:元素的左边距,表示元素左侧到视口左侧的距离

**rect.top**:元素的上边距,表示元素顶部到视口顶部的距离

**rect.right**:元素的右边距,表示元素右侧到视口左侧的距离

**rect.bottom**:元素的下边距,表示元素底部到视口顶部的距离

**rect.width**:元素的宽度

**rect.height**:元素的高度

2.15 日期对象

2.15.1 实例化

1
const date = new Date('2024-09-26')

2.15.2 方法

getFullYear 获取四位年份

getMonth 获取月份,取值为 0 ~ 11

getDate 获取月份中的每一天,不同月份取值也不相同

getDay 获取星期,取值为 0 ~ 6

getHours 获取小时,取值为 0 ~ 23

getMinutes 获取分钟,取值为 0 ~ 59

getSeconds 获取秒,取值为 0 ~ 59

2.15.3 时间戳

时间戳是指1970年01月01日00时00分00秒起至现在的总秒数或毫秒数,它是一种特殊的计量 时间的方式

1
2
3
4
5
6
7
8
// 1. 实例化
const date = new Date()
// 2. 获取时间戳
console.log(date.getTime())
// 还有一种获取时间戳的方法
console.log(+new Date())
// 还有一种获取时间戳的方法
console.log(Date.now())

2.16 DOM节点

2.16.1 插入节点

createElement 动态创建任意 DOM 节点

cloneNode 复制现有的 DOM 节点,传入参数 true 会复制所有子节点

appendChild 在末尾(结束标签前)插入节点

insertBefore 在父节点中任意子节点之前插入新节点

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
<body>
<h3>插入节点</h3>
<p>在现有 dom 结构基础上插入新的元素节点</p>
<hr>
<!-- 普通盒子 -->
<div class="box"></div>
<!-- 点击按钮向 box 盒子插入节点 -->
<button class="btn">插入节点</button>
<script>
// 点击按钮,在网页中插入节点
const btn = document.querySelector('.btn')
btn.addEventListener('click', function () {
// 1. 获得一个 DOM 元素节点
const p = document.createElement('p')
p.innerText = '创建的新的p标签'
p.className = 'info'
// 复制原有的 DOM 节点
const p2 = document.querySelector('p').cloneNode(true)
p2.style.color = 'red'
// 2. 插入盒子 box 盒子
document.querySelector('.box').appendChild(p)
document.querySelector('.box').appendChild(p2)
})
</script>
</body>

image-20240926084654608

2.16.2 删除节点

删除现有的 DOM 节点,也需要关注两个因素:首先由父节点删除子节点,其次是要删除哪个子节点

removeChild 删除节点时一定是由父子关系

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<body>
<!-- 点击按钮删除节点 -->
<button>删除节点</button>
<ul>
<li>HTML</li>
<li>CSS</li>
<li>Web APIs</li>
<script>
const btn = document.querySelector('button')
btn.addEventListener('click', function () {
// 获取 ul 父节点
let ul = document.querySelector('ul')
// 待删除的子节点
let lis = document.querySelectorAll('li')
// 删除节点
ul.removeChild(lis[0])
})
</script>
</body>

2.16.3 查找节点

  • 父子关系

    childNodes 获取全部的子节点,回车换行会被认为是空白文本节点

    children 只获取元素类型节点

    parentNode 获取父节点,以相对位置查找节点

  • 兄弟关系

previousSibling 获取前一个节点,以相对位置查找节点,实际应用中非常灵活

nextSibling 获取后一个节点,以相对位置查找节点,实际应用中非常灵活

2.17 JS组成

image-20240926102416061

ECMAScript: 规定了js基础语法核心知识

Web APIs :

DOM 文档对象模型, 定义了一套操作HTML文档的API

BOM 浏览器对象模型,定义了一套操作浏览器窗口的API

2.18 window对象

  • BOM (Browser Object Model ) 是浏览器对象模型

    • window对象是一个全局对象,也可以说是JavaScript中的顶级对象

    • 像document、alert()、console.log()这些都是window的属性,基本BOM的属性和方法都是 window的

    • 所有通过var定义在全局作用域中的变量、函数都会变成window对象的属性和方法

    • window对象下的属性和方法调用的时候可以省略window

image-20240926103043593

2.19 定时器

JavaScript 内置的一个用来让代码延迟执行的函数,叫 setTimeout

1
setTimeout(回调函数, 延迟时间)

延迟函数仅执行一次

延时函数需要等待,所以后面的代码先执行

返回值是一个正整数,表示定时器的编号

清除延迟函数

1
clearTimeout(timerId)

2.20 location对象

location (地址) 它拆分并保存了 URL 地址的各个组成部分, 它是一个对象

image-20240926103517141

2.21 navigator对象

navigator是对象,该对象下记录了浏览器自身的相关信息

通过 userAgent 检测浏览器的版本及平台

1
2
3
4
5
6
7
8
9
10
// 检测 userAgent(浏览器信息)
(function () {
const userAgent = navigator.userAgent
// 验证是否为Android或iPhone
const android = userAgent.match(/(Android);?[\s\/]+([\d.]+)?/)
const iphone = userAgent.match(/(iPhone\sOS)\s([\d_]+)/)
// 如果是Android或iPhone,则跳转至移动站点
if (android || iphone) {
location.href = 'http://m.itcast.cn'
}})();

2.22 history对象

history (历史)是对象,主要管理历史记录, 该对象与浏览器地址栏的操作相对应,如前进、 后退等

image-20240926103751723

2.23 本地存储

本地存储:将数据存储在本地浏览器中

优点:

1、页面刷新或者关闭不丢失数据,实现数据持久化

2、容量较大,sessionStorage和 localStorage 约 5M 左右

2.23.1 localStorage

数据可以长期保留在本地浏览器中,刷新页面和关闭页面,数据也不会丢失

以键值对的形式存储,并且存储的是字符串, 省略了window

image-20240926104153833

2.23.2 sessionStorage

当页面浏览器被关闭时,存储在 sessionStorage 的数据会被清除

1
2
3
存储:sessionStorage.setItem(key,value)
获取:sessionStorage.getItem(key)
删除:sessionStorage.removeItem(key)

2.23.3 localStorage存储复杂数据类型

将复杂类型转换为JSON字符串

JSON.stringify(复杂数据类型)

JSON字符串:

​ 首先是1个字符串

​ 属性名使用双引号引起来,不能单引号

​ 属性值如果是字符串型也必须双引号

JSON.parse(JSON字符串)

2.24 正则表达式

正则表达式(Regular Expression)是一种字符串匹配的模式(规则)

2.24.1 正则基本使用

定义规则

1
const reg = /表达式/
  • 其中 / / 是字面量
  • 正则表达式也是对象

使用正则

1
2
3
4
5
6
7
8
9
<script>
// 正则表达式的基本使用
const str = 'web前端开发'
// 1. 定义规则
const reg = /web/
// 2. 使用正则 test()
console.log(reg.test(str)) // true 如果符合规则匹配上则返回true
console.log(reg.test('java开发')) // false 如果不符合规则匹配上则返回 false
</script>

2.24.2 元字符

元字符:是一些具有特殊含义的字符,可以极大提高了灵活性和强大的匹配功能

  • 边界符

​ 正则表达式中的边界符(位置符)用来提示字符所处的位置,主要有两个字符

image-20240926105645148

​ 如果 ^ 和 $ 在一起,表示必须是精确匹配

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<script>
// 元字符之边界符
// 1. 匹配开头的位置 ^
const reg = /^web/
console.log(reg.test('web前端')) // true
console.log(reg.test('前端web')) // false
console.log(reg.test('前端web学习')) // false
console.log(reg.test('we')) // false
// 2. 匹配结束的位置 $
const reg1 = /web$/
console.log(reg1.test('web前端')) // false
console.log(reg1.test('前端web')) // true
console.log(reg1.test('前端web学习')) // false
console.log(reg1.test('we')) // false
// 3. 精确匹配 ^ $
const reg2 = /^web$/
console.log(reg2.test('web前端')) // false
console.log(reg2.test('前端web')) // false
console.log(reg2.test('前端web学习')) // false
console.log(reg2.test('we')) // false
console.log(reg2.test('web')) // true
console.log(reg2.test('webweb')) // flase
</script>
  • 量词

​ 量词用来设定某个模式重复次数

​ 逗号左右两侧千万不要出现空格

image-20240926105803531

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
<script>
// 元字符之量词
// 1. * 重复次数 >= 0 次
const reg1 = /^w*$/
console.log(reg1.test('')) // true
console.log(reg1.test('w')) // true
console.log(reg1.test('ww')) // true
console.log('-----------------------')

// 2. + 重复次数 >= 1 次
const reg2 = /^w+$/
console.log(reg2.test('')) // false
console.log(reg2.test('w')) // true
console.log(reg2.test('ww')) // true
console.log('-----------------------')

// 3. ? 重复次数 0 || 1
const reg3 = /^w?$/
console.log(reg3.test('')) // true
console.log(reg3.test('w')) // true
console.log(reg3.test('ww')) // false
console.log('-----------------------')

// 4. {n} 重复 n 次
const reg4 = /^w{3}$/
console.log(reg4.test('')) // false
console.log(reg4.test('w')) // flase
console.log(reg4.test('ww')) // false
console.log(reg4.test('www')) // true
console.log(reg4.test('wwww')) // false
console.log('-----------------------')

// 5. {n,} 重复次数 >= n
const reg5 = /^w{2,}$/
console.log(reg5.test('')) // false
console.log(reg5.test('w')) // false
console.log(reg5.test('ww')) // true
console.log(reg5.test('www')) // true
console.log('-----------------------')

// 6. {n,m} n =< 重复次数 <= m
const reg6 = /^w{2,4}$/
console.log(reg6.test('w')) // false
console.log(reg6.test('ww')) // true
console.log(reg6.test('www')) // true
console.log(reg6.test('wwww')) // true
console.log(reg6.test('wwwww')) // false

// 7. 注意事项: 逗号两侧千万不要加空格否则会匹配失败
</script>
  • 范围

​ 表示字符的范围,定义的规则限定在某个范围,比如只能是英文字母,或者数字等等,用表示范围

image-20240926110035740
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
<script>
// 元字符之范围 []
// 1. [abc] 匹配包含的单个字符, 多选1
const reg1 = /^[abc]$/
console.log(reg1.test('a')) // true
console.log(reg1.test('b')) // true
console.log(reg1.test('c')) // true
console.log(reg1.test('d')) // false
console.log(reg1.test('ab')) // false

// 2. [a-z] 连字符 单个
const reg2 = /^[a-z]$/
console.log(reg2.test('a')) // true
console.log(reg2.test('p')) // true
console.log(reg2.test('0')) // false
console.log(reg2.test('A')) // false

// 想要包含小写字母,大写字母 ,数字
const reg3 = /^[a-zA-Z0-9]$/
console.log(reg3.test('B')) // true
console.log(reg3.test('b')) // true
console.log(reg3.test(9)) // true
console.log(reg3.test(',')) // flase

// 用户名可以输入英文字母,数字,可以加下划线,要求 6~16位
const reg4 = /^[a-zA-Z0-9_]{6,16}$/
console.log(reg4.test('abcd1')) // false
console.log(reg4.test('abcd12')) // true
console.log(reg4.test('ABcd12')) // true
console.log(reg4.test('ABcd12_')) // true

// 3. [^a-z] 取反符
const reg5 = /^[^a-z]$/
console.log(reg5.test('a')) // false
console.log(reg5.test('A')) // true
console.log(reg5.test(8)) // true
</script>
  • 字符类

​ 某些常见模式的简写方式,区分字母和数字

image-20240926110404204

2.25 替换和修饰符

replace 替换方法,可以完成字符的替换

1
字符串.replace(/正则表达式/,'替换的文本')

修饰符约束正则执行的某些细节行为,如是否区分大小写、是否支持多行匹配等

  • i 是单词 ignore 的缩写,正则匹配时字母不区分大小写 、
  • g 是单词 global 的缩写,匹配所有满足正则表达式的结果
1
2
3
4
5
6
7
8
9
10
<script>
// 替换和修饰符
const str = '欢迎大家学习前端,相信大家一定能学好前端,都成为前端大神'
// 1. 替换 replace 需求:把前端替换为 web
// 1.1 replace 返回值是替换完毕的字符串
// const strEnd = str.replace(/前端/, 'web') 只能替换一个
// 2. 修饰符 g 全部替换
const strEnd = str.replace(/前端/g, 'web')
console.log(strEnd)
</script>

2.26 正则插件

image-20240926111239876

2.27 change事件

给input注册 change 事件,值被修改并且失去焦点后触发

  • JavaScript诞生于1995年,主要用于处理网页中的前端验证,即检查用户输入的内容是否符合一定的规则
  • 由Netscape 和 SUN公司研发
  • ECMAScript是JS的标准,简称ES
  • 一个完整的JavaScript组成:

  • JS特点
    • 解释性语言
    • 动态语言
    • 基于原型的面向对象

二、JS的HelloWorld

1
2
3
4
5
<script type="text/javascript">
alert("helloworld")
document.write("你好~")
console.log("这是一行代码");
</script>

三、JS编写位置

  1. 写在button按钮的onclick属性里面,点击按钮,执行JS代码
1
<button onclick="console.log('点击按钮')">点击按钮</button>
  1. 写在超链接的href属性中,点击超链接,执行JS代码(不推荐使用)
1
2
3
4
<a href="javascript:alert('让你点你就点!');">你也点我一下</a>
<!-- 没有javascript限定符,点击超链接会显示一串英文 -->
<a href="javascript:;">你也点我一下</a>
<!-- 点击没有任何反应,只是用于JS处理一些功能 -->
  1. 写在script标签里面
1
2
3
<script>
console.log("写在script标签里面的JS代码");
</script>
  1. 写在外部的JS文件中,通过script标签引入(推荐使用)

此时不能在标签内部写代码,即使编写,浏览器也会忽略

1
<script src="./js/script.js"></script>

ES6+

DOM树一共有12种节点类型,常用的有4种:

1、Document类型(document节点)——DOM的“入口点”

2、Element节点(元素节点)——HTML标签,树构建块

3、Text类型(文本节点)——包含文本

4、Comment类型(注释节点)——有时我们可以将一些信息放入其中,它不会显示,但JS可以从DOM中读取它。

JS Tips

DocumentFragment加快DOM渲染速度

DocumentFragment

概念:文档片段接口,表示一个没有父对象的最小文档对象

它被作为一个轻量版的 Document 使用,就像标准的 document 一样,存储由节点(nodes)组成的文档结

构。与 document 相比,最大的区别是它不是真实 DOM 树的一部分,它的变化不会触发 DOM 树的重新渲

染,且不会对性能产生影响。

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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<ul id="list">
<li>第一项</li>
<li>第二项</li>
<li>第三项</li>
</ul>
<script>
console.time('执行一万次需要用的时间')
let list = document.getElementById('list');
for(let i = 0; i < 10000; i++) {
let tempLi = document.createElement('li');
tempLi.innerHTML = i;
list.append(tempLi);
}
//执行一万次需要用的时间: 12.816162109375 ms
console.timeEnd('执行一万次需要用的时间')
</script>
</body>
</html>
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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<ul id="list">
<li>第一项</li>
<li>第二项</li>
<li>第三项</li>
</ul>
<script>
let list = document.getElementById('list');
let df = document.createDocumentFragment();
for(let i = 0; i < 10000; i++) {
let tempLi = document.createElement('li');
tempLi.innerHTML = i;
df.append(tempLi);
}
console.time('执行一万次需要用的时间')
list.append(df);
//执行一万次需要用的时间: 2.6708984375 ms
console.timeEnd('执行一万次需要用的时间')
</script>
</body>
</html>

JS计算程序运行时间

方法一

1
2
3
4
5
console.time('执行一万次需要用的时间')
for(let i=0;i<10000;i++){

}
console.timeEnd('执行一万次需要用的时间')//执行一万次需要用的时间: 0.523193359375 ms

JSON和XML之间的区别:

1、JSON是JavaScript Object Notation;XML是可扩展标记语言。

2、JSON是基于JavaScript语言;XML源自SGML。

3、JSON是一种表示对象的方式;XML是一种标记语言,使用标记结构来表示数据项。

4、JSON不提供对命名空间的任何支持;XML支持名称空间。

5、JSON支持数组;XML不支持数组。

6、XML的文件相对难以阅读和解释;与XML相比,JSON的文件非常易于阅读。

7、JSON不使用结束标记;XML有开始和结束标签。

8、JSON的安全性较低;XML比JSON更安全。

9、JSON不支持注释;XML支持注释。

10、JSON仅支持UTF-8编码;XML支持各种编码。

Domtree的根节点是document

https://www.cnblogs.com/cangqinglang/p/8963557.html

如果文档包含框架( 或


JavaScript教程
http://example.com/2024/09/25/前端入门/JavaScript教程/
作者
王承磊
发布于
2024年9月25日
许可协议