Skip to content

JAVASCRIPT

JS的应用:

编写在网页中,操作网页的元素,实现用户的各种交互效果

编写服务器端代码

编写移动APP应用、命令行…

JS的历史

1995年 JS最早出现在网景浏览器中

1996年 IE也出了一个JS克隆版

1997年 制定了JS的标准规范 ECMAScript->ES6

2009年,JS开始向后端发展,出现了Node.js

JS的学习步骤

  • 了解背景知识(什么时候出现 发展历史 背景 现状)

  • 搭建开发环境

  • 基础语法(变量 常量 数据类型 逻辑运算 运算符。。。)

  • 函数和对象

  • 第三方框架和库(VUE REACT)

  • 完成自己的个人项目

JS入门

1JS的运行方式

(1) 内部写法
<script>JS代码<script>
(2) 外部写法
先创建01.js外部文件,然后引入:
<script src=“01.js”> 这里不能再写其他的代码 </script>

2JS的语法规范

  • 代码严格区分大小写

  • 使用到的所有符号都是英文符号

  • 推荐将js代码写到body中的最后面

  • 每行代码后的分号可以省略不写

JS基础语法

1注释

注释:用于解释代码的内容

被注释掉的内容不会被执行的,可以提高代码的可阅读性和后续的可维护性。

单行注释,格式: // 快捷键 Ctrl+/

多行注释,格式:/* */ 快捷键 Ctrl+Shift+/

2变量

变量的概念:

用于存储数据的容器

var(variable变量) 用于声明变量,格式:

声明变量var age

声明变量并且赋值 var age = 18

声明单个变量 var name = ‘tom’

声明多个变量 var a=11,b=22

使用关键字var来声明变量,会在内存中开辟空间,把值存入

变量的赋值:

  • 变量可以声明时同时赋值,也可以先声明再赋值,变量需要声明后才可以使用

  • 变量声明了但没赋值,打印出来的是undefined(未定义)空值

  • 变量允许多次赋值,后面的值会把前面的值覆盖掉,重新赋值不需要再写var

  • 变量的多次赋值可以赋不同类型的值(这是弱类型语言的特点)

变量的命名规则

  • 变量名称可以由字母、数字、下划线、美元符号构成

  • 变量名称不可以使用数字开头

  • 变量名称不可以是关键字,比如false、var、undefined

  • 变量名称不推荐使用汉字

  • 变量名称要做到见名知意,如:user_name

多个单词的命名方式:

CSS类名: background-color 中划线命名法

文件名:UserName 大驼峰命名法

变量名: userName 小驼峰命名法

数据库表字段: user_name 下划线命名法

常量

\1. 和变量一样,都是用于存储数据的容器

\2. 常量的值不允许被修改

\3. 常量声明的时候必须赋值,否则报错

数据类型

数据类型分为原始类型引用类型

原始类型分为:数值型、字符串型、布尔型、未定义型、空

1数值型

整数类型

整数类型就是我们所说的整数 0 1 2 …

数值有多种进制的表示方式 二进制0b 八进制0 十六进制0X

浮点型

浮点型就是我们所说的小数

浮点型可以使用科学计数法的方式来表示

//1.数值型number
//1.1 整型
var n1 = 10 //十进制
//2进制:用0b开头的数字
var n2 = 0b1010
//8进制:用0开头的数字
var n3 = 012
//16进制:用0x开头的数字
var n4 = 0xA
console.log(n1,n2,n3,n4)
			
//1.2浮点型
var f1 = 3140
var f2 = 3.14E+3
var f3 = 31.4e2
var f4 = 31400e-1
console.log(f1,f2,f3,f4)

2字符串型

被引号所包裹的值,引号不区分单双引号,JS中推荐使用单引号

*检测数据类型* typeof 值

'number'数值型/'string'字符串型/'boolean'布尔型/'undefined'未定义型/'object'空

查看任意一个字符的编码

'a'.charCodeAt()

3布尔型

只有两个值,分别是true和false,表示真和假,用于保存只有两个值的数据。

如:是否登录、是否会员、是否购买过...

4未定义型

只有一个值是undefined,表示一个空值,例如:声明变量未赋值就是undefined

5空

只有一个值,null,常和引用类型的数据一起使用

数据类型的转换

数据类型转换分为隐式转换和强制转换

1隐式转换

在运算过程中,自动产生的转换

数字+字符串 数字转为字符串如: 2+‘3’//23
数字+布尔 布尔转为数字如:3-false //3
字符串+布尔 布尔转字符串如: ‘2’+true //2true
JS中加号(+)的作用数字之间的加法运算 字符串之间的拼接

2强制转换

强制转换需要用到函数

函数:是一个功能体,需要我们提供若干数据,它会根据预设好的逻辑执行并返回结果

(1) 强制转换为数字 Number()

Number('3') //3
Number(true) //1
Number(false) //0
Number(null) //0
Number('3a') //NaN
Number(undefined) //NaN

**NaN:**Not a Number 不是一个数字

是指Number()在将一个值转为数字的过程中,没有成功得到数字结果,那么就是NaN

NaN与任何数字执行数学运算的结果还是NaN

(2) 强制转换为整型parseInt()

用于将小数或者字符串转为整型,其他类型的转换结果为NaN

parseInt(3.6)//3
parseInt('6a')//6
parseInt('a6')//NaN
parseInt(null)//NaN

(3) 强制转换为浮点型 parseFloat()

用于将字符串转为浮点型,其他类型结果为NaN

parseFloat('75')//75
parseFloat('3.14a')//3.14
parseFloat('a3.14')//NaN

运算符

运算符是用于连接表达式中的操作数并对操作数执行运算的符号

例如:表达式num1+num2,其中操作数是num1和num2,运算符是“+”

常见的运算符有以下几类:

算术运算符、比较运算符、逻辑运算符、赋值运算符、三目运算符、位运算符

1算术运算符

+ - * / 普通的四则运算符
% 取余数
自增自减运算符:++ 自增 变量的值自动加1--自减 变量的值自动减1口诀: 符号在前,先变化,再使用;符号在后,先使用,再变化变化指的是++或者--使用指的是可以打印这个变量的值,或者将这个变量的值赋值给其他变量…

2比较运算符

< > <= >=== 等于,比较两个值是否相同=== 全等于 不仅比较两个值是否相同,还会比较类型是否相同!= 不等于,比较两个值是否不相同!== 不全等于,比较的是两个值不相同或者类型不相同
(1) 数字与字符串比较,字符串转为数字如:3>‘7’ //false
(2) 字符串之间的比较,比较的是字符的编码如: ‘3’>‘10’//true 字符3的编码51 字符10的编码49
(3) NaN和任何值比较,结果都是false如: 3>'10a' 3<'10a' 3=='10a' NaN==NaN

3逻辑运算符

&& 逻辑与
只有关联的左右两个条件的结果都为true,最终结果才为true,否则false
|| 逻辑或
只有关联的左右两个条件的结果都为false,最终结果才为false,否则为true
**!**逻辑非
取反 !true=>false !false=>false
短路逻辑:
&& 当第一个条件的结果为false时,最终结果就是false,不再执行第二个条件
|| 当一个条件的结果为true时,最终结果就是true,不再执行第二个条件

4赋值运算符

= 赋值 等号右边给左边

运算赋值:先执行运算再执行赋值

+= *= -= /= %=….

如: a -=5 就等价于 a = a-5

5三目(元)运算符

一目运算符:连接了一个操作数的运算符 比如:a++ a-- !

二目运算符:连接了两个操作数的运算符 + &&

三目运算符:由两个运算符连接了三个表达式

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

当条件表达式的结果为true时,执行表达式1

当条件表达式的结果为false时,执行表达式2

口诀:1?2:3 1真取2 1假取3

操作网页元素的步骤

1查找网页元素

给标签设置id属性,一个网页中的id值不允许重复

<button id="btn">按钮</button>

2给按钮绑定事件,监听用户操作

btn.onclick=function(){   点击事件
  一旦监听到用户的操作,要执行的内容
}

3弹出警示框

alert() 可以把要显示的内容写到小括号里

4修改标签之间的内容(修改HTML)

<span id="sum"></span>
sum.innerHTML=值

5修改标签的CSS样式

<div id="sum"></div>
sum.style.样式名称=值
例如:sum.style.display='none'
      sum.style.fontSize='20px'  
属性名如果是多个单词组成,采用驼峰命名法

流程控制

我们需要控制程序的执行方式

分支结构

1if语句

if(条件表达式){
    如果条件表达式的结果为true,执行此处代码,否则跳过
}

注意:以下值作为条件表达式会转为false: 0 NaN undefined null ‘’

2if-else语句

if(条件表达式){
   语句块1
}else{
   语句块2
}

3if-else嵌套

if(条件表达式1){
   语句块1
}else if(条件表达式2){
   语句块2
}else{
   语句块n+1 //以上条件都不满足执行此处代码
}

4switch-case语句

是一种特殊的多项分支语句,条件只能进行全等于的比较

switch(表达式){
	case 值1:
		语句块1
		break //用于中断当前选项,防止穿透现象
	case 值n:
		语句块n
		break //用于中断当前选项,防止穿透现象
	default://如果前面的case都不满足,执行语句块n+1
		语句块n+1
}

执行顺序:

\1. 会拿着表达式的值依次与case后的值做全等比较,如果相等,就会执行case后的语句块

\2. 如果没有遇到break,会依次穿透所有case,包括default

\3. 如果遇到了break,就会结束当前case,不会继续向后执行了

\4. case的个数、break的个数、default是否要加都是可选项,根据具体业务决定

如果前面的所有case都没有匹配上,就会执行default后的语句

循环结构

1while循环

while(循环条件){
   只要循环条件的结果为true,就一直执行循环
}
  • 当循环条件直接写true时,表明这是一个死循环

  • 注意:死循环必须要设置结束条件,我们可以通过break关键字强制结束当前循环

2do-while循环

do{
   循环体
}while(循环条件)

\1. do-while循环会直接执行第一轮循环,不做任何判断

\2. 是否要执行第二轮循环,取决于是否满足循环条件

\3 while和do-while的循环使用上区别不大,do-while常用于需要先执行一次的情况

3for循环

for(开始条件;循环条件;增量){
   循环体
}

\1. for循环也属于先判断再执行的循环

\2. for循环的开始条件可以一次声明多个变量

\3 循环的循环条件如果有多个的话,是最后一个起作用

4break和continue

\1. break和continue都是关键字

\2. break用于强制结束当前循环(后面所有轮数均不执行)

\3. break还可以用在switch-case中,结束当前case,防止穿透现象

\4 continue在循环中用于跳过代码,continue后的代码在本轮不执行,直接开启下一轮循环

5循环的嵌套

for (var i = 1; i <= 3; i++) { //外层循环
	for (var j = 1; j <= 5; j++) { //内层循环
}
}

\1. 循环嵌套就是在一个循环中嵌套了另外一个循环

\2. 外层循环与内层循环的循环变量应该不同,防止互相影响,推荐使用i j

\3. 外层循环执行1次,内层循环执行多次

\4 对于图形而言,外层循环控制的是图形的行数,内层循环控制的是这一行的列数

函数【重点】

Number() parseInt() parseFloat()….

函数: 就是一个功能的集合,我们可以给它提供数据,它根据预先设计好的逻辑执行,给我们结果

**函数分为:**系统函数 和 自定义函数

1创建自定义函数

function 函数名(){
   函数体(这个函数的具体功能)
}

1. 自定义函数封装了我们预设好的一段功能,可以使用多次,这样会提高程序的复用性

2. 函数必须要调用才会执行,调用几次,执行几次

3. 调用函数的格式: 函数名称()

2创建带有参数的函数

function 函数名(形参){
   函数体(这个函数的具体功能)
}

调用函数的格式: 函数名称(实参)

\1. 形参:定义函数时,参数列表中出现的名字,这个参数的名字可以更换

\2. 实参:调用函数时,在参数列表中传入的实际的值,这个值会影响函数的执行结果

\3. 实参的值会赋值给形参,两者的个数可以不匹配

\4. 如果实参的个数小于形参的个数,未被赋值的形参变量值为undefined

\5. 如果实参的个数大于形参的个数,多余的实参会被舍弃

3创建带有返回值的函数

function 函数名(参数列表){
   函数体(这个函数的具体功能)
   return 值 //返回值,外部调用本函数后可以拿到的结果
}

调用函数的格式:**函数名称(参数) ** //如果函数有返回值,可以拿到返回值

\1. return用于向外部传递函数调用后的结果

\2. return后不允许写代码,因为不会执行,一遇到return函数就结束了

\3. return后如果没有值,外部接收到的值是unefined

4return和break

return用在函数中,结束函数的调用

break用在循环与switch-case中,用于结束循环或者防止穿透

变量的作用域

全局作用域: 函数以外的作用域,有且只有一个全局作用域

函数作用域:函数以内的作用域,可能有多个函数作用域

全局变量:全局作用域下声明的变量,可以在任意作用域下访问到

局部变量:函数作用域下声明的变量,只能在所在的函数作用域访问到

在函数内,不加var声明的变量是全局变量

注意:这只是一种特殊现象,我们定义全局变量时还是要按规范来写

JS的预加载/预解析:

在程序执行前,会将var声明的变量提升到所在作用域的最前面

但注意只是提升声明部分,赋值不提升

其他规则:

\1. 变量的查找原则:就近原则,当前作用域下由就不再往全局作用域下查找

\2. 形参属于是局部变量,只能在函数内访问

函数作用域

函数提升:在程序执行前,会将函数整体提升到所在作用域的最前面
局部函数: 在函数作用域下创建的函数,只能在当前的函数作用域下调用
作用域链: 是作用域之间嵌套形成的作用域结构
在查找变量的时候,会先在当前作用域下查找,如果没有会沿着作用域链一级级向上找
函数提升:在程序执行前,会将函数整体提升到所在作用域的最前面

匿名函数

没有名字的函数

1创建函数

函数声明:--创建有名字的函数
function fn(){
   函数体
}
函数表达式:--创建匿名函数
var fun = function(){
  函数体
}
把匿名函数赋值给一个变量,变量名称就代表函数的名称

2函数名称 和 函数名称()

函数名称: 本质上是一个变量,变量中保存了一个函数类型的值fn

函数名称():表示在调用函数,要执行函数体中的代码fn()

3对比函数声明与函数表达式的区别

函数声明:存在函数的整体提升,可以先写调用,再写创建

函数表达式:只有函数声明提升,必须先写创建再写调用

4函数名与变量名相同时的提升情况

正常情况下,普通函数整体先提升,变量声明再提升

函数名称与变量名称相同时,当函数名称先提升后,变量声明就不再提升了

5全局污染与解决方案

**全局污染:**我们在代码中可能会出现多个同名的变量,范围控制不好可能会互相影响“污染”

全局污染的解决方案:匿名函数自调用
;(function() {
匿名函数函数体			
})()
; 在匿名函数自调用前加分号结束上一行,防止与上一行代码连接到一起执行
() 前面的这个小括号把匿名函数整体包裹起来,表示这是一个整体
() 后面的这个小括号表示要调用当前的这个匿名函数

\1. 普通函数也可以自调用--少用

\2. 自调用这个区域相当于把内部的函数变成了局部函数

回调函数

函数可以传哪些参数:

Fn(77) 函数的实参可以是数值类型

Fn(‘王小明’) 函数的实参可以是字符串类型

Fn(false) 函数的实参可以是布尔类型

前面我们讲过,函数也是一种类型function,函数也可以作为参数被传递到另一个函数中

函数的实参可以是函数类型,分为两种:

Fn(函数名称) ->普通函数需要传自己的函数名即可,注意:没有小括号

Fn(匿名函数)->匿名函数由于没有名字,需要传自己的全部内容

回调函数:

就是把一个函数类型的值作为参数传递,被传递的这个函数被称为回调函数

回调函数会自动调用

回调函数的格式:
function xinRun(madai){
	madai()//调用传进来的函数
}
function liangRun(){
	console.log('亮哥开始跑')
	console.log('亮哥跑完了')
}
xinRun(liangRun)//把普通函数作为回调函数
xinRun(function(){匿名函数的函数体})//把匿名函数作为回调函数

获取函数的执行结果,有两种方式:

\1. 通过return返回结果

\2. 通过回调函数,将值传给回调函数的形参带出

function fn(cb) {
	console.log('fn()开始了')
	var a = 666
	cb(a) //调用回调函数,并将变量a赋值给回调函数的形参r带出
	console.log('fn()结束了')
}
//fn()表示调用fn函数
//将下面的匿名函数作为回调函数传给fn()
fn(function(r) {//调用fn函数,并将匿名函数作为参数传入,匿名函数现在就是回调函数
	console.log(r)
})

递归recursion

递归指的是函数自己调用自己

递归本身是一个死循环,如果要使用递归解决问题,切记要设置边界条件及时退出

递归的要点:总结规律+边界条件

边界条件通常结合return一起使用

斐波那契数列: 1 1 2 3 5 8 13 21 34 55 89…

我们可以使用递归求出第n项数字是几

规律:前两项数字为1,后面每一项的值都为前两项数字之和

补充:系统函数isNaN()

/用于检测数据中是否含有非数字,有返回true,没有返回false

console.log(isNaN(666))//false
console.log(isNaN(true))//false->1
console.log(isNaN(false))//false->0
console.log(isNaN(null))//false->0
console.log(isNaN(undefined))//true
console.log(isNaN(NaN))//true
console.log(isNaN('1a'))//true

对象

对象:是一组属性和方法的集合

一个杯子:属性有颜色、容量、大小、形状... 方法有装水、发泄...

一部手机:属性有颜色、品牌、型号、内存、CPU、屏幕尺寸... 方法有打电话、发短信、看视频、聊天、办公、娱乐...

万物皆对象

1.JS中的对象

  • 自定义对象:用户自己创建的对象
  • 内置对象:JS提供的对象
  • 宿主对象:根据不同的执行环境来划分

​ 前端(浏览器宿主): dom

​ 后端(Node.js宿主): mysql

2.自定义对象

(1)对象字面量

js
var cup = { '属性名': 属性值, 属性名: 属性值 }

属性名的引号可以省略,如果属性名中含有特殊字符必须添加引号。

练习:创建一个商品对象,包含的属性有编号、名称、价格、图片

js
var product = {
    id: 1,
    title: '戴尔灵越',
    price: 5499,
    image: 'img/1.jpg'
}

练习:创建一个员工对象,包含的属性有编号、姓名、性别、生日、工资、部门编号

js
var emp = {
    eid: 2,
    ename: 'xin',
    sex: 0,
    birthday: '1981-8-30',
    salary: 50000,
    deptid: 20
}

(2)内置构造函数

js
// 创建一个空对象,需要手动添加每个属性
var student = new Object()   // {}

练习:创建一个手机对象,包含的属性有品牌、颜色、屏幕尺寸、内存大小。

js
var phone = new Object()
phone.brand = '华为'
phone.color = '黑色'
phone.size = 6.71
phone.memory = '8G'
console.log(phone)

3.属性的访问

对象.属性名

对象['属性名']

如果属性不存在,获取的值为undefined

练习:创建一本图书的对象,包含的属性有编号、书名、作者、价格;打印输出书名属性,修改图书的价格属性,添加图书的出版社属性。

js
var book = {
    id: 'NS228909113',
    title: '三国演义',
    author: '罗贯中',
    price: 59.9
}
console.log(book.title)
book.price = 49.9
book['publish'] = '人民邮电出版社'
console.log(book)

4.遍历(枚举)属性

采用循环方式,依次访问对象中的每个属性

js
for(var k in 对象){
    // k 代表每个属性名
    // 对象[k]  属性名对应的属性值
}

练习:创建一个对象,包含一组工资的值;计算出工资总和、平均工资

js
var salary = {
    a: 8000,
    b: 12000,
    c: 5000,
    d: 10000
}
// 声明变量,用于保存总和
var sum = 0
// 声明变量,用于保存工资的数量
var count = 0
// 获取到每个工资的值
for(var k in salary) {
    // salary[k] 每个工资的值
    // console.log(k,salary[k])
    // 把每个工资的值加到sum
    sum += salary[k]
    // 数量加1
    count++
}
console.log(sum,count,sum/count)

5.检测属性是否存在

js
对象.属性名  === undefined   // true->不存在    false->存在
对象.hasOwnProperty('属性名')   //true->存在   false->不存在
'属性名' in 对象   //true->存在   false->不存在

练习:创建一个商品对象,包含的属性有编号、标题、价格;如果颜色属性不存在,则添加该属性;如果价格属性存在,要求打8折。

js
var product = {
    id: 1,
    title: '小米13',
    price: 4199,
    // color: '白色'
}
// 如果颜色属性不存在
// product.color === undefined
// !product.hasOwnProperty('color')
if( !('color' in product) ) {
	product.color = '黑色'
}
// 如果价格属性存在
if( product.hasOwnProperty('color') ) {
	product.price *= 0.8
}

模板字符串

简化了字符串的拼接

js
`模板字符串 ${JS表达式}`

对象中的方法

本质上就是一个函数

js
var phone = {
    // 方法
    call: function() {
       // this 指向调用方法的对象
    }
}
// 调用
phone.call()

练习:创建一个圆对象,包含的属性有半径和圆周率,添加方法计算出圆的面积,添加方法计算出圆的周长;最后调用两个方法

js
var circle = {
    radius: 4,
    pi: 3.14,
    area: function() {
        console.log( this.pi * this.radius * this.radius )
    },
    len: function() {
        console.log( 2 * this.pi * this.radius )
    }
}
// console.log( circle )
circle.area()
circle.len()

练习:创建一个计算器对象,添加两个方法;第一个方法传递任意两个数字返回总和,结果渲染到span标签之间;第二个方法传递任意两个数字返回相减结果,最后渲染到span标签之间。

js
var calc = {
    plus: function(a,b) {
        // a,b接收传递进来的两个数字
        // 先把接收到两个值转为数字
        // 求和后,把结果渲染到cont标签之间
        cont.innerHTML = Number(a) + Number(b)
    },
    subtract: function(a,b) {
        // 将两个值相减,结果渲染到cont之间
        cont.innerHTML = a - b
    }
}
// 给按钮绑定点击事件
btn1.onclick = function(){
    // 获取两个值
    calc.plus(num1.value, num2.value)
}
btn2.onclick = function() {
    calc.subtract(num1.value, num2.value)
}

数组

数组是一组数据的集合,每一个数据称为元素

1.创建数组

(1)数组字面量

js
var arr = [ 元素1, 元素2, 元素3,... ]

练习:创建数组,包含有一组工资的值; 创建函数,包含一组城市名称

(2)内置构造函数

js
var arr1 = new Array(元素1, 元素2, 元素3,..)
var arr2 = new Array(3)  //初始化长度,后期需要手动添加每一个元素,可以添加更多个元素

练习:创建数组,包含多个国家

练习:创建数组,初始化长度为4,添加4门课程

2.访问数组元素

下标(索引):JS为每个元素自动添加的编号,是从0开始的一个整数

语法格式 数组名称[下标]

3.数组的长度

数组.length 获取数组元素个数

在数组的末尾添加元素 数组[ 数组.length ] = 值

练习:创建一个空数组,使用数组长度往数组中添加若干个汽车品牌。

4.数组分类

**索引数组:**以>=0的整数作为下标,可以通过length获取长度。

**关联数组:**以字符串作为下标,需要单独添加元素,不能使用length获取长度。

练习:创建对象,包含一个人的多门成绩

练习:创建数组,包含一个班所有人的成绩。

对比数组和对象的区别

两者都可以存储一组数据,对象是通过属性,数组是通过元素;

对象中这一组是无序的,不分先后顺序; 数组中这一组是有序的,可以进行排序。

练习:创建数组,包含一组新闻;每一条新闻包含的属性有标题、发表时间

js
var news = [
    {
        title: '盘点!美国操纵网络霸权的四大罪状',
        ctime: '2022-11-18'
    },
    {
        title: '周鸿祎:微信已经从精神上把运营商干掉了',
        ctime: '2022-09-30'
    }
]
// console.log(news)
console.log(news[0].title, news[0].ctime)

5.遍历数组元素

通过循环依次访问数组中每个元素

js
// for-in
for(var k in 数组){
    //k  下标
    //数组[k]  下标对应元素
}
//for循环(推荐方式)
for(var i = 0;i < 数组.length;i++) {
    //i 下标
    //数组[i]   下标对应元素
}

练习:创建数组,包含一组成绩,计算出平均成绩。

js
var arr = [82, 93, 74, 63]
for(var i = 0, sum = 0; i < arr.length; i++) {
    // console.log(i, arr[i])
    // 把每个成绩加到sum
    sum += arr[i]
}
console.log( sum, sum/arr.length )

练习:创建数组,包含一组工资,遍历数组,统计出工资在5000~8000的百分比; 38.5%

js
var arr = [3500, 8000, 12000, 7500, 9000, 6500,4000]
// 声明变量,用于统计数量
for(var i = 0, count = 0; i < arr.length; i++) {
    if(arr[i] >= 5000 && arr[i] <= 8000) {
        // console.log(arr[i])
        // 数量加1
        count++
    }
}
console.log(count/arr.length * 100 + '%')

数组

1.API

concat(arr2,arr3...) 拼接多个数组,返回拼接后的结果

如何学习一个API:作用、参数、返回值

数组API还需要查看原数组是否会发生变化

reverse() 翻转数组元素,返回翻转后的数组,原数组会发生变化

slice(start, [end]) 截取数组元素,start开始的下标,end结束的下标,不包含end本身;end为空会截取到最后;如果下标为负数表示倒数;返回截取的数组

练习:创建数组,包含a~h,每个字母是一个元素,分别截取出bc,f;再将两部分拼接成一个新数组。

splice(start, [count], [v1],...) 删除数组元素,start开始的下标,count删除的长度;count为空会删除到最后;下标为负数表示倒数;v1,..表示删除后要补充的元素;返回删除的元素,原数组会发生变化。

练习:创建数组,包含a~h,每个字母是一个元素,删除cd,替换h为m,在下标为1的位置插入z

sort() 对数组元素进行排序,默认是按照首个字母的Unicode码排列

js
arr.sort( function(a, b) {
    return a - b  //按照数字从小到大排列
    // return b - a  //按照数字从大到小排列
} )

push() 在数组的末尾添加元素,返回数组的长度,原数组会发生变化

pop() 删除数组末尾的一个元素,返回删除的元素,原数组会发生变化

unshift() 在数组的开头添加元素,返回数组的长度,原数组会发生变化

shift() 删除数组开头的一个元素,返回删除的元素,原数组会发生变化

indexOf() 检测数组中是否含有某个元素,存在返回下标,不存在返回-1

练习:创建数组,包含爱国福、和谐福;如果不含有敬业福,则把敬业福添加到数组。

其它API: https://www.w3school.com.cn/js/js_array_methods.asp

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array

2.二维数组

数组中的元素还是数组,用于对数据进行二次分类

[ [元素1, 元素2], [ 元素3, 元素4 ],... ]

字符串对象

包装对象:目的是为了让原始类型的值像对象一样具有属性和方法;JS中提供了三种包装对象,分别是字符串对象、数字对象、布尔对象

js
new String()   // 将一个字符串包装为对象
String()   // 将一个值强制转换为字符串

1.转义字符 —— \

改变字符本身的意义

js
\'   // 将一个具有特殊意义的引号转义为普通引
\n   // 将字符n转义为换行符
\t   // 将字符t转义为制表符,一组连续空格

练习:打印输出以下字符串

​ C:\User\Teacher

2.API

length 获取字符串的长度

charAt() 获取下标对应的字符,也可以使用数组的格式 字符串[下标]

练习:遍历字符串javascript,获取每一个字符,统计出字符a出现的次数。

​ 面试题:统计出一个字符串中哪一个字符出现的次数最多,一共几次

indexOf() 检测字符串中是否含有某个字符串,返回的是找到的第1个的下标,找不到返回-1

lastIndexOf() 检测字符串中是否含有某个字符串,返回的是找到的最后1个的下标,找不到返回-1

练习:声明变量保存用户输入的邮箱,如果邮箱中不含有@,打印'非法的邮箱'

slice(start, [end]) 截取字符串,start开始的下标,end结束的下标,不包含end这一项;end为空会截取到最后;下标为负数表示倒数

练习:截取出一个邮箱的用户名和域名

toUpperCase() 将英文字母转大写

toLowerCase() 将英文字母转小写

split() 将一个字符串按照指定的字符转为数组

所有的字符串API都返回新的字符串,不会影响到原来的字符串

Math对象

提供了一组数学相关的API;是一个特殊的对象,不需要使用new来创建就可以直接调用API

PI 获取圆周率

abs() 计算绝对值

ceil() 向上取整

floor() 向下取整

round() 四舍五入取整

pow(x,y) 计算x的y次方

max() 获取一组数字最大值

min() 获取一组数字最小值

random() 获取随机数,范围0~1之间 >=0 <1

Date对象

用于项目中日期时间的存储和计算

创建Date对象

js
new Date('2023/2/27 10:40:30')
new Date(2023,1,27,10,40,30)  //第2个参数月份的值是0~11对应1~12月
new Date()   //存储的是当前操作系统的时间
new Date(1680000000000)   //存储的是距离计算机元年(1970-1-1 0:0:0)的毫秒数,会产生一个具体的日期时间    1秒 = 1000毫秒

Date对象转为本地字符串

js
toLocaleString()  //存在兼容性问题,不同的JS解释器下显示有差别,只能用于开发调试阶段

获取Date对象存储的值

js
getFullYear()  //年
getMonth()   //月份,值是0~11,用的时候需要加1
getDate()  //日期
getHours()   //小时
getMinutes()  //分钟
getSeconds()  //秒钟
getMilliseconds()  //毫秒
getDay()   //星期,值是0~6,对应的是日~六
getTime()   //获取时间戳

Date.now()   //获取当前时间的时间戳

练习:创建Date对象,保存当前系统的日期时间,最后打印以下格式

'今天是xxxx年xx月xx日 xx时xx分xx秒 星期x'

js
var d = new Date()
var year = d.getFullYear()
var month = d.getMonth() + 1
var date = d.getDate()
var hour = d.getHours()
var minute = d.getMinutes()
var second = d.getSeconds()
var day = d.getDay()  //范围0~6
// 如果秒钟小于10,在前拼接0,否则正常显示
second = second<10 ? '0'+second : second
// 创建数组,包含日~六,下标范围0~6
var arr = ['日', '一', '二', '三', '四', '五', '六']
// console.log(arr[day])
console.log(`今天是${year}年${month}月${date}日  ${hour}时${minute}分${second}秒  星期${arr[day]}`)

练习:计算出当前时间距离周末(2023/3/4)还有x天x时x分x秒

js
// 创建对象,保存当前时间
var d1 = new Date()
// 获取当前时间的时间戳
// console.log(Date.now(),d1.getTime())
// 创建对象,保存周末时间
var d2 = new Date('2023/3/4')
// 计算相差的毫秒数
var d = d2.getTime() - d1.getTime()
// 把相差的单位改为秒
d = parseInt(d/1000)
// 计算相差的秒钟部分:去除含有的分钟(60秒),总的相差的秒钟%60
var second = d%60
// 计算相差的分钟部分:去除总的秒数中含有的小时(3600秒),单位要求是分钟
var minute = parseInt(d%3600/60) 
// 计算相差的小时部分:去除总的秒数中含有的天(24*3600),单位要求是小时
var hour = parseInt(d%(24*3600)/3600) 
// 计算相差的天部分:直接将总的秒数单位转为天(24*3600)
var day = parseInt(d/(24*3600)) 
console.log(`距离周末还有${day}天${hour}时${minute}分${second}秒`)

设置存储的值

js
setFullYear()  //年
setMonth()   //月份,值是0~11
setDate()  //日期
setHours()   //小时
setMinutes()  //分钟
setSeconds()  //秒钟
setMilliseconds()  //毫秒
setTime()   //设置时间戳,会产生一个具体的日期时间

拷贝一个Date对象

js
var d = new Date('2023/2/21 14:42:50')
// 拷贝一份Date对象,把一个对象作为参数传递
var d2 = new Date(d)

练习:创建Date对象,保存员工的入职时间2023/2/28,假设合同期为3年,计算出合同的到期时间;合同到期前一个月进行续签,计算出续签时间

最后打印出以下格式

入职时间:xxxx-xx-xx..

到期时间:xxxx-xx-xx..

续签时间:xxxx-xx-xx..

二、数字对象

js
new Number()   //将一个数字包装为对象,存储在了堆内存中
Number()   //将一个值转换为数字
toFixed(n)   //保留小数点后n位

三、布尔对象

js
new Boolean()   //将一个布尔值包装为对象
Boolean()   //将一个值转换为布尔型
!!//将一个值隐式转换为布尔型

四、错误处理

常见的错误

  • 语法错误(SyntaxError):代码书写不符合语法规范,例如缺少半块括号、使用中文符号...
  • 引用错误(ReferenceError):使用了未声明的变量
  • 类型错误(TypeError):当前调用的不是一个函数类型
  • 范围错误(RangeError):当前使用的值超出了JS的规定范围
  • 自定义错误:用户自己指定的错误,用于调试
js
throw 错误内容

错误处理

js
try{
    // 尝试执行,可能会出现错误
}catch(err){
    // 一旦try中出现错误,就会捕获到错误,把错误信息放入到err
    // 不会阻止后续代码执行
}

一、ES6

let声明的变量解决提升的问题,不允许重复声明

1. 块级作用域

大括号之间的语句块就是块级作用域,块级作用域下,let和const声明的变量和常量,只能在当前作用域访问到。

块级作用域:if、else、for... 所有大括号之间都是

js
// 块级作用域
{
    var a = 1  //全局变量
    let b = 2  //局部变量
    const c = 3  //局部常量
}

面试题:var、let、const的区别

var声明的变量存在声明提升,允许重复声明;

let声明的变量解决了声明提升问题,不允许重复声明;

const声明的是常量,声明后必须赋值,不允许重新赋值;

var声明变量只有在函数作用域下是局部变量

let和const存在块级作用域,在块级作用域是局部变量和常量

2. 参数增强

可以给参数设置默认值

js
function add(a,b,c=0) {
    //ES5默认值
    b = b || 0
}
add(5000)

3. 箭头函数

js
 () => {  }

简化了匿名函数的写法

js
sort( (a, b)=>{
    return a-b
} )

如果箭头函数的函数体中只有一行代码,并且是return形式可以进一步简化

js
sort( (a,b) => a-b )

二、Node.js概述

Node.js是一种运行在服务器端的JS环境

Node.js对比JS

  • JS运行在客户端浏览器,有多种解释器,存在代码兼容性问题;Node.js运行在服务器端,只有一种解释器,不存在代码兼容性问题

  • 两者都有相同的内置对象和自定义对象,有各自的宿主对象

  • JS用于操作网页元素,实现用户交互;Node.js用于服务器端开发,例如操作数据库、调用其它的服务器...

    官网:www.nodejs.org

    国内镜像:www.nodejs.cn

Node.js运行方式

  1. 脚本模式
bash
node  拖拽产生文件的路径
  1. 交互模式
bash
node       //回车 进入到交互模式
ctrl+d   或者   按两次  ctrl+c    //退出交互模式

Node.js特点

单线程运行逻辑

三、全局对象

global对象

可以查看一个变量或者函数是否为全局的

在交互模式下,属于全局作用域,全局变量和全局函数可以通过global访问

js
var a = 1  //全局变量
function fn(){   //全局函数
	return 2
}
global.a
global.fn()

在脚本文件下,属于模块作用域,变量和函数都是局部的

JS下全局对象global名称为:window

console对象

用于输出到控制台

js
console.log(1)  //输出日志
console.info(2)  //输出消息
console.warn(3)  //输出警告
console.error(4)  //输出错误
console.time()  //开始计时
console.timeEnd()  //结束计时

注意事项:开始计时和结束计时的参数要保持一致

process对象

进程:计算上任何的软件运行都会占用一定得资源

js
process.arch    //查看当前服务器的CPU架构
process.platform   //查看服务器的操作系统
process.pid   //查看当前进程的编号
process.kill()   //结束指定编号的进行

Buffer对象

缓冲区,是内存中一块区域,用于临时存储数据

js
// 从内存分配空间作为缓冲区,存储一个值,单位是字节,一个汉字占3个字节
var buf = Buffer.alloc(6, 'abc新') // <Buffer 61 62 63 e6 96 b0>
buf.toString()  // 将Buffer转为字符串

四、模块

每一个JS文件就是模块,模块内部封装了一组功能,会形成一个独立的作用域,变量和函数都不能被外部直接访问。

使用模块化来管理项目,便于维护和代码的复用

require: 是一个函数,用来引入其它的模块,得到暴露的对象

module.exports: 是模块暴露的对象,默认是一个空对象,如果想让外部使用哪个功能,就需要添加到这个对象

局部变量

__dirname 获取当前模块的绝对路径

__filename 获取当前模块的绝对路径+模块名称

模块分类

模块分为自定义模块、核心模块、第三方模块

以路径开头不以路径开头
文件模块require('./circle.js') 用于引入自定义模块
目录模块require('./02_2') 首先会到目录中查找说明文件package.json中main属性对应的文件,如果文件找不到会自动寻找index.jsrequire('04_2') 首先会到当前目录下的node_modules目录中寻找;如果找不到会不断的往上一级的node_modules目中寻找;用于引入第三方模块

练习:在03_main.js中引入同一级的目录模块03_2,目录中包含文件index.js,模块中创建函数,传递任意3个数字返回最大值,将该函数暴露出去;最终在主模块中调用暴露的函数。

二、包和NPM

CommonJS:是一种模块化规范,Node.js就是采用了这种模块化规范,require、module.exports都是基于CommonJS模块规范。

:package,就是第三方模块,以目录模块的形式出现

NPM:是管理包的工具模块,例如:下载安装、上传、更新、卸载... NPM在Node.js安装的过程已经附带安装

bash
npm  -v

准备工作:确保命令行指向到要下载的目录

NPM网址:www.npmjs.com

NPM命令

bash
npm init -y

初始化生成一个项目描述文件,名称为package.json;可以用来记录下载安装的包

bash
npm install 模块名称

下载安装指定的模块,会将所有的包放入到node_modules目录,在package.json中记录下载的当前这个包;会生成package-lock.json,会记录下载安装的所有的包(包括依赖的包)

bash
npm install

自动下载安装package.json记录的包

设置中国镜像: npm set registry https://registry.npmmirror.com

查看当前镜像: npm get registry

其它npm命令:https://www.npmjs.cn/

三、URL

URL:统一资源定位器,互联网上的任何资源都有对应的URL,用来标识资源,例如:网页、图片、视频、声音...

http
https://www.jd.com/product.html?a=1&b=2

​ 协议 域名 资源的路径 查询参数

**协议:**用来传输资源

**域名(主机):**用来查找服务器

**资源的路径:**表示要请求的服务器端文件路径

**查询参数:**客户端向服务器端所传递的值,例如:传递搜索的关键、用户名、密码; 格式为 参数名1=参数值1&参数名2=参数值2

js
new URL()    // 将一个URL转为对象,可以获取各个部分
searchParams   //获取对象中查询参数部分
get('参数名')    //获取查询参数中参数名对应的参数值
js
// 将URL转为对象
var obj = new URL(str)
// 获取查询参数
var qs = obj.searchParams
console.log(`
	班级名称:WEB${qs.get('cid')}   课程名称:${qs.get('cname')}
`)

四、定时器

一次性定时器

js
// 开启
var timer = setTimeout(回调函数, 间隔时间) //当间隔时间到了,会调用一次回调函数
// 清除
clearTimeout(timer)

周期性定时器

js
//开启
var timer = setInterval(回调函数, 间隔时间) //当间隔时间到了,会调用一次回调函数
//清除
clearInterval(timer)

立即执行定时器

js
setImmediate(回调函数)/clearImmediate()
process.nextTick(回调函数)  // 没有清除的方法

定时器执行逻辑:所有定时器中的回调函数都是放在主线程后的事件(任务)队列中执行,不会阻止主线程中后续代码执行

一、文件系统模块

属于是核心模块

文件分为目录形式和文件形式

查看文件状态

js
var s = fs.statSync(文件的路径)
s.isFile()  //是否为文件
s.isDirectory()   //是否为目录

fs.stat(文件路径, 回调函数)

同步方法和异步方法

同步方法:在主线程中执行,会阻止主线程后续代码的执行;通过返回值获取结果

异步方法:在一个独立的线程执行,不会阻止主线程后续代码的执行;通过回调函数获取结果

清空写入文件

js
writeFileSync(文件的路径, 写入的值) 
writeFile(文件的路径, 写入的值, 回调函数)

如果文件不存在,会先创建文件然后写入值

如果文件已经存在,会清空文件中的内容然后写入值

追加写入文件

js
appendFileSync(文件的路径, 写入的值)
appendFile(文件的路径, 写入的值, 回调函数)

如果文件不存在,会先创建文件然后写入值

如果文件已经存在,会在文件的末尾追加写入值

读取文件

js
readFileSync(文件的路径)
readFile(文件的路径, 回调函数)

读取结果的格式为Buffer

删除文件

unlinkSync(文件的路径)
unlink(文件的路径, 回调函数)

检测文件(目录)是否存在

existsSync(文件的路径)

练习:如果文件3.txt存在,则删除文件。

练习:如果文件4.txt存在,则读取文件

文件流

createReadStream() 创建可读取的文件流

createWriteStream() 创建可写入的文件流

pipe() 管道

js
// 引入文件系统模块
const fs = require('fs')
// 以流的方式读取文件
var rs = fs.createReadStream('banner.jpg')
// 创建写入的流,可以流的方式写入文件
var ws = fs.createWriteStream('1.jpg')
// 将读取的流添加到写入的流
rs.pipe(ws)

Node.js事件监听

on('事件名称', 回调函数) 一旦监听到事件后,会调用回调函数,事件名称是固定的字符串

js
// 绑定事件,监听是否有数据流入到内存
rs.on('data', (c) => {
	// c 表示每次读取的数据
	console.log(c)
})

二、HTTP协议

HTTP:超文本传输协议,是客户端和WEB服务器之间的通信协议;

WEB服务器:为客户端提供资源的服务器

1.通用头信息

**请求的URL:**要请求的服务器端资源

**请求的方式:**对资源的操作方式 例如:GET / POST

响应的状态码:

​ 1**: 接收到部分请求,还在继续...

​ 2**: 成功的响应

​ 3**: 响应的重定向,会请求另一个资源

​ 4**: 客户端错误

​ 5**: 服务器端错误

2.响应头信息

服务器做出的是响应

Content-Type:设置响应的内容类型,解决中文乱码 text/html;charset=utf-8

Location:设置要跳转的URL,通常配合状态码302使用

3.请求头信息

客户端发出的是请求

三、HTTP模块

是Node.js提供的一个核心模块,可以用来创建WEB服务器

createServer() 创建WEB服务器

listen() 设置端口

res 响应对象

res.statusCode 设置响应的状态码

res.setHeader() 设置响应的头信息

res.write() 设置响应到客户端的内容

res.end() 结束响应

js
// 引入http模块
const http = require('http')
// 创建WEB服务器
const app = http.createServer()
// 给服务器设置端口
app.listen(3000, () => {
	console.log('服务器启动成功')
})
// 域名、IP地址
// http://127.0.0.1:3000
// http://localhost:3000

// 给服务器绑定事件,监听客户端的请求
app.on('request', (req, res) => {
	// console.log('有一个请求进来')
	// req 请求的对象
	// res 响应的对象
	// 设置响应头信息中,内容类型
	res.setHeader('Content-Type','text/html;charset=utf-8')
	// 设置响应的内容
	res.write('WEB前端2212')
	// 结束响应
	res.end()
})

一、http模块

 req 请求对象

 req.url     获取请求的资源, 格式为   /xxxxx

 req.method    获取请求的方式

 练习:编写文件02_https.js,使用http模块创建WEB服务器,设置端口;监听客户端的请求,根据请求的URL做出不同的响应

​    '/login'    响应     '<h2>登录成功</h2>'

​    '/index.html'      响应     index.html文件

​    '/study'       跳转到       

​    其它           响应          404    Not  Found

框架:是一整套解决方案,简化了已有的功能,增加新的功能,便于开发维护,专门用于项目开发。

二、express框架

基于Node.js平台,快速、开放、极简的WEB开发框架。

属于是第三方模块,需要先去下载安装

bash
npm install express

网址:www.expressjs.com.cn

创建WEB服务器

js
const express = requrie('express') 
//创建WEB服务器
const app = express()
//设置端口
app.listen(3000)

路由

用来处理特定的请求,就是设置客户端请求和服务器端响应之间的对应关系

路由包含请求的URL、请求的方式、回调函数

js
app.请求的方式('请求的URL', 回调函数)

res 响应对象

res.send() 设置响应的内容并结束

res.redirect() 设置响应的重定向,会跳转到另一个URL

res.sendFile() 设置响应的文件,文件必须使用绝对路径 __dirname

req 请求对象

req.url 获取请求的URL

req.method 获取请求的方式

req.query 获取请求的URL中的查询参数,格式为对象

一、路由器

将多个路由写入到一个独立的模块中,便于管理路由 ——— 模块化开发

路由器模块

js
//1.引入express
//2.创建路由器对象
const router = express.Router()
//3.往路由器对象添加路由
//4.暴露路由器对象
module.exports = router

WEB服务器

js
//1.引入路由器模块
//2.挂载路由器,路由器模块下的路由成为WEB服务器的一部分
// 添加的前缀可以给所有的路由的URL添加前缀
app.use(要添加的前缀, 引入的路由器)

二、中间件(插件)

用于拦截对服务器端的请求

express下中间件分为 应用级中间件、路由级中间件、内置中间件、第三方中间件、错误处理中间件

1.应用级中间件

本质上是一个函数,一旦拦截后会调用这个函数

js
function fn(req, res, next) {
    next()  //往后执行,可能是下一个中间件,或者路由
}
app.use('/list', fn)  //拦截特定的请求
app.use( fn )  //拦截所有的请求

2.路由级中间件

就是路由器的使用,在拦截到URL以后,会到指定的路由器下寻找路由

js
app.use(拦截的URL, 路由器)

3.内置中间件

express提供的中间件就是内置中间件,可以直接使用

(1) 将POST传参转为对象

js
app.use( express.urlencoded({
	extended: true
}) )

(2) 托管静态资源

​ 动态资源:通常指要请求的数据库的数据,伴随着传递参数不同,获取的资源也就不同;例如:传递关键字'手机'和'笔记本'获取的结果是不一样的。

​ 静态资源:通常指要请求的静态文件,例如:HTML、CSS、图像、视频、声音... 这个过程中没有传参传递

​ 托管静态资源:客户端要请求静态资源,不需要通过路由去响应文件,而是让客户端自己去到指定的目录查找文件。

js
app.use( express.static('目录的路径') )

4.第三方中间件

属于第三方模块形式,使用之前需要先去下载安装

5.错误处理中间件

用于后期项目中

一、使用express项目生成器搭建项目(脚手架)

文档:https://www.expressjs.com.cn/starter/generator.html

生成项目

bash
npx express-generator

npx会临时安装一个包,使用完会自动卸载

bash
npm install

会安装package.json中记录的依赖包

bash
npm start

启动项目,运行的是package.json中scripts下start对应的命令

或者 node bin/www

启动后服务器占用的端口是3000

二、pm2

是Node.js的进程管理器,可以简化node任务管理

1.特征:

自动重新启动

后台运行

服务信息查看

最大内存重启....

2.安装

pm2属于Node.js下的第三方模块

全局安装pm2

bash
npm install -g pm2

查看是否安装了pm2

bash
pm2 -v

使用pm2启动一个node项目

--watch 监听程序源文件的编号,一旦发生变化会自动重启(热启动)

bash
pm2 start 项目的启动文件  --name 自定义名称  --watch

查看当前node进程列表

bash
pm2 list

停止一个node进程

bash
pm2 stop [进程名] | [进程id]

重启进程

bash
pm2 restart [进程名] | [进程id]

删除进程

bash
pm2 delete [进程名] | [进程id]

查看日志

bash
pm2 logs       所有日志
pm2 logs  [进程名] | [进程id]

AJAX(阿贾克斯)

Asynchronous Javascript And XML(异步JavaScript和XML)

不用整个刷新页面,而是局部刷新

在页面加载后,向服务器端请求并结束数据,在后台向服务器发请求

好处:用于体验好

XML :是一种数据格式,采用了自定义的标签

[
    {
        title: '标题1',
        ctime: '2023-3-10'
    },
    {
        title: '标题2',
        ctime: '2023-4-10'
    }
]
xml
<list>
	<news>
        <title>标题1</title>
        <ctime>2023-3-10</ctime>
    </news>
    <news>
        <title>标题2</title>
        <ctime>2023-4-10</ctime>
    </news>
</list>

一、跨域

跨域:Ajax中请求,协议、域名、端口不同,都会产生跨域。

解决跨域:

在接口端使用中间件模块

1.下载安装cors模块

npm install cors

2.使用中间件

js
const cors = require('cors')
//在WEB服务器创建后
app.use( cors() )

二、AJAX请求步骤

1.创建http请求对象

js
var xhr = new XMLHttpRequest()

2.打开服务器的连接,设置请求的接口

js
xhr.open(请求方式, 接口地址, 是否为异步)
请求方式:标准的写法为大写

3.发送请求

js
xhr.send()

4.绑定事件,监听服务器端响应

js
xhr.onload = function() {
	xhr.responseText //获取服务器端响应的文本
}

三、post传参的内容类型


| Content-Type                      | 请求体中的格式   | 用途            |
| --------------------------------- | ---------------- | --------------- |
| application/x-www-form-urlencoded | a=1&b=2          | 传递多个参数    |
| application/json                  | {"a": 1, "b": 2} | 传递多个参数    |
| application/form-data             |                  | 上传文件        |
| text/xml                          |                  | 传递xml格式参数 |

AJAX中POST传参

js
xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded')
xhr.send('a=1&b=2')
js
xhr.setRequestHeader('Content-Type','application/json')
xhr.send('{"a": 1, "b": 2}')

express中将post传参格式为json数据转为对象

js
//使用中间件将post传参json转为对象
app.use( express.json() )
// 路由获取
req.body   //{a:1, b:2}

JSON

JSON:JS对象表示法,是一种数据交互的文本形式,不是一种编程语言。

JSON由键值对组成,键名称必须用双引号包裹,值可以是字符串、数字、布尔型、数组、对象、null

json
{
    "a": 1,
    "b": "hello",
    "c": true,
    "color": ["红色","绿色","蓝色"],
    "p": {"name": "xin", "age": 18}
}

JSON对象: 提供JS和JSON相互转换的API

JSON.parse() 将JSON转为JS(反序列化)

JSON.stringify() 将JS转为JSON(序列化)

var baseURL = 'http://127.0.0.1:3000'
function get(obj) {
    // obj.type  接口请求方式
    // obj.url   接口地址
    // obj.data  接口传递的参数,对象格式,需要转换为查询参数格式  a=1&b=2
    // obj.success  函数,成功以后会调用
    // 将传递的对象转换为查询参数
    var arr = []
    for(var k in obj.data) {
        arr.push(k + '=' + obj.data[k])
    }
    // 数组转字符串,用&分割
    var str = arr.join('&')
    // 1.创建HTTP请求对象
    var xhr = new XMLHttpRequest()
    // 2.打开服务器连接,设置请求的接口
    xhr.open(obj.type, baseURL + obj.url + '?' + str)
    // 3.发送请求
    xhr.send()
    // 4.绑定事件,监听服务器端响应
    xhr.onload = function() {
        // 将响应的结果JSON转为JS,然后再传递到外部 
        obj.success(JSON.parse(xhr.responseText))
    }
}

一.正则表达式

专门描述字符串中字符出现规则的表达式

1.字符集

[] 可选范围

2.预定义字符集

对极其常用的字符集进行了简化

\d 表示匹配[0-9]

\w 表示匹配[a-zA-Z0-9_]

\s 表示匹配空白字符,例如:空格、制表符、回车、换行

. 表示匹配任意个字符

3.量词

规定一个字符集连续出现的次数

{n} 必须是n个

{n,m} 至少n个,最多m个

{n,} 至少是n个,多了不限

+ 至少1个,多了不限

? 可有无可,最多1个

* 可有可无,多了不限

4.匹配特殊位置

^ 开始的位置

$ 结束的位置

如果同时使用以上两个可以实现从头到尾的完整匹配

全局作用域

html
<!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>作用域相关 09:22</title>
</head>

<body>
  <!-- 
    什么是作用域?
    答: 代码运行时的生效范围.   有作用的领域

    有哪些作用域?
    - 全局作用域: 这里的代码在哪里都能用, 公共的
    - 局部作用域: 函数运行时产生的, 函数私有的
    - 块级作用域: ES6提供的
   -->

  <script src="./01.a.js"></script>
  <script src="./01.b.js"></script>
  <script>
    // 直接在脚本中书写的代码, 都属于全局作用域
    // 全局中 声明的变量 存储在 顶级对象 -- window
    // window是浏览器提供的, 存储了 浏览器提供的所有 API 
    // JS在浏览器中运行, 就可以操作 window 对象中提供的功能

    // 宿主环境: JS代码运行时所在的平台
    // 1. node.js  -- 2阶段
    // 2. 浏览器  -- 3阶段 - 研究运行在浏览器上的JS 有什么特点.

    var a = 10

    function b() { }

    // clg : 快速生成打印的脚本代码
    console.log(window)
    // 运行方式:  右键当前页面内容 -> open with live server
    // 会自动在 默认浏览器 开启当前页面, 自带热更新功能

    // alert: 浏览器提供的 弹窗功能 函数
    // window.alert("Hello Window!")

    // 使用全局对象 window 中的属性时, 可以省略 window 前缀
    alert('Hello Window!')

    // 全局(对象)污染:
    // 全局对象指的是 window 对象, 本质功能是存储系统级的内容
    // 我们声明的变量默认也会存储在 window 对象里,  这并非window的主要职责, 所以说:  自定义变量 污染了 全局对象

    // 造成的问题:
    // 1. 属性名和系统属性重复, 会导致覆盖
    // 2. 多个外部脚本中的变量 都存放在全局, 会导致冲突覆盖!

    // 利用函数作用域来解决全局污染!
  </script>
</body>

</html>

window对象

  • window 对象哪里来的?

浏览器提供的

  • window对象里的内容是做什么的

    提供操作浏览器的各种功能函数

  • 如何使用window对象中的内容

    两种方式可以使用

    • window.属性名
    • 属性名 : 全局中没有书写前缀, 默认到window对象中查找使用

函数作用域

html
<!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>作用域-局部 09:53</title>
</head>

<body>
  <script>
    // 函数运行时产生的作用域, 其中的变量使用范围仅限函数内部!
    // 函数分两种状态:
    // 1. 静态 -- 声明时
    // 2. 动态 -- 触发函数/调用函数, 例如 函数名()
    function show() {
      // 这里的 x 属于 函数的私有财产, 外部无法使用
      var x = 10

      // 函数中声明的变量, 存储在函数的作用域对象中
      // 没有存储在全局window里, 不会造成污染!
    }

    show() //动态

    console.log('x:', x) // 能打印出10吗?

      // 快速生成函数作用域的语法:  匿名函数自调用
      // var z = function () { }
      // z()

      ; (function () {
        var a = 10 // 属于函数作用域, 不会造成污染!
        var b = 20
      })()
  </script>
</body>

</html>

作用域链

html
<!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>作用域链 10:21</title>
</head>

<body>
  <!-- 作用域链: 多层作用域嵌套时, 当在某个作用域中使用一个变量, 按照作用域链的原则 向上层就近查找 -->

  <script>
    // var a = 10

    function x() {
      // var a = 20

      function y() {
        // var a = 30

        function z() {
          // var a = 40
          console.log('a:', a)
        }
        z()
      }
      y()
    }
    x()
  </script>
</body>

</html>

闭包

为什么有闭包机制?

  • 为了自身的正常运行, 需要把用到的外部作用域存储在自身, 防止其释放

Closure闭包:

  • 一种称呼, 代表函数作用域的一种状态: 被其他函数存储在 scopes 中,无法释放

官方描述:

  • 闭包(closure)是一个函数以及其捆绑的周边环境状态(lexical environment词法环境)的引用的组合。换而言之,闭包让开发者可以从内部函数访问外部函数的作用域。在 JavaScript 中,闭包会随着函数的创建而被同时创建。
html
<!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>闭包 10:32</title>
</head>

<body>
  <script>
    // 设定: 函数运行时产生的内存会在函数执行完毕后自动销毁, 节省内存
    // 除非 把变量存放在全局对象window, 因为window永存!

    function displayName() {
      var name = 'mike'

      var x = function () {
        console.log('name:', name)
      }

      return x
    }

    // show 中存储的是 displayName() 函数中声明的变量x
    // 所以x函数不会释放!
    var show = displayName()
    // 这里能打印出 name, 就说明 name 一定被存储了
    show()

    console.log(window)
    //scopes: 作用域 们  -- 存放函数声明时所在的作用域链 中的作用域
    //closure: 闭包 -- 封闭的包;  函数作用域生成的对象

    // 闭包 表达的是函数作用域的一种状态, 被别人存储在 scopes 里了
    // 类似 妻子/丈夫 这种称呼, 代表一个人的一种状态

    // 为什么要存闭包: 防止其释放后, 导致自身函数运行时, 找不到对应的变量资源 而 运行失败!

    // 闭包的缺点: 不可避免的消耗更多的内存
  </script>
</body>

</html>
html
<!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>闭包 10:55</title>
</head>

<body>
  <script>
    // 闭包的原则: 只存储用的到的值, 杜绝内存的浪费
    // 存储顺序是 就近原则
    // 只存储外部作用域
    function x() {
      var a = 10, b = 20

      function y() {
        var b = 30, c = 40

        function z() {
          var d = 50
          // 使用了 a b c d 4个变量
          // 这些变量都属于哪些作用域?
          console.log(a, b, c, d);
        }
        // cdi:  direct直接输出. 以对象的方式打印函数
        // log: 按照一定的规则对输出内容进行美化, 函数会以字符串模式打印
        console.dir(z)
      }
      y()
    }
    x()
  </script>
</body>

</html>

私有变量

公共变量: 存储在全局中 能够被多个函数使用. 不安全 不可靠; -- 公共的电脑

私有变量: 专门为 函数声明的变量 安全可靠, 非共享 -- 个人笔记本电脑

声明的方式:

  • 非全局变量: 局部变量, 通过函数运行时产生
    • 利用 匿名函数自调用语法 快速生成作用域
  • 利用 闭包 这个被动技能, 把这个局部作用域保存在自身, 后续随时使用.
html
<!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>私有属性 11:26</title>
</head>

<body>
  <script>
    // 公共属性
    var a = 10  //全局区

    function x() {
      a++
      console.log('a:', a)
    }

    function y() {
      console.log('a:', a)
    }

    x()
    y()

    // 私有属性: 只给某个函数使用的属性, 其他函数无法使用
    // 案例: 记录某个函数调用的次数

    // 利用匿名函数自调用语法 快速生成函数作用域/局部作用
    var show = (function () {
      // 这里的count 是函数作用域中的, 不在全局中, 所以非公有
      var count = 0  // alt + 上/下  可以调整代码位置

      function show() {
        count++
        console.log('调用次数:', count)
      }
      // show函数发动 被动技能 - 闭包, 把当前的匿名函数作用域保存在自身的闭包中, 属于show函数私有的财产
      console.dir(show)
      // 需要通过return的方式, 把show函数暴露到全局中
      return show
    })()

    console.log(window)
    // 在全局中使用show函数
    show()
    show()
    show()


    //////////////////////////////////////
    // 标准的私有属性声明方式:
    var suibian = (function () {
      // 命名规范: 以 _ 作为私有属性的开头, 可以达到 见名知意的效果
      var _x = 10
      var _y = 20
      // var z = function () {
      //   console.log(_y, _x)
      // }
      // return z
      return function () {
        console.log(_y, _x)
      }
    })()

    suibian()
    suibian()
  </script>
</body>

</html>

上午知识点回顾

作用域

  • 全局作用域

    • 顶级对象: window, 由浏览器提供的, 存放系统方法 (API) 的对象

      • 使用window中的属性, 可以直接调用

        js
        window.alert()
        alert()
  • 局部作用域

    • 函数在运行时产生的作用域, 属于一个私有的空间
    • 作用: 避免全局(变量/对象)污染
      • 在脚本中 自定义的属性, 默认存储在 window 对象里, 污染
      • 解决: 用匿名函数自调用语法, 快速生成局部作用域, 保存自定义的变量
  • 块级作用域

作用域链:

  • 当多层作用域嵌套时, 当在某个作用域下使用变量, 如果自身没有, 则按照就近原则 到上层作用域查找

闭包: 对于函数作用域的 一种状态的描述

  • 状态: 被其他函数存储在 scopes 中
  • 为什么: 为了确保函数的顺利执行, 需要把用到的外部作用域存储在自身scopes中, 防止其释放

闭包的使用场景: 私有属性

  • 利用闭包的存储特性, 把 存放在函数作用域中的属性, 存储在自身使用

    js
    var 函数名 = (function(){
        var _私有属性 =
        
        return function(){}
    })()

arguments

函数自带的属性:arguments

  • 其中存储函数收到的所有实参

使用场景:

  • 实参个数不固定的函数, 例如 max min
  • 函数重载: 通过判断 实参的个数 或 类型 执行不同的代码逻辑, 实现不同的效果!
html
<!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>arguments 14:10</title>
</head>

<body>
  <script>
    function show(x, y) {
      console.log(x, y)
      // 函数内部自带 arguments 变量, 存储收到的所有实参
      console.log('arguments:', arguments)
    }

    show(11, 22, 'mike', 'lucy', 'lily')

    // 来自 Math 的 max 函数, 求多个实参中的最大值
    console.log(
      Math.max(11, 2, 3, 43, 54),
      Math.max(11, 2, 3, 43, 54, 11, 23, 345, 6786),
    );

    // 仿写 max 函数
    function max() {
      // 利用 arguments 来查看收到的所有实参, 进行进一步处理
      console.log('max:', arguments)
      // 思路:
      // 1. 先假设数组的第一个值是最大的
      // 2. 然后从第二个值开始比较, 是否有更大的
      // 3. 如果有更大的, 就替换成 找到的最大值, 一直到最后
      var tmp_max = arguments[0] //临时最大值

      for (var i = 1; i < arguments.length; i++) {
        // 如果临时最大值 比 遍历出来的值 小
        if (tmp_max < arguments[i]) {
          // 替换临时最大值
          tmp_max = arguments[i]
        }
      }
      // 遍历结束后, 返回最终的结果
      return tmp_max
    }

    console.log(
      max(11, 22),
      max(11, 22, 342, 43, 564, 76),
    )

    // 什么是编程?
    // 把人类解决问题的思维,转换成代码的方式表达出来.


    // 练习1: 制作一个min函数, 找到实参中的最小值
    function min() {
      var tmp_min = arguments[0]

      for (var i = 1; i > arguments.length; i++) {
        if (tmp_min > arguments[i]) {
          tmp_min = arguments[i]
        }
      }

      return tmp_min
    }

    console.log(
      min(11, 22, 33),
      min(11, 22, 33, 34, 54, 65, 766),
    )

    // 练习2: 制作一个sum函数, 求出实参的总和
    function sum() {
      var total = 0
      for (var i = 0; i < arguments.length; i++) {
        total += arguments[i]
      }
      return total
    }

    console.log(
      sum(11, 232, 43, 56),
      sum(54, 54, 23, 3, 5, 4, 10),
    );
  </script>
</body>

</html>

函数重载

html
<!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>函数的重载 15:07</title>
</head>

<body>
  <!-- 函数重载: 根据实参的数量或类型不同, 执行不同的逻辑操作; 可以让函数变成多功能函数 -->
  <script>
    function zhekou(money) {
      // 判断实参的个数,  3个 就是满减
      if (arguments.length == 3) {
        if (money >= arguments[1]) { //超过满减条件
          money -= arguments[2]
        }
      }

      // 两个参数
      if (arguments.length == 2) {
        // 参数2: 字符串类型 - 代表 vip
        // 参数2: 数字类型 - 折扣
        if (typeof arguments[1] == 'number') {
          money *= arguments[1]
        }
        if (typeof arguments[1] == 'string') {
          if (arguments[1] == 'vip1') {
            money *= 0.95
          }
          if (arguments[1] == 'vip2') {
            money *= 0.8
          }
        }
      }

      return money
    }

    // zhekou : 计算折扣价格的函数
    // 使用方式:
    console.log(zhekou(3000, 0.8)) // 消费3000 打八折
    console.log(zhekou(3000, 0.6)) // 6折

    console.log(zhekou(3000, 2000, 500)) // 满2000 减500
    console.log(zhekou(3000, 3000, 700)) // 满3000 减700

    console.log(zhekou(3000, 'vip1')) // 95折
    console.log(zhekou(3000, 'vip2')) // 8折

  </script>
</body>

</html>

练习

html
<!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>练习</title>
</head>

<body>
  <script>
    function calc() {
      if (arguments.length == 1) {
        if (typeof arguments[0] == 'number') {
          return arguments[0] * arguments[0]
        }
        // instanceof: 判断对象是否是指定构造函数创建的
        // 是 Array 创建的, 则说明是数组类型
        if (arguments[0] instanceof Array) {
          var total = 0
          // arguments[0] 是 数组
          for (var i = 0; i < arguments[0].length; i++) {
            total += arguments[0][i]
          }
          return total
        }
      }

      if (arguments.length == 2) {
        return arguments[0] + arguments[1]
      }

      if (arguments.length == 3) {
        return arguments[0] * arguments[1] * arguments[2]
      }
    }

    // 实现一个 calc 函数, 预期效果如下
    console.log(calc(10)) //算出数字的平方, 即 10 * 10
    console.log(calc(20)) //算出数字的平方, 即 20 * 20
    console.log(calc(10, 20)) //算出和值, 即 10 + 20
    console.log(calc(10, 20, 30)) //算出乘积, 即 10 * 20 * 30

    // 难
    console.log(calc([11, 22, 33])) //数组类型, 则计算出数组内容的和
  </script>
</body>

</html>

this

是什么?

  • 是函数中自带的一个变量, 指向函数运行时所在的对象 -- 非常灵活

使用场景?

  • 为函数提供了另一种执行方式
    • 传统方案: 通过传递参数的方式 把对象传入
    • 当前方案: 把函数作为参数传递到对象里执行 -- 反过来
html
<!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>this 16:13</title>
</head>

<body>
  <!-- 
    this关键词: 代表函数运行时所在的对象
    从格式上分辨:  对象.方法名()  方法中的this 就是 前面的对象
   -->

  <script>
    function show() {
      console.log('this:', this) // window
    }

    show() // 简写
    window.show() // 全局的show函数, 在window对象里

    var emp = {
      ename: "俊俊",
      show: function () {
        console.log('this:', this)
      }
    }

    emp.show()
  </script>
</body>

</html>
html
<!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>this使用场景 16:22</title>
</head>

<body>
  <script>
    // 矩形对象:
    var r1 = { length: 10, width: 20 }
    var r2 = { length: 100, width: 120 }

    // area函数: 用于计算 矩形的面积  长 * 宽

    // 传统做法:
    function area(rect) {
      return rect.length * rect.width
    }

    console.log(
      area(r1), area(r2)
    );


    // this模式的函数
    function area1() {
      // this: 运行时服务的对象, 所在的
      return this.length * this.width
    }

    // 1. 把 area1 存储到对象中 :  上门服务
    r1.area1 = area1
    console.log(r1)
    // 2. 执行 r1 中的 area1 函数
    console.log(
      r1.area1()
    )
    // 3. 执行完毕后, 从对象中删除.
    delete r1.area1

    // 练习: 把 area1 放到 r2 执行
    // 1. 上门服务:  函数存入r2中
    r2.area1 = area1
    // 2. 在 r2 中执行
    console.log(r2.area1())
    // 3. 删除
    delete r2.area1
  </script>
</body>

</html>

练习

html
<!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">
  
</head>

<body>
  <script>
    // 立方体: 长 宽 高
    var c1 = { length: 10, width: 20, height: 30 }

    // 制作一个函数 volume 计算体积 = 长 * 宽 * 高
    function volume() {
      // 在制作包含this变量的函数时, 要提前预判 这个函数运行时的环境, 才能知道 this 是什么
      return this.length * this.width * this.height
    }

    // 要求: 把 volume 传递到 c1 对象里执行, 返回其体积
    // 1. 函数上门服务: 到c1对象里
    c1.volume = volume
    // 2. 调用
    console.log(c1.volume())
    // 3. 删除
    delete c1.volume
  </script>
</body>

</html>

call

html
<!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">
 
</head>

<body>
  <script>
    // call: 短暂拜访
    // 函数 具有一个短暂拜访对象的方法 -- call
    function area() {
      return this.length * this.width
    }

    var r1 = { length: 10, width: 20 }

    // 任务: 把 area 函数临时放到 r1 对象中执行
    console.log(
      area.call(r1) // area函数短暂拜访 r1 对象
    )

    console.dir(area) // dir: 直接输出函数对象

    // 练习: 立方体
    var c1 = { length: 10, width: 20, height: 30 }

    // 1. 制作 volume 函数, 计算 体积=长*宽*高
    // 把此函数放到 c1 中执行, 获取其体积
    function volume() {
      return this.length * this.width * this.height
    }
    console.log(volume.call(c1))

    // 2. 制作 area 函数, 计算 面积=(长x宽 + 长x高 + 宽x高)*2
    // 把此函数放到 c1 中执行, 获取其面积
    function area() {
      return (this.width * this.length + this.length * this.height + this.width * this.height) * 2
    }

    console.log(area.call(c1))
  </script>
</body>

</html>

练习

html
<!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">
  
</head>

<body>
  <script>
    var emp1 = { name: "俊俊", salary: 30000 } //月薪
    var emp2 = { name: "小明", salary: 10000 }
    var emp3 = { name: "泡泡", salary: 20000 }

    // 所得税汇算函数  shui
    // 年薪 10~ 15w   扣税 5%
    // 年薪 >15 ~ 20  扣税 10%
    // 年薪 >20 ~ 30  扣税 15%
    // 年薪 >30 ~ 40  扣税 20%
    // >40  扣税40%;   <10  不扣税
    function shui() {
      var total = 12 * this.salary // 年薪
      // 语法糖: 代码在满足一些特定条件时, 有简化写法. 让程序员能够偷懒, 像吃糖一样, 令人愉悦!
      // if判断的 {} 里只有一行代码, 可以省略{}
      if (total < 10) return 0
      if (total >= 10 && total < 15) return total * 0.05
      if (total >= 15 && total < 20) return total * 0.1
      if (total >= 20 && total < 30) return total * 0.15
      if (total >= 30 && total < 40) return total * 0.2
      if (total >= 40) return total * 0.4
    }

    // 把shui函数, 放到 emp1 2 3 中, 分别计算扣税的金额
    console.log(shui.call(emp1))
    console.log(shui.call(emp2))
    console.log(shui.call(emp3))
  </script>
</body>

</html>

call的实参

html
<!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>call的参数 17:48</title>
</head>

<body>
  <script>
    var emp = { ename: '俊俊', salary: 20000 }

    // 制作一个函数, 用于获取员工  n 个月的总薪资
    function total(n) {
      return this.salary * n
    }

    console.log(
      // 6个月
      // 参数1: 指定函数执行时所在的对象, 即 this 的指向
      // 参数2 之后 : 传递给函数的实参列表
      total.call(emp, 6)
    )

    ///////////////////////
    var x = { a: 10 }

    function show(b, c, d) {
      return this.a + b + c + d
    }

    console.log(
      show.call(x, 20, 30, 40)
    );
  </script>
</body>

</html>

内容总结

  • 作用域

    • 全局
      • 顶级对象window - 浏览器提供, 存放系统方法
    • 局部
      • 利用局部的函数作用域, 避免全局污染
    • 块级
  • 作用域链

    • 多层作用域嵌套, 按照就近原则 到上级作用域查找变量来使用
  • 闭包 Closure

    • 是一种状态: 函数作用域被 别的函数保存在自身的scopes中, 无法释放
  • 函数中隐藏的变量

    • arguments: 保存所有实参

      • 实参个数不固定的函数
      • 函数重载: 多功能函数
    • this: 指向函数运行时所在的对象, 特别灵活

      • 把函数 传递到 对象中执行

      • call: 短暂访问; 更加便捷的把 函数放到对象中执行

        js
        函数.call(对象, 实参, 实参...)

作用域: 代码起作用的领

  • 全局 - 在 script 脚本中直接声明的变量

    • 顶级对象: window. 浏览器提供, 存放系统级的各种方法 API

      顶级对象中的属性, 在使用时 可以直接书写调用

      js
      window.alert()
      
      alert()

      全局污染: window用于存储系统API, 把 自定义的变量放这里 则造成污染

  • 局部 - 函数运行时诞生, 仅限在函数内使用

    • 变量在函数中声明, 就不会造成全局污染. 利用 匿名函数自调用 快速形成函数作用域

      js
      (function(){})()
  • 块级

作用域链: 多层作用域嵌套时, 使用到某个变量如果自身不存在, 则到上级作用域就近查找

闭包:

  • 为什么有闭包: 防止外层作用域中的变量自动释放, 导致自身运行失败
  • 做法: 把外层作用域保存到自身的 scopes 属性里
  • 闭包Closure:被保存到 scopes 中的 函数作用域对象, 就叫闭包

使用场景:私有属性

js
var 函数 = (function(){
    var _属性名 =
    
    return function(){}
})()

函数相关: 函数体中存在两个隐形的变量

  • arguments: 存储了所有的实参

    • 制作 实参个数 不固定的函数, 比如 max min
    • 制作 多功能函数 - 函数重载. 通过 if 判断 实参的个数或类型不同, 做不同的逻辑操作
  • this: 非常灵活, 函数运行时才能确定, 指向函数所在的对象

    • 新的做法: 把函数传递到对象里执行

    • 函数的call方法: 短暂访问; 快速把函数临时放到指定对象里执行

      函数.call(对象, 实参, 实参...)

apply

函数的一个属性, 作用和 call 方法极其相似:

  • 函数.apply(对象): 把函数临时放到对象里执行, 修改函数中的this指向

不同点:

  • 实参通过 数组类型 进行传递

    js
    函数.apply(对象, [实参, 实参, ...])

用途:

  • 如果函数接收的是实参列表, 但是我们只有数组类型, 则利用此函数实现数组转实参列表的效果
html
<!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>apply 09:16</title>
</head>

<body>
  <script>
    // call: 临时把函数放对象里执行
    // apply: 临时把函数方对象执行
    var emp = { name: '小明', salary: 10000 }
    // 年终奖 , 扣税
    function total(zhong, shui) {
      return 12 * this.salary + zhong - shui //年终奖
    }
    // 区别: 实参传递不同
    // call: 实参要1个1个传递
    console.log(total.call(emp, 50000, 30000))
    // apply: 实参要放数组里传递
    console.log(total.apply(emp, [50000, 30000]))
  </script>
</body>

</html>

练习

html
<!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">
</head>

<body>
  <script>
    // 制作一个 sum 函数, 能够计算出 所有实参的总和
    function sum() {
      var total = 0
      for (var i = 0; i < arguments.length; i++) {
        total += arguments[i]
      }
      return total
    }

    var nums = [21, 32, 43, 6546, 65, 3]
    // 用sum函数,求出 nums 数组中的元素总和

    // 利用 apply 把 数组 转为 参数列表的形式
    // 参数1: 是 sum 在哪个对象里执行, 即 this 的指向
    // 由于 sum 函数体中, 没有使用this, 则可以随意传递
    console.log(sum.apply(1, nums))

    // 预期用法:
    console.log(sum(11, 22, 33))
    console.log(sum(5, 4, 32, 14, 45, 67))

    // 练习:
    // max: 求出实参列表中的最大值
    console.log(Math.max(1, 21, 43, 54, 65));
    // 参数1: 随便书写, max的执行不依赖于this
    console.log(Math.max.apply(null, nums))

    // 练习: 求最小值
    console.log(Math.min.apply('', nums))
  </script>
</body>

</html>

bind

函数触发的三种方案:

  • call: 临时把函数放在对象中执行, 用于设定this的指向
  • apply: 临时放对象中执行, 可以把数组 转为 参数列表
  • bind: 提前绑定 函数和运行时的资源, 简化后续调用时的格式
html
<!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>bind 10:06</title>
</head>

<body>
  <!-- 问题: 在HTML代码中, 掺杂过多的 JS 代码, 导致些许混乱 -->
  <button onclick="total.call(emp, 50000, 30000)">计算年薪</button>
  <br>
  <!-- 触发bind函数 -->
  <button onclick="total_bind()">计算年薪</button>

  <script>
    var emp = { ename: "小明", salary: 10000 }

    function total(zhong, shui) {
      alert(this.salary * 12 + zhong - shui)
    }


    // bind: 绑定
    // 作用: 把函数和其运行时需要的资源捆绑在一起, 返回新的函数
    var total_bind = total.bind(emp, 50000, 30000)

    console.dir(total_bind);
  </script>
</body>

</html>

构造函数

html
<!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>构造函数 10:23</title>
</head>

<body>
  <script>
    // 给web的老师建立档案
    var teachers = [
      // 代码中 不应该出现 大批量的复制粘贴情况.
      // 通常用函数实现复用 -- 重复使用
      { ename: "1", age: 18, phone: "18989787444" },
      { ename: "2", age: 28, phone: "18944444446" },
      { ename: "3", age: 38, phone: "18933338944" },
      { ename: "4", age: 32, phone: "18985478784" },
      { ename: "5", age: 35, phone: "15689789879" },
      { ename: "6", age: 25, phone: "13659879797" },
    ]

    console.log(teachers)

    // 新建一个 Teacher 函数, 帮我们生成 讲师 对象
    function Teacher(ename, age, phone) {
      var obj = {}

      obj.ename = ename
      obj.age = age
      obj.phone = phone

      return obj
    }
    // 构造函数: 功能是用于构建创造对象的函数, 称为构造函数.
    var t1 = Teacher('6', 32, '10086')
    console.log(t1)
  </script>
</body>

</html>

练习

html
<!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>练习</title>
</head>

<body>
  <script>
    // 代码规范: 构造函数 采用大驼峰命名法, 即单词首字母均大写
    // - 达到见名知意的效果
    function Student(sname, age, sid) {
      var obj = {}

      obj.sname = sname
      obj.age = age
      obj.sid = sid

      return obj
    }

    // 制作1个 学生对象的构造函数, 使用时
    var stu1 = Student('小明', 22, '0002')
    //效果: { sname:"小明", age:22, sid:"0002" }
    console.log(stu1)

    function Product(pname, price, maker, count) {
      var obj = {}

      obj.pname = pname
      obj.maker = maker
      obj.price = price
      obj.count = count

      return obj
    }

    // 制作1个 产品对象的构造函数
    var p1 = Product('鼠标', 300, 'logi', 4)
    console.log(p1)
    // {pname:"鼠标", price:300, maker:"logi", count:4}
  </script>
</body>

</html>

new

html
<!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>new</title>
</head>

<body>
  <script>
    // new运算符: 搭配构造函数使用时, 可以自动完成一些代码
    function Teacher(ename, age, phone) {
      // this非常灵活: 默认代表函数运行时所在对象
      // 如果是 new 运算符触发, 则this指向 当前构造出来的对象

      // 省略的代码如下
      // this = {}

      this.ename = ename
      this.age = age
      this.phone = phone

      // return this
    }

    // 当系统发现 函数前存在 new 运算符, 则认为此函数为构造函数
    // 就会自动辅助此函数 完成一些构造函数 特有的代码
    var t1 = new Teacher('小明', 32, '10086')
    console.log(t1)
  </script>
</body>

</html>

练习

html
<!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>练习</title>
</head>

<body>
  <script>
    function Student(sname, age, sid) {
      this.sname = sname
      this.age = age
      this.sid = sid
    }

    // 制作1个 学生对象的构造函数, 使用时
    var stu1 = new Student('小明', 22, '0002')
    //效果: { sname:"小明", age:22, sid:"0002" }

    function Product(pname, price, maker, count) {
      this.pname = pname
      this.price = price
      this.maker = maker
      this.count = count
    }

    // 制作1个 产品对象的构造函数
    var p1 = new Product('鼠标', 300, 'logi', 4)
    // {pname:"鼠标", price:300, maker:"logi", count:4}

    console.log(p1, stu1);
  </script>
</body>

</html>

prototype

总结:

  • 为什么要设计原型模式?
    • 节省内存
  • 如何节省的?
    • 把公共的方法存储在 共享的对象 : 构造函数.prototype 中
    • new 运算符生成对象时, 自动为对象关联 __proto__ 到 共享对象 prototype 上
    • JS引擎的原型链设计, 全自动
      • 如果 对象.属性 对象自身没有, 则自动到 __proto__ 中查找!
html
<!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>原型设计</title>
</head>

<body>
  <script>
    // 制作生成矩形对象的构造函数 Rect
    function Rect(length, width) {
      // new运算符构造对象时, 会额外默认完成
      // 为当前构造的对象, 关联其父元素 为 构造函数的prototype
      // this.__proto__ = Rect.prototype

      this.length = length
      this.width = width

      // 新增计算面积的方法
      // this.area = function () {
      //   return this.length * this.width
      // }
    }

    // 构造函数 -- 相当于 母亲角色, 能生对象
    // 构造函数必然存在 prototype 属性, 即其丈夫
    // prototype.constructor : 关联到构造函数, 即丈夫的妻子
    console.dir(Rect) //查看 prototype 属性

    // 把共享的方法,存储在 prototype 原型中
    Rect.prototype.area = function () {
      return this.length * this.width
    }

    // 用法:
    // r1: 母亲通过new方案 生下来的对象
    var r1 = new Rect(10, 20) // {length: 10, width:20}
    console.log(r1)

    // 母亲的丈夫 == 孩子的父亲
    // 原型属性的名称 : __proto__ 是未经美化的原属性名
    // 由于官方的要求, 浏览器必须按照固定规则 美化后再显示
    console.log(Rect.prototype == r1.__proto__) // true

    console.log(
      // JS引擎自带 原型链机制: 
      // 白话文: 我自己没有的东西, 找我爸爸要
      // 专业: 自身没有的属性, 到原型链__proto__中查找
      r1.area()
    )

    // 构造函数能够反复多次调用, 每次调用都会执行函数中的代码

    var r2 = new Rect(20, 30)
    var r3 = new Rect(40, 50)
    // 代表两个对象中的area 函数非同一个
    console.log(r2.area == r3.area) //false

    // 问题: 在构造函数中, 为对象添加方法属性, 导致内存的浪费. 因为方法是可以共享的
  </script>
</body>

</html>

练习

html
<!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>练习</title>
</head>

<body>
  <script>
    // Rect : 矩形构造函数
    // 初始化时, 需要传入 宽 和 高的值
    // 生成的对象具有两个方法: area面积  perimeter周长
    function Rect(length, width) {
      this.length = length
      this.width = width
    }

    // 函数: 应该共享 -- 存放在原型中
    Rect.prototype.area = function () {
      return this.width * this.length
    }

    Rect.prototype.perimeter = function () {
      return (this.width + this.length) * 2
    }

    // 使用时:
    var r1 = new Rect(20, 30)
    console.log(r1) // {length:20, width:30}
    console.log(r1.area()) // 面积 = 长x宽
    console.log(r1.perimeter()) // 周长 = (长+宽)*2

    ////////////////////////////////////////////////
    // 立方体构造函数 Cube
    // 初始化时, 有三个属性.  长length 宽width 高height
    // 带有3个方法: area面积   volume体积   perimeter周长
    function Cube(length, width, height) {
      this.length = length
      this.width = width
      this.height = height
    }
    // 为了防止每次构造时, 都去创建一次函数, 所以才要放在外部书写
    Cube.prototype.area = function () {
      return 2 * (this.length * this.width + this.length * this.height + this.width * this.height)
    }
    Cube.prototype.volume = function () {
      return this.length * this.height * this.width
    }
    Cube.prototype.perimeter = function () {
      return (this.length + this.width + this.height) * 4
    }

    // 使用方式
    var c1 = new Cube(10, 20, 30)
    // {length:10,width:20,height:30}
    console.log(c1)
    console.log(c1.area()) // 面积=(长x宽+长x高+宽x高)*2
    console.log(c1.perimeter()) // 周长=(长+宽+高)*4
    console.log(c1.volume()) // 体积=长x宽x高

    ////////////////////////////////////////
    // 圆形构造函数: Circle
    // 初始化参数: 半径 radius
    // 拥有方法: 周长  面积  直径
    function Circle(radius) {
      this.radius = radius
    }

    Circle.prototype.diameter = function () {
      return this.radius * 2
    }
    Circle.prototype.area = function () {
      return this.radius * this.radius * 3.14
    }
    Circle.prototype.perimeter = function () {
      return this.radius * 3.14 * 2
    }

    // 使用时
    var c1 = new Circle(10) //{radius: 10}
    console.log(c1)
    console.log(c1.diameter()) // 直径 = 半径 x 2
    console.log(c1.area()) // 面积 = 3.14 * 半径 * 半径
    console.log(c1.perimeter()) // 周长=2 * 3.14 * 半径
  </script>
</body>

</html>

数组的原型

html
<!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>数组的原型</title>
</head>

<body>
  <!-- 
    JS: 万物皆对象, 对象由构造而来
   -->
  <script>
    var a = 5  // 字面量写法: 属于语法糖
    console.log(a.toFixed(2))

    //构造写法
    var a = new Number(5)
    console.log(a)

    // 数组
    var nums = [11, 22, 33, 44, 55]
    console.log(nums)

    // 为数组的原型增加一个求和的方法 sum
    Array.prototype.sum = function () {
      // this: 函数运行时所在的对象
      var total = 0
      for (var i = 0; i < this.length; i++) {
        total += this[i]
      }
      return total
    }

    console.log(nums.sum()) 
  </script>
</body>

</html>

class语法

html
<!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>class语法</title>
</head>

<body>
  <script>
    // JS原型的这套写法并不受欢迎, 在java语言中存在相同功能的语法 -- class语法.  比JS的语法更容易让人接受
    // 在 2015年6月 出品的 ES6 即 JS的第六个版本中, 集成了Java的class语法

    // class: 类;    一类事物

    // 本质相当于一种语法糖, 自动完成原型的相关操作. 不需要程序员自己操作prototype 属性
    class Rect {
      // 固定名称的方法, 省略function关键词, 通过new触发
      constructor(length, width) {
        this.length = length
        this.width = width
      }

      area() {
        return this.width * this.length
      }

      perimeter() {
        return 2 * (this.width + this.length)
      }
    }

    console.dir(Rect)

    var r1 = new Rect(10, 20)
    console.log(r1)

  </script>
</body>

</html>

练习

html
<!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>练习</title>
</head>

<body>
  <script>
    // 立方体构造函数 Cube
    // 初始化时, 有三个属性.  长length 宽width 高height
    // 带有3个方法: area面积   volume体积   perimeter周长
    class Cube {
      constructor(length, width, height) {
        this.length = length
        this.width = width
        this.height = height
      }

      area() {
        return (this.length * this.width + this.width * this.height + this.length * this.height) * 2
      }

      perimeter() {
        return (this.width + this.length + this.height) * 4
      }

      volume() {
        return this.height * this.length * this.width
      }
    }

    // 使用方式
    var c1 = new Cube(10, 20, 30)
    // {length:10,width:20,height:30}
    console.log(c1)
    console.log(c1.area()) // 面积=(长x宽+长x高+宽x高)*2
    console.log(c1.perimeter()) // 周长=(长+宽+高)*4
    console.log(c1.volume()) // 体积=长x宽x高

    ////////////////////////////////
    // 圆形构造函数: Circle
    // 初始化参数: 半径 radius
    // 拥有方法: 周长  面积  直径
    class Circle {
      constructor(radius) {
        this.radius = radius
      }

      diameter() {
        return this.radius * 2
      }

      area() {
        return this.radius * this.radius * 3.14
      }

      perimeter() {
        return this.radius * 2 * 3.14
      }
    }

    // 使用时
    var c1 = new Circle(10) //{radius: 10}
    console.log(c1)
    console.log(c1.diameter()) // 直径 = 半径 x 2
    console.log(c1.area()) // 面积 = 3.14 * 半径 * 半径
    console.log(c1.perimeter()) // 周长=2 * 3.14 * 半径
  </script>
</body>

</html>

严格模式

html
<!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>严格模式</title>
</head>

<body>
  <!-- 2009年出品的 JS第五个版本: ES5.  提供的新特性 - 严格模式 -->
  <!-- 可以手动让 JS 代码进入到严格模式的状态, 此时 JS引擎会对代码严格要求, 有风险的操作都会报错!  强制程序员写出健康的代码. -->
  <script>
    // use:使用;   strict:严格
    // 代表下方所有代码, 用严格模式处理
    'use strict'

    // 严格模式下, 变量必须先声明 再使用

    var servername = 'localhost'

    // 修改
    servename = 'www.tedu.cn'

    // 默认全局中的属性, 自带前缀  window.
    // window.servename = 'www.tedu.cn'

    // 由于单词拼写错误, 导致在 window对象中 新增了属性 -- 全局污染!

    console.log(window)
  </script>
</body>

</html>
html
<!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>严格模式 16:00</title>
</head>

<body>
  <script>
    // 严格模式下, 函数中的this如果是window, 则改为undefined; 防止全局污染
    'use strict'

    function Rect(length, width) {
      // window.length = length
      console.log('this:', this)
      this.length = length
      this.width = width
    }
    // new: 专门搭配构造函数使用, 默认会把函数中的this 指向构造出来的对象
    // 忘记写new了, 则this指向函数运行时所在对象 --> window
    var r1 = Rect(10, 20)
    console.log(r1)

    console.log(window)
  </script>
</body>

</html>
html
<!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>严格模式</title>
</head>

<body>
  <script>
    // 以后写JS代码, 尽量开启严格模式
    // 后续使用的第三方框架 默认均开启严格模式

    // 严格模式下, 禁用 静默失败
    // 失败的操作 提供报错, 辅助程序员排查BUG
    'use strict'

    var emp = { ename: "皮喆", age: 19 }
    // freeze: 冻结
    // 冻结的对象, 其内容无法被修改
    Object.freeze(emp)

    emp.age = 30
    console.log(emp)
  </script>
</body>

</html>

ES6

html
<!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>ES6</title>
</head>

<body>
  <!-- 
    ES6 也称  ES2015:
    2015年6月出品的 JS的第6个版本

    此版本中, 引入大量的新特性, 解决JS沉积多年的各种弊病
    - 这是一个 里程碑的版本

    https://www.runoob.com/w3cnote/es6-tutorial.html
   -->
</body>

</html>

let与const

html
<!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>let 与 const</title>
</head>

<body>
  <script>
    // ES6前: 两种方式可以声明变量  var   function
    var a = 10
    function aa() { }
    // 问题: 在全局中声明的变量, 会存储在顶级对象 window 中
    // window用于存储系统属性. 放自定义属性则 污染

    // 解决方案: 利用匿名函数自调用语法, 把变量存储在这里.

    // 使用 let /const 声明的变量, 存储在新增的顶级对象 script 中
    // 专门存储 自定义的全局属性.  需要利用浏览器的断点功能查看
    // 断点: 中断的点. 让代码运行时 停在某一行 (子弹时间)
    let abb = 111
    const bba = 222

    console.log(window)

    // let: 变量 -- 值可以变更  -> 和var相同
    // const: 常量 -- 初始化赋值后, 不能修改.  更安全

    // 根据实际的场景来选择用什么方式声明变量

    // 声明一个变量, 存放 俊俊的 薪资
    let salary = 8000
    salary = 20000 //后期可修改

    // 声明一个变量, 存放 俊俊的 媳妇
    const wife = '翠花'
    // wife = '金莲'
    // console.log(wife)

    // 特殊情况
    const wife1 = { name: '翠花', hair: "短发" }
    wife1.hair = '大波浪'
    console.log(wife1)


  </script>
</body>

</html>

块级

html
<!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>块级作用域</title>
</head>

<body>
  <script>
    // 块级作用域就是 函数作用域的 简化版本

    // 以前: 用匿名函数自调用语法生成作用域
    // (function () { })()

    // 现在: 用 {} 配合 let/const 即可形成块级作用域
    {
      let a = 10
      const b = 20
      console.log(111)
    }

    for (let i = 0; i < 10; i++) {
      console.log(i)
    }


    console.log(window)

    // 私有属性: 函数作用域 诞生私有的变量, 通过闭包机制存储在函数里
    var show = (function () {
      var _count = 0

      return function () {
        _count++
        console.log('次数:', _count)
      }
    })()

    console.dir(show) //查看scopes
    show()
    show()

    ///////////////////////////////////////
    // 块级实现私有属性
    {
      let _count = 0

      function talk() {
        _count++
        console.log('次数:', _count);
      }
    }

    console.dir(talk);
    talk()
    talk()
    talk()
  </script>
</body>

</html>

声明提升

html
<!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>声明提升 </title>
</head>

<body>
  <script>
    // JS引擎的机制: 会先阅读一次代码, 把代码按照规范改为规范的代码 然后执行

    // 整体是数学表达式
    // 理论上 参与数学表达式的值, 都应该是数字类型
    // 隐式类型转换: 类型不规范, 则按照规则转换后, 再执行
    console.log(30 - true + null * '12')

    // 声明提升
    // 正常的规范: 先声明 再使用
    // 隐式的声明提升操作: 会先阅读一次代码, 把所有的声明操作移动到代码的最上方, 然后再执行

    // 声明提升 广受诟病: 导致代码的运行结果 和 所见到的不一样
    function show() {
      console.log(11);
    }
    show()

    function show() {
      console.log(22)
    }
    show()

    // ES6: 提供的 let /const 对声明提升有新的处理
    console.log(x)
    var x = 10

    // let: 拥有暂存死区的设定;
    // 首先: let/const 存在声明提升
    // 但是: 暂存死区的状态要求: 在执行声明代码行之前, 不允许使用此变量, 否则报错
    // 总结: 声明提升是 JS引擎的基本设定,无法推翻;  作者添加报错机制, 强制用户先声明再使用.
    console.log(y);
    let y = 10
  </script>

</body>

</html>

总结

函数的三种触发方式

  • call: 让函数短暂访问对象, 修改函数中的this指向
  • apply: 把数组 转换成 参数列表形式, 来调用函数
  • bind: 把函数运行时依赖的 对象和实参 绑定起来. 便于后续的调用.

构造函数: 用于创建对象的函数, 要求大驼峰命名法

  • new: 3件事, 专门辅助构造函数

    js
    this = {}
    this.__proto__ = 构造函数.prototype
    return this
  • prototype: 原型机制

    • 节省内存, 把对象共享的方法存储在 prototype 对象里
    • 利用原型链机制:__proto__ 对象.属性, 对象没有属性 就自动到 __proto__中查找
  • class: 来自 Java 的语法, 更简单

ES5 的严格模式

  • 2009年推出, 提供更多的报错. 强制程序员写出更好的代码
  • 开启方式: 'use strict'

ES6 的 let/const

  • 新的全局变量存储位置: 脚本区, 专门放自定义属性
  • 新的作用域: 块级
  • 声明提升: 暂存死区. 利用报错强制程序员 先声明 再使用
  • const: 常量; 初始化之后无法修改, 安全!

回顾

函数触发的方式

  • call: 函数临时放到对象中执行. 切换函数中的this指向
  • apply: 把 数组 转换成 参数列表. 传递到函数中使用
  • bind: 函数运行时的 参数 和 所在对象 捆绑在一起. 便于后期调用

构造函数: 用于构建创造对象的函数

  • new: 用于辅助构造函数使用, 隐式完成一些代码

    js
    this = {}
    this.__proto__ = 构造函数.prototype
    return this
  • prototype: 原型

    • 作用: 节省内存
    • 实现方式:
      • 共享方法存储在 构造函数.prototype 对象中
      • 原型链机制: 对象使用属性时, 自身没有 则 自动到 __proto__查找使用
  • class: 来自 Java 的class语法, 更容易书写

    js
    class 类名{
        // new 运算符触发
        constructor(){}
        
        方法(){}
        
        方法(){}
    }

ES5 - 严格模式

  • 开启方式: 'use strict'
  • 作用: 提供更多的报错. 强制程序员书写更健康的代码

ES6 - let/const

  • 带来新的顶级对象, 用于存储自定义的全局变量
  • 块级作用域: 用 {} 搭配使用
  • 声明提升: 存在声明提升 但是 有暂存死区 设定. 在声明行代码运行之前,不允许使用此变量.
    • 利用报错方式, 强制用户 先声明再使用
  • const 常量: 声明时必须赋值, 后续无法更改. 更安全

模板字符串

html
<!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>模板字符串</title>
</head>

<body>
  <script>
    // 传统字符串 用 "" 或 '' 书写

    // 问题1: 不支持换行
    // var html = '<ul>
    //   <li></li>'

    // 问题2: 字符串拼接方案繁琐, 用 + 
    var emp = { ename: "小明", age: 19, phone: "10086" }

    // 姓名:xxx, 今年xx岁. 手机号xxxx
    var words = '姓名:' + emp.ename + ', 今年' + emp.age + '岁. 手机号' + emp.phone

    // ES6中的 字符串增强语法 -> 模板字符串
    // 符号: ``
    // 支持换行 和 ${} 在字符串中书写JS代码
    var skills = ['js', 'html', 'css']

    var words = `<ul>
        <li>${skills[0]}</li>
        <li>${skills[1]}</li>
        <li>${skills[2]}</li>
      </ul>`
    console.log(words)
  </script>
</body>

</html>

箭头函数

html
<!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>箭头函数</title>
</head>

<body>
  <script>
    // ES6 提供的 匿名函数语法

    // 之前的匿名函数
    // function() { }
    // 箭头函数
    // () => { }

    // 官方提供语法糖:
    var show = (x) => {
      return 2 * x
    }
    // 糖1: 形参有且只有1个时, 可以省略()
    var show = x => {
      return 2 * x
    }
    // 糖2: 函数体仅有一行时,可以省略 {return }
    var show = x => 2 * x

    console.log(show(10))

    //////////////////////////
    //练习: 尝试用语法糖 简化下方代码
    var a = (y) => { return y * y }
    var a = y => y * y

    var b = (x, y) => { return x + y }
    var b = (x, y) => x + y

    var c = (m, n) => {
      return { m: m, n: n }
    }
    // 问题: 对象的{} 被误认为是 函数的{}, 产生歧义
    // 解决: 用()括起来, 从格式上避免歧义

    // { 属性名: 值 }.  巧合: 值是变量, 变量名和属性名相同
    var c = (m, n) => ({ m: m, n: n })
    // 语法糖: 属性名和变量名一样, 则可以合写
    var c = (m, n) => ({ m, n })

    console.log(
      c(10, 20)
    )

    // 糖:
    var emp = {
      // 可以省略 : function
      // show: function () { alert("Hello!") }
      show() { alert("Hello!") }
    }

    emp.show()
  </script>
</body>

</html>

箭头函数的this

html
<!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>箭头函数中的this</title>
</head>

<body>
  <script>
    // 关于function的this
    // 对象.方法名(): this是前方的对象
    // 方法名(): this是window
    // new 方法名(): this是构造出的 实例对象

    // 关于箭头函数的this
    // 箭头函数没有this, 按照作用域链的就近原则到上级作用域查找
    var emp = {
      ename: "小明",
      show() {
        // 箭头函数没有this, 所以使用上级 show 函数中的this
        // emp.show() :  所以 show的this 就是emp
        var a = () => {
          console.log('this:', this)
          console.log(this == emp)
        }
        a()
      }
    }

    emp.show()
  </script>
</body>

</html>

数组的高阶函数

函数内 使用了其他函数, 就称为高阶函数

  • every: 每一个元素都满足条件
  • some: 至少有一个元素满足条件
  • filter: 把满足条件的元素过滤出来
  • map
  • forEach
  • reduce

every

html
<!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>every</title>
</head>

<body>
  <script>
    // 数组的高阶函数 -- 不属于ES6的新特性
    // 高阶函数: 一个函数的内部使用了其他函数, 常见的带有回调函数的函数;

    console.log(Array.prototype)

    // every: 每一个
    // 数组中, every可以自动遍历数组, 检查每一个元素是否符合指定条件
    // every最终结果: 全真则真, 有假为假;  与 逻辑与操作相似 &&

    var nums = [12, 432, 453, 65, -32, 12, 43]
    // 需求: 判断数组中 是否 所有的/每一个 值都是正数

    // 实参: 要求函数类型
    // every会自动遍历数组, 把数组中的每个元素 都传递给 箭头函数
    var res = nums.every((value, index, array) => {
      // 三个参数: 值, 序号, 数组本身
      // 关系: array[index] == value
      console.log(value, index, array)
      // 返回 判断的结果, 例如 >0 代表正数
      return value > 0
    })

    console.log(res ? '都是正数' : '非都是正数');
  </script>
</body>

</html>

练习

html
<!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>练习</title>
</head>

<body>
  <script>
    var nums = [12, 43, 54, 6, 65, 23, 54, 33]
    // 1. 判断元素是否都大于10
    var res = nums.every((value, index, array) => {
      return value > 10
    })

    // 形参: 未使用到的可以不写
    var res = nums.every((value) => {
      return value > 10
    })

    // 糖1: 形参只有1个 可以省略()
    var res = nums.every(value => {
      return value > 10
    })
    // 糖2: 方法体只有一行,省略{return }
    var res = nums.every(value => value > 10)

    console.log(res ? '都大于10' : "非都大于10")

    // 2. 判断元素是否都是偶数  对2取余是0
    var res = nums.every((value, index, array) => {
      return value % 2 == 0
    })

    var res = nums.every(value => value % 2 == 0)
    console.log(res ? '都是偶数' : '非都是偶数');
  </script>
</body>

</html>

练习

html
<!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>练习</title>
</head>

<body>
  <script>
    // 构造员工对象的 构造函数
    function Employee(eid, ename, age, married) {
      this.eid = eid // 员工id
      this.ename = ename
      this.age = age
      this.married = married //婚姻
    }

    var emps = [
      new Employee('0001', '小明', 29, false),
      new Employee('0002', '李四', 32, true),
      new Employee('0003', '俊俊', 36, true),
      new Employee('0004', '楠姐', 19, false),
      new Employee('0005', '小新', 34, true),
    ]

    console.log(emps)

    // 1. 判断是否所有人都已婚
    var res = emps.every((value, index, array) => {
      // value: Employee类型的对象
      return value.married == true
    })

    var res = emps.every(value => value.married)

    console.log(res ? '都已婚' : '非都已婚');

    // 2. 是否所有人年龄都超过20
    var res = emps.every(value => value.age > 20)
  </script>
</body>

</html>

some

html
<!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>some</title>
</head>

<body>
  <script>
    // some: 一些, 至少有一个
    // 只要存在 1个 满足条件的元素, 就算真;  类似逻辑或 ||

    var nums = [21, 3, 34, -54, 65, 34, 43, 6]
    // 判断: 是否存在负数
    var res = nums.some((value, index, array) => {
      return value < 0 // 判断结果: 是否有值是负数
    })

    // 简化
    var res = nums.some(value => value < 0)

    console.log(res ? '存在负数' : '不存在负数')
  </script>
</body>

</html>

练习

js
<!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>练习 11:31</title>
</head>

<body>
  <script>
    function Employee(eid, ename, age, married) {
      this.eid = eid // 员工id
      this.ename = ename
      this.age = age
      this.married = married //婚姻
    }

    var emps = [
      new Employee('0001', '小明', 29, false),
      new Employee('0002', '李四', 32, true),
      new Employee('0003', '俊俊', 36, true),
      new Employee('0004', '楠姐', 19, false),
      new Employee('0005', '小新', 34, true),
    ]

    // 1. 判断是否有人年龄小于20
    var res = emps.some((value, index, array) => {
      return value.age < 20
    })

    var res = emps.some(value => value.age < 20)

    console.log(res ? '有小于20' : '无小于20');

    // 2. 判断是否有人未婚
    var res = emps.some((value, index, array) => {
      return value.married == false
    })
    // 婚姻状态为 假, 属于满足条件,  非假为真
    var res = emps.some(value => !value.married)
    console.log(res ? '有人未婚' : '都已婚')
  </script>
</body>

</html>

filter

html
<!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>filter</title>
</head>

<body>
  <script>
    // filter: 过滤
    // 把数组中满足条件的元素 过滤出来, 形成新的数组
    var nums = [12, 3, 54, 23, 43, 65, 67]
    // 把大于20的元素找出来
    var res = nums.filter((value, index, array) => {
      return value > 20
    })

    var res = nums.filter(value => value > 20)

    console.log(res)
  </script>
</body>

</html>

练习

html
<!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>练习 11:48</title>
</head>

<body>
  <script>
    function Employee(eid, ename, age, married) {
      this.eid = eid // 员工id
      this.ename = ename
      this.age = age
      this.married = married //婚姻
    }

    var emps = [
      new Employee('0001', '小明', 29, false),
      new Employee('0002', '李四', 32, true),
      new Employee('0003', '俊俊', 36, true),
      new Employee('0004', '楠姐', 19, false),
      new Employee('0005', '小新', 34, true),
    ]

    // 1. 找出年龄大于20的所有人
    var res = emps.filter((value, index, array) => {
      return value.age > 20
    })

    var res = emps.filter(value => value.age > 20)

    console.log(res)

    // 2. 找出所有已婚的人
    var res = emps.filter((value, index, array) => {
      return value.married == true
    })

    var res = emps.filter(value => value.married)

    console.log(res)
  </script>
</body>

</html>

map

html
<!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>map</title>
</head>

<body>
  <ul id="box"></ul>

  <script>
    // map: 映射
    // 把数组中的元素 按照规律进行转换, 形成新的数组
    var nums = [1, 2, 3, 4, 5, 6]

    var res = nums.map((value, index, array) => {
      return value * 2
    })

    var res = nums.map(value => value * 2)

    console.log(res)

    var skills = ['html', 'css', 'js', 'dom']

    // 把每个元素放在 li 标签里:   <li>html</li>
    var res = skills.map((value, index, array) => {
      return `<li>${value}</li>`
    })

    var res = skills.map(value => `<li>${value}</li>`)
    // 如何把数组转化/拼接成字符串?  join
    // join的参数, 代表间隔的符号, 默认是 逗号
    console.log(res.join(''))

    box.innerHTML = res.join('')

    console.log(res)
  </script>
</body>

</html>

练习

html
<!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>练习</title>
</head>

<body>
  <div id="box"></div>
  <div id="box1">
    <!-- <a href="" title=""></a> -->
  </div>

  <script>
    var webs = [
      { title: "百度一下", href: "http://www.baidu.com" },
      { title: "京东", href: "http://www.jd.com" },
      { title: "淘宝", href: "http://www.taobao.com" },
      { title: "斗鱼", href: "http://www.douyu.com" },
    ]

    var res = webs.map(value => `<a href="${value.href}" title="${value.title}">${value.title}</a>`)

    box1.innerHTML = res.join('')


    var names = ['小明', '李四', '泡泡', '小新']
    // 要求: 把元素放在 button 标签里, 显示到页面上
    var res = names.map(value => `<button>${value}</button>`)

    box.innerHTML = res.join('')
  </script>
</body>

</html>

练习

html
<!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>练习 14:38</title>

  <style>
    table {
      width: 400px;
      border-collapse: collapse;
    }

    td {
      border: 1px solid gray;
      text-align: center;
      padding: 6px;
    }

    thead {
      background-color: #ccc;
    }
  </style>
</head>

<body>
  <table>
    <thead>
      <tr>
        <td>序号</td>
        <td>id</td>
        <td>姓名</td>
        <td>年龄</td>
        <td>婚姻状态</td>
      </tr>
    </thead>
    <tbody id="box">
      <!-- <tr>
        <td></td>
        <td></td>
        <td></td>
        <td></td>
        <td></td>
      </tr> -->
    </tbody>
  </table>

  <script>
    function Employee(eid, ename, age, married) {
      this.eid = eid // 员工id
      this.ename = ename
      this.age = age
      this.married = married //婚姻
    }

    var emps = [
      new Employee('0001', '小明', 29, false),
      new Employee('0002', '李四', 32, true),
      new Employee('0003', '俊俊', 36, true),
      new Employee('0004', '楠姐', 19, false),
      new Employee('0005', '小新', 34, true),
    ]

    // 数据 -> HTML代码
    var res = emps.map((value, index) => {
      return `<tr>
        <td>${index + 1}</td>
        <td>${value.eid}</td>
        <td>${value.ename}</td>
        <td>${value.age}</td>
        <td>${value.married ? '已婚' : '未婚'}</td>
      </tr>`
    })

    box.innerHTML = res.join('')
  </script>
</body>

</html>

ajax

html
<!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>AJAX 15:15</title>
</head>

<body>
  <ul id="box"></ul>

  <script>
    // AJAX: 在JS中通过网络请求 从服务器获取数据
   
    var url = 'https'

    const xhr = new XMLHttpRequest()
    xhr.open('get', url)
    xhr.onload = function () {
      var data = JSON.parse(xhr.response)
      console.log(data)

      // 把请求到的数组 list, 转为 li 元素;  <li>标题...<li>
      var res = data.data.list.map(value => {
        return `<li>${value.title}</li>`
      })
      console.log(res)

      box.innerHTML = res.join('')
    }
    xhr.send()
  </script>
</body>

</html>

练习

html
<!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>练习 15:34</title>
</head>

<body>
  <div id="box"></div>

  <script>

    // 任务:
    // 1. 利用AJAX 请求接口中的数据
    // 2. 把数据中的内容, 转为 li 标签, 显示出标题
    // 3. 把 li 标签显示到页面上
    var url = 'https:'

    const xhr = new XMLHttpRequest()
    xhr.open('get', url)
    xhr.onload = function () {
      var data = JSON.parse(xhr.response)
      console.log(data)

      var res = data.data.map(value => {
        return `<li>${value.title}</li>`
      })
      console.log(res)

      box.innerHTML = res.join('')
    }
    xhr.send()

    // 步骤跟上一个 几乎一样
  </script>
</body>

</html>

ajax封装

js
// 需求:
// get(地址, 回调函数)

// callback:回调;  简写: cb
function get(url, cb) {
  var xhr = new XMLHttpRequest()
  xhr.open('get', url)
  xhr.onload = function () {
    var data = JSON.parse(xhr.response)

    cb(data) //传递到回调函数中
  }
  xhr.send()
}

练习

html
<!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>练习 15:50</title>

  <!-- 解除防盗链 -->
  <meta name="referrer" content="no-referrer">

  <style>
    #box {
      display: flex;
      flex-wrap: wrap;
      margin: 0;
      padding: 0;
    }

    li {
      list-style: none;
      margin: 0 10px 10px 0;
      box-shadow: 0 0 2px 2px rgba(0, 0, 0, 0.1);
    }

    li>img {
      width: 220px;
      height: 330px;
    }

    li>div {
      padding: 4px;
      text-align: center;
    }

    li>div>span {
      font-size: 0.9em;
      color: orange;
    }
  </style>
</head>

<body>
  <ul id="box">
    <!-- <li>
      <img src="" alt="">
      <div>
        <b></b>
        <span></span>
      </div>
    </li> -->
  </ul>

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

    var url = 'https:'

    get(url, data => {
      console.log(data)

      var res = data.subjects.map(value => {
        return `<li>
          <img src="${value.cover}" alt="">
          <div>
            <b>${value.title}</b>
            <span>${value.rate}</span>
          </div>
        </li>`
      })

      box.innerHTML = res.join('')
    })

    // var xhr = new XMLHttpRequest()
    // xhr.open('get', url)
    // xhr.onload = function () {
    //   var data = JSON.parse(xhr.response)
    //   console.log(data)

    //   var res = data.subjects.map(value => {
    //     return `<li>${value.title}</li>`
    //   })

    //   box.innerHTML = res.join('')
    // }
    // xhr.send()

    // 1. 请求到接口中的数据
    // 2. 转为 li, 内容为 题目 title
    // 3. 显示到页面上
  </script>
</body>

</html>
html
<!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>练习 16:47</title>

  <style>
    #box {
      margin: 0;
      padding: 0;
      display: flex;
      flex-wrap: wrap;
    }

    li {
      list-style: none;
      margin: 0 10px 10px 0;
      box-shadow: 0 0 2px 2px rgba(0, 0, 0, 0.1);
      display: flex;
      flex-direction: column;
      background-color: #14151A;
      padding: 10px;
      border-radius: 4px;
      align-items: center;
    }

    li>b {
      color: white;
      padding: 10px 0;
    }

    li>img {
      width: 100px;
      height: 100px;
    }

    li>span {
      color: #AB934D;
    }
  </style>
</head>

<body>
  <ul id="box">
    <!-- <li>
      <img src="" alt="">
      <b></b>
      <span></span>
    </li> -->
  </ul>

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

    get('https://', data => {
      console.log(data)

      var res = data.items.map(value => {
        return `<li>
          <img src="${value.iconPath}" alt="">
          <b>${value.name}</b>
          <span>${value.price}</span>
        </li>`
      })

      box.innerHTML = res.join('')
    })

    // 1. 利用 common.js 中封装的 get 方法获取数据
    // 2. 在html中书写 ul#box 标签 和 li标签模板
    // 3. 在 js 中, 把请求到的数据 转为 li 代码
    // 4. 把 li 代码显示到 box 标签里
    // 5. 利用CSS进行美化

    // 此接口的图没有防盗链问题
  </script>
</body>

</html>
html
<!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>练习 17:29</title>

  <meta name="referrer" content="no-referrer">

  <style>
    #box {
      display: flex;
      flex-wrap: wrap;
      padding: 0;
      margin: 0;
    }

    li {
      margin: 0 10px 10px 0;
      box-shadow: 0 0 2px 2px rgba(0, 0, 0, 0.1);
      list-style: none;
      display: flex;
      flex-direction: column;
      width: 250px;
    }

    li>b {
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
      padding: 5px 0;
    }

    li>div {
      display: flex;
    }

    li>div>span {
      width: 50%;
    }

    li>img {
      width: 100%;
      height: 150px;
    }
  </style>
</head>

<body>
  <ul id="box">
    <!-- <li>
      <img src="" alt="">
      <b></b>
      <div>
        <span></span>
        <span></span>
      </div>
    </li> -->
  </ul>

  <script src="common.js"></script>
  <script>
    get('', data => {
      console.log(data)

      var res = data.data.archives.map(value => {
        return `<li>
          <img src="${value.pic}" alt="">
          <b>${value.title}</b>
          <div>
            <span>${value.stat.view}</span>
            <span>${value.stat.danmaku}</span>
          </div>
        </li>`
      })

      box.innerHTML = res.join('')
    })


    // 1. 请求接口中的数据
    // 2. html中完成 ul 和 li的代码
    // 3. 数据转html
    // 4. 把 html 添加到页面上
    //  -- 图片有防盗链,需要解除
    // 5. css美化
  </script>
</body>

</html>

总结

  • 模板字符串
    • 支持文本的换行, 特别适合 在JS中书写带有格式的 html 代码
    • 更好的字符串拼接方案 : ${}
  • 箭头函数
    • 格式更简单的匿名函数语法
    • 带有两个语法糖
      • 形参只有一个 省略()
      • 函数体只有一行 省略
    • this
      • 没有this, 通过作用域链 使用上层作用域的
  • 数组高阶函数
    • 高阶函数: 函数中使用了其他函数, 就叫高阶函数
    • every : 判断每个元素都符合条件
    • some : 判断至少一个元素符合条件
    • filter : 把满足条件的元素过滤出来
    • map : 映射. 把每个元素 处理后的返回值, 组合成新的数组

回顾

  • 模板字符串

    • 使用 反引号 `` 书写
    • 特点
      • 支持换行: 允许在JS中书写 HTML 代码时, 可以带有格式, 更易读
      • 更好的字符串拼接方案: ${} 即可
  • 箭头函数

    • 更简单的匿名函数写法: ()=>{}
    • 语法糖
      • 形参只有一个, 省略()
      • 函数体只有一行, 省略 {return }
  • 其他语法糖

    • 对象类型: {属性名: 变量名} 一旦两者名称相同, 可以合写 { 属性名 }

    • 对象中的函数类型的属性, 可以省略 :function

      js
      var obj = {
          show : function(){},
          
          show(){}
      }
  • 数组高阶函数

    • 高阶: 函数中 使用了 其他函数
    • every: 每一个元素都符合条件
    • some: 至少有一个元素符合条件
    • filter: 把满足条件的元素过滤出来
    • map: 映射. 把数组中的每个元素 修改后, 组成新的数组

forEach

html
<!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>forEach 09:07</title>
</head>

<body>
  <script>
    // 数组的遍历操作: 4种方案
    var names = ['mike', 'lucy', 'lily', 'john']

    // 1. for
    for (let i = 0; i < names.length; i++) {
      console.log(i, names[i]);
    }

    // 2. for..in : 用于遍历对象类型, key是属性名, 字符串类型
    // 由于数组属于对象类型的一种, 所以可以用for..in遍历. 但是并不合适
    for (const key in names) {
      console.log(key, names[key])
    }

    // 3. for..of   来自 ES6
    // 特色: 直接遍历值;    
    // 区别for..in. 先拿属性名 再通过属性名拿值
    // for..of 更加直接
    for (const value of names) {
      console.log(value)
    }

    // 4. forEach   来自 ES6
    // 没有返回值, 只是存粹的遍历数组
    names.forEach((value, index, array) => {
      console.log(index, value)
    })

    ////////////////////////////
    // 取舍问题:
    // 通常对数组采用 forEach 方案
    // 对 伪数组 / 类(似)数组 采用 for..of 方案

    function show() {
      // 伪数组/类数组: 内容与数组的结构相同, 但是原型不是数组的
      console.log(arguments)
      // 无法使用forEach
      // arguments.forEach()

      // for..of 可以遍历
      for (const value of arguments) {
        console.log(value)
      }

      // 另一种方案: 把其原型替换成数组的prototype
      Object.setPrototypeOf(arguments, Array.prototype)
      arguments.forEach((value, index) => {
        console.log(index, value)
      })
    }

    show(11, 22, 333, 44, 55, 66)
  </script>
</body>

</html>

练习

html
<!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>练习 09:36</title>
</head>

<body>
  <div id="box"></div>

  <script>
    var nums = [12, 4, 34, 54, 67, 76, 8, 43]

    // 要求: 分别用 for-of 和 forEach 方式, 计算出数组中元素总和
    var total = 0

    for (const value of nums) {
      total += value
    }
    console.log(total)

    total = 0
    nums.forEach(value => total += value)
    console.log(total)

    ////////////////////////////////
    // 结合 html
    var skills = ['html', 'css', 'js', 'dom']
    // 把元素转化成 button 标签, 拼接到一起形成字符串
    // 昨天: 先用map映射, 再用 join 拼接

    var template = '' //模板
    skills.forEach(value => template += `<button>${value}</button>`)

    console.log(template);

    box.innerHTML = template
  </script>
</body>

</html>

练习

html
<!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>练习 10:06</title>

  <meta name="referrer" content="no-referrer">

  <style>
    #box {
      width: 600px;
    }

    li {
      display: flex;
      list-style: none;
      margin-bottom: 10px;
    }

    li>img {
      width: 160px;
      border-radius: 4px;
      margin-right: 10px;
    }

    li>div {
      display: flex;
      flex-direction: column;
      justify-content: space-between;
    }

    li>div>div {
      color: #888;
    }
  </style>
</head>

<body>
  <ul id="box">
    <!-- <li>
      <img src="" alt="">
      <div>
        <b></b>
        <div>
          <div></div>
          <div></div>
        </div>
      </div>
    </li> -->
  </ul>

  <script src="common.js"></script>
  <script>
    // 万的转换
    function wan(value) {
      return value >= 10000 ? (value / 10000).toFixed(1) + "万" : value
    }


    var url = 'https://api.kunkun.cool/bilibili/recommend.json'

    get(url, data => {
      console.log(data)

      var tem = ''

      data.data.season.forEach(value => {
        // 问题点: 在 模板字符串中 书写过多的 过长的 JS代码, 会导致 html的结构被打乱, 不容易阅读!
        // 先把用到的变量都解构出来
        const {
          title,
          new_ep: { cover, index_show },
          stat: { view, danmaku }
        } = value

        tem += `<li>
          <img src="${cover}" alt="">
          <div>
            <b>${title}</b>
            <div>
              <div>${index_show}</div>
              <div>${wan(view)}播放 · ${wan(danmaku)}弹幕</div>
            </div>
          </div>
        </li>`
      })

      box.innerHTML = tem
    })
  </script>
</body>

</html>

练习

html
<!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>练习 10:34</title>

  <meta name="referrer" content="no-referrer">

  <style>
    #box {
      display: flex;
      flex-wrap: wrap;
      padding: 0;
      margin: 0;
    }

    li {
      display: flex;
      width: 250px;
      list-style: none;
      margin: 0 10px 10px 0;
      box-shadow: 0 0 2px 2px rgba(0, 0, 0, 0.1);
    }

    li>img {
      width: 100px;
      height: 100px;
    }

    li>div {
      display: flex;
      flex-direction: column;
      justify-content: space-between;
      padding: 10px 5px;
    }

    li>div>div>span {
      display: inline-block;
      padding: 2px 10px;
      background-color: pink;
      border-radius: 40px;
    }
  </style>
</head>

<body>
  <ul id="box">
    <!-- <li>
      <img src="" alt="">
      <div>
        <b></b>
        <div>
          更新至 <span></span>
        </div>
      </div>
    </li> -->
  </ul>

  <script src="common.js"></script>
  <script>
    // 用 forEach 方式实现
    var url = 'https://api.kunkun.cool/bilibili/timeline.json'

    get(url, data => {
      console.log(data)

      var tem = ''

      data.result.map(value => {
        tem += `<li>
          <img src="${value.square_cover}" alt="">
          <div>
            <b>${value.title}</b>
            <div>
              更新至 <span>${value.bgmcount}</span>
            </div>
          </div>
        </li>`
      })

      box.innerHTML = tem
    })
  </script>
</body>

</html>

reduce

html
<!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>reduce 11:10</title>
</head>

<body>
  <div id="box"></div>

  <script>
    // reduce: 减少, 合并
    // 把数组中的元素 经过处理之后, 合并成 1个值
    var nums = [1, 2, 3, 4, 5, 6, 7, 8]

    // 任务: 把数组的元素 累加到一起, 得到总和
    // 参数1: 每个元素都被此函数处理, 其中 box 形参, 接收的是 当前的总和 
    // 参数2: 初始时的 总和
    var res = nums.reduce((box, value, index, array) => {
      return box + value
    }, 0)

    console.log(res)

    /////////////////////////////
    var skills = ['js', 'dom', 'html', 'css']
    // 把元素转为按钮标签, 拼接到一起
    var res = skills.reduce((box, value) => {
      return box + `<button>${value}</button>`
    }, '')

    console.log(res)

    box.innerHTML = res
  </script>
</body>

</html>

练习

html
<!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>练习 11:34</title>
</head>

<body>
  <ul id="box"></ul>

  <script src="common.js"></script>
  <script>
    var url = 'https://douyu.kunkun.cool/api/cate/recList'
    get(url, data => {
      console.log(data)

      var res = data.data.reduce((box, value) => {
        return box + `<li>${value.name}</li>`
      }, '')

      box.innerHTML = res
    })

    // 把请求到的数据 中的 name 属性, 放到 li 标签中, 进行展示
    // 采用 reduce 方案实现   
  </script>
</body>

</html>

练习

html
<!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>练习 11:49</title>
</head>

<body>
  <script>
    // https://douyu.kunkun.cool/api/room/list?page=1&type=ms


  </script>
</body>

</html>

练习

html
<!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>练习 11:49</title>

  <style>
    #box {
      display: flex;
      flex-wrap: wrap;
      padding: 0;
      margin: 0;
    }

    li {
      margin: 0 10px 10px 0;
      list-style: none;
    }
  </style>
</head>

<body>
  <ul id="box">
    <!-- <li>
      <img src="" alt="">
      <div></div>
    </li> -->
  </ul>

  <script src="common.js"></script>
  <script>
    var url = "https://douyu.kunkun.cool/api/room/list?page=1&type=ms"

    get(url, data => {
      console.log(data)

      var res = data.data.list.reduce((box, value) => {
        const { roomSrc, roomName } = value

        return box + `<li>
          <img src="${roomSrc}" alt="">
          <div>${roomName}</div>
        </li>`
      }, '')

      box.innerHTML = res
    })
  </script>
</body>

</html>

数组数据转HTML代码的方案

  • map 搭配 join 使用
    • 缺点: 用map和join 两个函数完成任务
  • forEach 搭配 外部的变量
    • 缺点: 需要额外的外部变量
  • reduce
    • 缺点: 理解上有难度, 使用少

展开语法

html
<!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>展开语法 14:06</title>
</head>

<body>
  <script>
    var nums = [12, 43, 435, 12, 43, 6, 67, 45]

    // ES6 推出了 展开运算符: ...
    // 可以把 数组/对象  的内容提取出来
    console.log(
      Math.max(...nums),
      // 下方两种写法是等效的
      Math.max(...[11, 22, 33]),
      // ... 和 [] 会抵消掉
      Math.max(11, 22, 33)
    );


    console.log(
      // apply: 把数组转为参数列表 来触发函数
      Math.max.apply(0, nums),
      Math.min.apply(null, nums)
    );

    ////////////////////////////////////
    // 利用展开语法可以更形象的进行 对象的合并操作
    var x = [11, 22]
    var y = [33, 44]

    var z = [...x, ...y, 55, 66]

    console.log(z)

    ////////////////////////////
    var m = { x: 10, y: 20 }
    var n = { y: 400, z: 600 }

    var q = { ...m, ...n, b: 1000 }
    console.log(q)
  </script>
</body>

</html>

剩余参数

html
<!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>剩余参数 14:22</title>
</head>

<body>
  <script>
    // ... 在形参中书写, 代表剩余参数
    // 没有形参接收的 实参, 都会存储在 剩余参数 这个 数组类型的变量里
    // 和 arguments 比较相似, 但是 arguments 是存储所有实参.
    function show(x, y, ...z) {
      console.log(x, y, z)
    }

    show(11, 22, 33, 44, 55, 66)
  </script>
</body>

</html>

解构语法

html
<!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>解构语法 14:32</title>
</head>

<body>
  <script>
    var emps = [
      { ename: "小明", age: 32, phone: '13599898888' },
      { ename: "张三", age: 22, phone: '13544498888' },
      { ename: "李四", age: 33, phone: '13599333888' },
      { ename: "俊俊", age: 36, phone: '13599777888' },
    ]

    // 可选解构:
    const [kai1, , , liang] = emps
    console.log(kai1.ename, liang.ename);

    // ES6的 解构语法
    const [kaikai, nannan] = emps
    console.log(kaikai.ename, nannan.ename);

    // 缺点: 把值赋给变量时, 格式略长
    const kai = emps[0], nan = emps[1]
    console.log(kai.ename, nan.ename)

    // 缺点: 无法直观的知道 0 和 1 是什么
    console.log(emps[0].ename, emps[1].ename)


  </script>
</body>

</html>

复杂解构

html
<!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>对象的解构 14:45</title>
</head>

<body>
  <script>
    var emp = {
      ename: "小明", age: 32, phone: "18879798888"
    }

    // 别名: 换个名字
    // { 属性名: 别名 }
    var { ename: en } = emp
    console.log('en:', en)

    // 解构
    var { ename, age, phone } = emp
    console.log(ename, age, phone)

    // 期望:
    var ename = emp.ename
    var age = emp.age
    var phone = emp.phone

    ///////////////////////////////////////////
    // 复杂的解构
    var paopao = {
      name: "皮喆",
      age: 18,
      tags: ['html', 'css', 'js'],
      husband: {
        name: "大连",
        age: 26
      }
    }

    // 技巧: ctrl + i 可以弹出提示
    var {
      name,
      age,
      husband: { age: hus_age, name: hus_name },
      tags: [tag1, tag2, tag3]
    } = paopao

    console.log(name, age, hus_age, hus_name, tag1, tag2, tag3);
  </script>
</body>

</html>

练习

html
<!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>练习 15:17</title>
</head>

<body>
  <script>
    var emp = {
      name: "小明", age: 32,
      desc: {
        home: "黑龙江鹤岗",
        phone: "18878789999",
        skills: ['html', 'css', 'js', 'vue', 'react'],
        wife: {
          name: "王二", age: 27,
          tags: ['宅', '时尚']
        }
      }
    }

    var { age, desc: {
      home, phone, skills: [
        skill1, skill2, , , skill3
      ], wife: { age: wife_age, name: wife_name, tags: [tag1, tag2] }
    }, name } = emp

    console.log(age, name, wife_name, wife_age, skill1, skill2, skill3, tag1, tag2);
  </script>
</body>

</html>

形参解构

html
<!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>形参解构</title>
</head>

<body>
  <script>
    var r1 = { length: 10, width: 20 }

    // 省略形参名, 直接把 实参中的值解构出来使用
    function area({ length, width }) {
      // const { length, width } = rect
      return length * width
      // return rect.length * rect.width
    }

    console.log(
      area(r1)
    )

    ////////////////////////////////////
    // 练习
    var c1 = { length: 10, width: 20, height: 30 }

    // 计算面积 =(长x宽 + 长x高 + 宽x高)*2
    function area({ length: l, width: w, height: h }) {
      return 2 * (l * w + l * h + w * h)
      // return 2 * (length * width + length * height + width * height)
    }

    //期望用法:
    console.log(
      area(c1)
    );
  </script>
</body>

</html>

dom操作初体验

html
<!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: 属于固有的标签 -->
  <title>dom 17:20</title>

  <style>
    h1 {
      color: green;
    }
  </style>
</head>

<body>
  <!-- 属于自定义标签 -->
  <h1>Hello World!</h1>

  <script>
    console.log(window) // 找到 document 属性
    // log: 会美化输出的结果, 让其更容易阅读
    console.log(document.body)
    console.log(document.head)
    console.log(document)

    // dir: 直接输出本体
    console.dir(document.body)

    // JS特点: 能够实现更强大的 带有逻辑性的操作

    // 制作一个时钟效果:
    // 通过定时器, 每隔1秒中, 就修改页面上的某个元素
    setInterval(() => {
      var now = new Date().toLocaleTimeString()

      // document.title : 就可以操作 head 中的 title 标签的内容
      document.title = now

      // 同css, js也提供类似选择器的方案, 通过标签名来查找某些标签

      //get:查找,获取    elements: 元素们    by:通过   tag:标签  name:名字
      const h1s = document.getElementsByTagName('h1')
      console.log('h1s:', h1s)
      // 修改 找到的第一个 h1 标签的 内容
      h1s[0].innerHTML = now

      console.log('now:', now)
    }, 1000) // 1000毫秒 == 1秒
  </script>
</body>

</html>

练习

html
<!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>练习 17:48</title>
</head>

<body>
  <p></p>

  <script>
    // 在 p 标签中, 实时显示当前时间
    setInterval(() => {
      const now = new Date().toLocaleTimeString()
      // 目标: 把 now 放到 p 标签的内容中显示
      // 1. 先找到p标签
      const ps = document.getElementsByTagName('p')
      console.log(ps)

      ps[0].innerHTML = now
    }, 1000);
  </script>
</body>

</html>

事件

html
<!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>元素的事件 09:06</title>
</head>

<body>
  <!-- 事件: 在元素上触发的一些事情, 能够触发的事件都被系统提前规定好 -->
  <!-- https://www.runoob.com/jsref/dom-obj-event.html -->

  <button id="btn1" onclick="alert('Hello!')">Click Me!</button>

  <!-- 尝试通过 DOM方式, 给这个按钮添加事件 -->
  <button id="btn2">Hello!</button>

  <button id="btn3">点我打印 666</button>

  <script>
    const btn3 = document.getElementById('btn3')
    btn3.onclick = function () {
      alert("666")
    }

    // 1. 先查询到 btn2 元素
    const btn2 = document.getElementById('btn2')
    // 2. 为元素的onclick属性赋值
    btn2.onclick = function () {
      alert('DOM!!')
    }

    console.dir(btn2) // 查看onclick属性



    // id: 唯一标识;  说明理论上 只有一个 满足条件的元素
    // 所以 : getElementById 的结果是 元素本身

    // 区分: 昨天的根据标签名查找 tagName
    // 页面中可以存在多个 相同的标签, 例如 很多个 li.  所以查询结果是类数组

    // 从document中, 查找 id=btn1 的元素
    const btn1 = document.getElementById('btn1')

    console.log('btn1:', btn1)
    // 所有 on 开头的属性, 都是事件相关的
    console.dir(btn1)
  </script>
</body>

</html>

style操作

html
<!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>style 09:35</title>
</head>

<body>
  <!-- 1个页面元素的样式, 由哪些因素决定? -->
  <!-- 1. 原本本身的样式:  w3c的标准规定 -->
  <!-- 2. 浏览器品牌 的 个性化美化 -->
  <!-- 3. class: 用户通过 class方案 -->
  <!-- 4. style: 内联样式的个性化 -->
  <button id="btn1">Click Me!</button>

  <div id="box" style="width: 100px;height: 100px; background-color: gray;"></div>

  <script>
    // 要求: box 被点击后, 背景色变为 红色red

    const box = document.getElementById('box')
    box.onclick = function () {
      // JS规范: 属性名不允许带有 -, 所以默认会以 小驼峰的形式出现
      this.style.backgroundColor = 'red'
      console.log(this.style)
    }


    // 1. 从document中查找到按钮元素
    const btn1 = document.getElementById('btn1')
    console.dir(btn1) // 找到 style 属性
    // 内联样式的优先级是最高的, 当这里赋值时, 会覆盖 前三种因素的效果
    btn1.style.color = 'green'

    // 与事件操作结合: 点击后, 让按钮的文字变红
    btn1.onclick = function () {
      // onclick属性 属于 btn1, 触发时 是btn1 触发的, 所以this是btn1
      this.style.color = 'red'
    }
  </script>
</body>

</html>

练习

html
<!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>练习 10:13</title>
</head>

<body>
  <ul>
    <li>DOM</li>
    <li>HTML</li>
    <li>CSS</li>
    <li>JS</li>
  </ul>

  <script>
    // 需求: 点击li 让其变为红色

    // 问题: 要操作的元素个数>1个, 即多个.  考虑批量修改的方案

    // 利用 标签选择方案, 查询到所有的li
    const lis = document.getElementsByTagName('li')
    console.log(lis)

    // 用 for ... of 遍历这个伪数组
    for (const li of lis) {
      li.onclick = function () {
        // this: 此变量非常灵活, 需要运行时才知道代表什么
        // 所以: vscode 静态分析代码时 不知道this是什么, 所以无法提供准确的提示
        li.style.color = 'red'
      }
    }
  </script>
</body>

</html>

querySelectorAll

html
<!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>选择器方案查找元素 10:27</title>

  <style>
    #girl>li {
      border: 1px solid blue;
    }
  </style>
</head>

<body>
  <ul id="boy">
    <li>富贵</li>
    <li>王五</li>
    <li>亮亮</li>
  </ul>

  <ul id="girl">
    <li>楠楠</li>
    <li>泡泡</li>
    <li>梦瑶</li>
  </ul>

  <script>
    // querySelectorAll
    // query:查询  selector:选择器  all:所有
    // 通过选择器查找所有符合的元素
    // qsa
    const girl_lis = document.querySelectorAll('#girl>li')
    console.log(girl_lis)
    // 此方式查询的结果, 是类数组类型, 但是其原型 NodeList中存在forEach方法可以实现遍历操作
    girl_lis.forEach(li => {
      li.onclick = function () {
        this.style.color = 'pink'
      }
    })

    // document: 整个html代码

    // 要想精确查找: 先找 id=boy 的元素
    const boy = document.getElementById('boy')
    // 再从boy里面查, 实现元素的小范围查找
    const boy_lis = boy.getElementsByTagName('li')
    console.log(boy_lis)

    for (const li of boy_lis) {
      li.onclick = function () {
        li.style.color = 'blue'
      }
    }
  </script>
</body>

</html>

练习

html
<!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>练习 10:50</title>
</head>

<body>
  <ul id="box1">
    <li>HTML</li>
    <li>CSS</li>
    <li>JS</li>
  </ul>

  <ul id="box2">
    <li>Node.js</li>
    <li>Express</li>
    <li>SQL</li>
  </ul>

  <ul id="box3">
    <li>DOM</li>
    <li>jQuery</li>
    <li>HTML5</li>
  </ul>

  <script>
    const box1_lis = document.querySelectorAll('#box1>li')

    box1_lis.forEach(li => li.onclick = function () {
      this.style.color = 'red'
    })
  </script>
</body>

</html>

class

html
<!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>class操作 11:17</title>

  <link rel="stylesheet" href="reset.css">

  <style>
    li {
      padding: 5px 10px;
      background-color: #ccc;
      border-radius: 4px;
      display: inline-block;
      cursor: pointer;
    }

    li:hover {
      box-shadow: 0 0 2px 2px rgba(0, 0, 0, 0.1);
    }

    /* <li class="active" */
    li.active {
      background-color: orangered;
      color: white;
    }
  </style>
</head>

<body>
  <h2>点击选择午餐:</h2>
  <ul>
    <li>KFC</li>
    <li>牛肉面</li>
    <li>麻辣烫</li>
    <li>黄焖鸡</li>
    <li>螺蛳粉</li>
  </ul>
  <script>
    // 需求: 点击 li 之后, 把他的 class="active"
    const lis = document.querySelectorAll('li')
    // 遍历查找到的元素们, 挨个绑定 点击事件
    lis.forEach(li => li.onclick = function () {
      // 在JS中, class 是关键词, 不能作为属性名
      // 改为 className

      console.dir(this) //找到 和 class 有关的属性
      // 切换效果:  有->无;  无->有;
      if (this.className == '') {
        this.className = 'active'
      } else {
        this.className = ''
      }
    })
  </script>
</body>

</html>

练习

html
<!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>练习 11:44</title>

  <link rel="stylesheet" href="reset.css">
  <style>
    ul {
      display: flex;
    }

    li {
      padding: 5px 15px;
      background-color: #eee;
      border-radius: 4px;
      margin: 4px;
      cursor: pointer;
    }

    li.suibian {
      background-color: orangered;
      color: white;
    }
  </style>
</head>

<body>
  <h2>勾选你的技术栈:</h2>
  <ul>
    <li>HTML</li>
    <li>CSS</li>
    <li>JS</li>
    <li>DOM</li>
    <li>Vue</li>
    <li>Node.js</li>
  </ul>

  <script>
    const lis = document.querySelectorAll('li')

    lis.forEach(li => li.onclick = function () {
      if (li.className == '') {
        li.className = 'suibian'
      } else {
        li.className = ''
      }
    })
  </script>
</body>

</html>

开关

html
<!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>switch 14:03</title>

  <style>
    .switch {
      border: 2px solid #aaa;
      background-color: #f6f6f6;
      display: inline-block;
      width: 100px;
      display: flex;
      cursor: pointer;
      padding: 2px;

      transition: 0.3s;
    }

    .switch>span {
      transition: 0.3s;
      width: 50px;
      height: 40px;
      background-color: #666;
    }

    .switch.open {
      background-color: #18832c;
    }

    .switch.open>span {
      transform: translateX(50px);
      background-color: orange;
      border-radius: 4px;
    }
  </style>
</head>

<body>
  <div class="switch open">
    <span></span>
  </div>

  <script>

    // querySelector: 直接查询到元素本身
    // 使用场景: 我们明确知晓要查询的元素只有1个时, 采用此方案更合理
    var my_switch = document.querySelector('.switch')
    console.log('my_switch:', my_switch);

    //1. 找到 .switch 元素
    var switchs = document.querySelectorAll('.switch')
    console.log(switchs)

    //2. 为其添加点击事件
    my_switch.onclick = function () {
      console.dir(this) // classList

      // classList 是通过构造函数创建出的对象, 属于对 原始的className 的封装
      // 提供强大的辅助功能

      // toggle: 开关, 切换
      // 自动判断目标样式是否存在, 实现切换效果
      this.classList.toggle('open')
      // if (this.className == "switch") {
      //   this.className = 'switch open'
      // } else {
      //   this.className = 'switch'
      // }
    }

    //3. 点击时判断其 class 的值, 来决定 open 样式的添加与否

  </script>
</body>

</html>

练习

html
<!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>练习 14:44</title>

  <style>
    #box {
      width: 200px;
      height: 200px;
      background-color: gray;
      transition: 0.3s;
    }

    #box.open {
      border-radius: 50%;
      background-color: orange;
    }
  </style>
</head>

<body>
  <div id="box"></div>

  <script>
    // 1. 预判 id=box的元素只有一个
    const box = document.querySelector('#box')

    box.onclick = function () {
      this.classList.toggle('open')
    }
  </script>
</body>

</html>

唯一性激活效果

html
<!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>练习 15:11</title>
  <link rel="stylesheet" href="reset.css">
  <style>
    ul {
      width: 200px;
    }

    li {
      padding: 10px;
      background-color: #eee;
      margin-bottom: 4px;
      transition: 0.3s;
    }

    li.active {
      box-shadow: 0 0 2px 2px rgba(50, 150, 200, 0.2);
      padding-left: 30px;
      background-color: orange;
      color: white;
    }
  </style>
</head>

<body>
  <ul>
    <!-- active: 激活, 代表此项目默认选中状态 -->
    <li class="active">阶段1: 皮喆</li>
    <li>阶段2: 俊俊</li>
    <li>阶段3: 小新</li>
    <li class="xx yy">阶段4: 李四</li>
    <li>阶段5: 文华</li>
  </ul>

  <script>
    // 1. 先查找你操作的元素
    const lis = document.querySelectorAll('li')

    lis.forEach(li => li.onclick = function () {
      // 唯一性激活效果:
      // 先删除之前 激活的元素, 再激活新的
      // 预判: 只有1个处于激活状态, qs
      const li_active = document.querySelector('li.active')
      // 采用 classList 提供的 remove 方法, 删除指定的样式类
      li_active.classList.remove('active')

      // = : 会导致覆盖操作, 覆盖原有的值. 此写法存在风险.
      // this.className = 'active'

      // classList中提供了 add 方案, 会保留原有的class 然后添加新的
      this.classList.add('active')
    })
  </script>
</body>

</html>
html
<!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>练习 15:38</title>

  <link rel="stylesheet" href="reset.css">
  <style>
    ul {
      display: flex;
      background-color: #002c69;
      color: white;
      padding: 0 40px;
    }

    li {
      padding: 10px 35px;
      cursor: pointer;
    }

    li.active {
      background-color: orange;
    }
  </style>
</head>

<body>
  <ul>
    <li class="active">首页</li>
    <li>关于净美仕</li>
    <li>公司动态</li>
    <li>产品中心</li>
    <li>联系我们</li>
  </ul>

  <script>
    const lis = document.querySelectorAll('li')

    lis.forEach(li => li.onclick = function () {
      const li_active = document.querySelector('li.active')
      li_active.classList.remove('active')

      this.classList.add('active')
    })
  </script>
</body>

</html>
html
<!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>练习 16:11</title>

  <link rel="stylesheet" href="reset.css">
  <style>
    ul {
      display: flex;
      background-color: #f2f5f6;
      padding: 50px;
    }

    li {
      border: 1px solid #bbb;
      color: #333;
      background-color: white;
      padding: 5px 15px;
      margin: 5px;
      border-radius: 100px;
      cursor: pointer;
    }

    li:hover {
      border-color: #FF5D23;
      color: #FF5D23;
    }

    li.active {
      background-color: #FF5D23;
      color: white;
      border-color: #FF5D23;
    }
  </style>
</head>

<body>
  <ul>
    <li class="active">网游竞技</li>
    <li>单机游戏</li>
    <li>手游休闲</li>
    <li>娱乐天地</li>
    <li>颜值</li>
  </ul>

  <script>
    const lis = document.querySelectorAll('li')

    lis.forEach(li => li.onclick = function () {
      // 在整个页面上的所有元素中, 找到符合条件的: 查询会消耗一定的性能
      // const li_active = document.querySelector('li.active')
      // li_active.classList.remove('active')

      // 简单粗暴的做法: 遍历之前查到的每个li 都删除一次激活状态
      lis.forEach(li => li.classList.remove('active'))

      li.classList.add('active')
    })
  </script>
</body>

</html>
html
<!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>练习 16:41</title>

  <style>
    #box {
      display: flex;
    }

    #box>img {
      width: 140px;
      height: 140px;
      border-radius: 4px;
      margin: 4px;
      /* 原始比例 和 显示比例不同, 存在适配问题 */
      /* cover: 覆盖;  保持原有比例, 充满整个显示的区域 */
      object-fit: cover;

      transition: 0.5s;
    }

    #box>img.active {
      width: 305px;
    }
  </style>
</head>

<body>
  <div id="box">
    <img class="active" src="./imgs/bigskin-1.jpg" alt="">
    <img src="./imgs/bigskin-2.jpg" alt="">
    <img src="./imgs/bigskin-3.jpg" alt="">
    <img src="./imgs/bigskin-4.jpg" alt="">
    <img src="./imgs/bigskin-5.jpg" alt="">
  </div>

  <script>
    const imgs = document.querySelectorAll('#box>img')
    // onmouseover : 鼠标移动到某元素上时触发
    imgs.forEach(img => img.onmouseover = function () {
      imgs.forEach(img => img.classList.remove('active'))

      img.classList.add('active')
    })
  </script>
</body>

</html>
html
<!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>练习 17:16</title>

  <link rel="stylesheet" href="reset.css">
  <style>
    ul {
      display: flex;
      background-color: #0a0e10;
      padding: 40px;
    }

    li {
      margin: 10px;
      color: white;
      display: flex;
      flex-direction: column;
      align-items: flex-end;
    }

    li>img {
      width: 90px;
      margin-bottom: 5px;
      border: 5px solid #7a7a7a;
      border-radius: 0 14px;
    }

    li.active>img {
      border-color: orange;
    }
  </style>
</head>

<body>
  <ul>
    <li class="active">
      <img src="./imgs/smallskin-5.jpg" alt="">
      <span>时之愿境</span>
    </li>
    <li>
      <img src="./imgs/smallskin-4.jpg" alt="">
      <span>时之祈愿</span>
    </li>
    <li>
      <img src="./imgs/smallskin-3.jpg" alt="">
      <span>遇见神鹿</span>
    </li>
    <li>
      <img src="./imgs/smallskin-2.jpg" alt="">
      <span>森&nbsp;&nbsp;&nbsp;&nbsp;</span>
    </li>
    <li>
      <img src="./imgs/smallskin-1.jpg" alt="">
      <span>鹿灵守心</span>
    </li>
  </ul>

  <script>
    const lis = document.querySelectorAll("li")

    lis.forEach(li => li.onmouseover = function () {
      lis.forEach(li => li.classList.remove('active'))

      li.classList.add('active')
    })
  </script>
</body>

</html>

广告弹窗

html
<!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>广告弹窗效果 17:37</title>

  <link rel="stylesheet" href="reset.css">
  <style>
    #ad {
      padding: 10px;
      background-color: #eee;
      width: 200px;
      position: fixed;
      bottom: 0;
      right: 0;
      transform: translateY(100%);
      transition: 0.4s;
    }

    #ad.active {
      transform: translateY(0)
    }
  </style>
</head>

<body>
  <div id="ad">
    <button>关闭</button>
    <p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Quo dignissimos, asperiores sit velit aut molestias quas
      placeat, autem alias corporis voluptates culpa nulla at ea sed illo ullam, nemo exercitationem?</p>
  </div>

  <script>
    setTimeout(() => {
      const ad = document.getElementById('ad')

      ad.classList.add('active')
    }, 3000)

    // 明确知晓: 关闭按钮只有一个
    const btn_close = document.querySelector('#ad>button')
    btn_close.onclick = function () {
      const ad = document.getElementById('ad')
      ad.classList.remove('active')
    }
  </script>
</body>

</html>

回顾

什么是DOM?

  • Document Object Model: 文档对象模型
  • HTML文档 被浏览器转为 document 对象, 然后再渲染到页面上

学习DOM能做什么?

  • 学习 document 对象的用法
  • 可以通过 JS 来操作页面上的内容
  • 由于 JS 语言可以书写复杂的逻辑操作, 可以丰富页面的交互

如何查找要操作的元素

  • 通过元素的特征
    • id: 唯一标识; document.getElementById 结果是元素本身
    • tagName: 标签名; document.getElementsByTagName 结果是多个元素组成的类数组
      • 通常使用for...of遍历使用
  • 通过css选择器查找
    • 单个元素: document.querySelector 结果是元素
    • 多个元素: document.querySelectorAll 结果是所有元素组成的类数组
      • 通过使用 forEach 遍历使用

操作元素的属性:

  • 事件相关: 由浏览器规定的一系列事件
    • 这些属性都以on开头
      • 鼠标点击: onclick
      • 鼠标悬浮: onmouseover
  • 样式相关
    • style: 内联样式. 优先级最高
    • class: 样式类
      • className: 本体. 一个字符串类型的值
      • classList: 通过构造函数生成的一个对象, 提供了操作 className 的一系列方法
        • toggle: 切换
        • add: 添加
        • remove: 删除

属性操作

html
<!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>属性操作 09:15</title>
</head>

<body>
  <!-- 1个元素上, 自带很多属性 -->
  <!-- 系统属性: 官方默认提供的, 每个属性都有自己的作用 -->
  <!-- 自定义属性: 添加一些自己的属性, 有两种添加方式 -->
  <!-- 1. 旧方案, 不规范: 随便写, 需要用固定方法来操作 -->
  <!-- 2. 新方案: 用 data- 做开头, 存储在 dataset 属性中 -->
  <a data-x="11" data-NanNAN-NANNAN="张三" suibian="随便" v-text="自定义格式" href="http://www.baidu.com" target="_blank"
    title="百度一下,你就知道" id="a1">百度一下</a>

  <script>
    const a1 = document.getElementById('a1')
    console.dir(a1) // 展开查看,找到上方书写的各个属性

    // 自定义属性: 用使用专业的方法来操作

    // 读:
    console.log(
      // Attribute: 属性
      // getAttribute: 读取元素上的自定义属性的值
      a1.getAttribute('suibian'),
      a1.getAttribute('v-text'),
    );

    // 写入操作
    a1.setAttribute('suibian', '换一个内容')

    // 读取使用 data- 声明的属性, 从 dataset 中读取
    // 多个单词 是 小驼峰命名法
    console.log(
      a1.dataset.x,
      a1.dataset.nannanNannan
    );
  </script>
</body>

</html>

练习

html
<!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>练习 09:35</title>

  <link rel="stylesheet" href="reset.css">
  <style>
    ul {
      display: flex;
      margin-top: 10px;
    }

    li {
      margin: 0 10px 10px 0;
      background-color: #eee;
      padding: 10px 25px;
      border-radius: 4px;
      cursor: pointer;
    }

    li.active {
      background-color: orange;
    }
  </style>
</head>

<body>
  <h2>请选择今日的午餐:</h2>
  <ul>
    <!-- 如何在元素上 存储单价信息?  通过自定义属性实现-->
    <li data-price="22">黄焖鸡</li>
    <li data-price="18">红烧牛肉面</li>
    <li data-price="35">老乡鸡</li>
    <li data-price="30">麻辣烫</li>
    <li data-price="15">水果捞</li>
    <li data-price="25">盖浇饭</li>
  </ul>

  <div id="box">
    消费金额: <b>0</b>
  </div>

  <script>
    const lis = document.querySelectorAll('li')

    lis.forEach(li => li.onclick = function () {
      li.classList.toggle('active')

      // 查询到所有激活状态的li, 计算price的总和
      const li_actives = document.querySelectorAll('li.active')
      var total = 0
      li_actives.forEach(li => {
        const price = li.dataset.price * 1 //读取自定义属性 data-price 的值
        total += price
      })

      const b = document.querySelector('#box>b')
      b.innerHTML = total
    })
  </script>
</body>

</html>

练习

html
<!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>练习 10:17</title>

  <link rel="stylesheet" href="reset.css">
  <style>
    #box {
      width: 400px;
      box-shadow: 0 0 2px 2px rgba(0, 0, 0, 0.2);
    }

    #box>img {
      width: 100%;
    }

    #box>div {
      display: flex;
    }

    #box>div>img {
      flex: 1;
      border: 3px solid transparent;
    }

    #box>div>img.active {
      border-color: orangered;
    }
  </style>
</head>

<body>
  <div id="box">
    <img src="imgs/1_lg.jpg" alt="">
    <div class="small">
      <!-- 自定义属性: 在小图上存放其对应的大图地址 -->
      <img data-big="imgs/1_lg.jpg" class="active" src="imgs/1_sm.jpg" alt="">
      <img data-big="imgs/2_lg.jpg" src="imgs/2_sm.jpg" alt="">
      <img data-big="imgs/3_lg.jpg" src="imgs/3_sm.jpg" alt="">
      <img data-big="imgs/4_lg.jpg" src="imgs/4_sm.jpg" alt="">
      <img data-big="imgs/5_lg.jpg" src="imgs/5_sm.jpg" alt="">
    </div>
  </div>

  <script>
    var imgs = document.querySelectorAll('#box>div>img')

    imgs.forEach(img => img.onmouseover = function () {
      imgs.forEach(img => img.classList.remove('active'))

      img.classList.add('active')
      // 读取大图地址:
      const big_src = img.dataset.big // data-big
      const img_big = document.querySelector('#box>img')
      // 修改 大图元素 显示的图片地址 src
      img_big.src = big_src
    })
  </script>
</body>

</html>

练习

html
<!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>练习 10:43</title>

  <link rel="stylesheet" href="reset.css">
  <style>
    #box {
      width: 600px;
    }

    #box>div {
      display: flex;
    }

    #box>div>img {
      border: 3px solid transparent;
    }


    #box>div>img.active {
      background-color: orange;
    }

    #box>img {
      width: 100%;
    }
  </style>
</head>

<body>
  <div id="box">
    <div>
      <img data-big="imgs/bigskin-1.jpg" class="active" src="imgs/smallskin-1.jpg" alt="">
      <img data-big="imgs/bigskin-2.jpg" src="imgs/smallskin-2.jpg" alt="">
      <img data-big="imgs/bigskin-3.jpg" src="imgs/smallskin-3.jpg" alt="">
      <img data-big="imgs/bigskin-4.jpg" src="imgs/smallskin-4.jpg" alt="">
      <img data-big="imgs/bigskin-5.jpg" src="imgs/smallskin-5.jpg" alt="">
    </div>
    <img src="./imgs/bigskin-1.jpg" alt="">
  </div>

  <script>
    const imgs = document.querySelectorAll('#box>div>img')

    imgs.forEach(img => img.onmouseover = function () {
      imgs.forEach(img => img.classList.remove('active'))

      img.classList.add('active')

      const big_src = img.dataset.big
      const img_big = document.querySelector('#box>img')
      img_big.src = big_src
    })
  </script>
</body>

</html>

练习

html
<!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>练习 11:17</title>

  <link rel="stylesheet" href="reset.css">
  <style>
    #box {
      width: 700px;
      overflow: hidden;
    }

    #box>ul {
      display: flex;
      background-color: #000;
    }

    #box>ul>li {
      flex: 1;
      padding: 10px 0;
      color: #b1b2be;
      text-align: center;
      cursor: pointer;
    }

    #box>ul>li.active {
      background-color: #262626;
      color: #f3c258;
    }

    #box>div>img {
      width: 100%;
      /* 关闭图片的 缩放属性 */
      flex: none;
    }

    #box>div {
      display: flex;
      transition: 0.3s;

      /* transform: translateX(-200%); */
    }
  </style>
</head>

<body>
  <div id="box">
    <div>
      <img src="wzry_imgs/1.jpeg" alt="">
      <img src="wzry_imgs/2.jpeg" alt="">
      <img src="wzry_imgs/3.jpeg" alt="">
      <img src="wzry_imgs/4.jpeg" alt="">
      <img src="wzry_imgs/5.jpeg" alt="">
    </div>

    <ul>
      <li data-x="0" class="active">夏洛特语音爆料</li>
      <li data-x="-100%">合金弹头4月上线</li>
      <li data-x="-200%">城市主理人招募</li>
      <li data-x="-300%">王者炸麦了</li>
      <li data-x="-400%">荣耀大话王</li>
    </ul>
  </div>

  <script>
    const lis = document.querySelectorAll('#box>ul>li')

    lis.forEach(li => li.onmouseover = function () {
      lis.forEach(li => li.classList.remove('active'))

      li.classList.add('active')

      // 读取偏移量x
      const x = li.dataset.x // data-x
      const div = document.querySelector('#box>div')
      div.style.transform = `translateX(${x})`
    })
  </script>
</body>

</html>

标签栏切换

html
<!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>标签栏切换 14:02</title>

  <link rel="stylesheet" href="reset.css">
  <style>
    .tabs {
      width: 800px;
    }

    .tabs>.tab-bar {
      border-bottom: 1px solid #e4393c;
      background-color: #f7f7f7;
      display: flex;
    }

    .tabs>.tab-bar>li {
      padding: 10px 30px;
      cursor: pointer;
    }

    .tabs>.tab-bar>li:hover {
      color: #e4393c;
    }

    .tabs>.tab-bar>li.active {
      color: white;
      background-color: #e4393c;
    }

    .tab-content>li {
      height: 400px;
      display: none;
    }

    .tab-content>li.active {
      display: block;
    }
  </style>
</head>

<body>
  <div class="tabs">
    <ul class="tab-bar">
      <!-- 利用自定义属性 关联到 对应的内容项 -->
      <li data-cid="content-1" class="active">商品介绍</li>
      <li data-cid="content-2">规格与包装</li>
      <li data-cid="content-3">售后保障</li>
      <li data-cid="content-4">商品评价(20万+)</li>
      <li data-cid="content-5">商品问答</li>
    </ul>

    <ul class="tab-content">
      <!-- 利用唯一标识 id 进行区分 -->
      <li id="content-1" class="active" style="background-color: orange;"></li>
      <li id="content-2" style="background-color: blue;"></li>
      <li id="content-3" style="background-color: purple;"></li>
      <li id="content-4" style="background-color: palegreen;"></li>
      <li id="content-5" style="background-color: brown;"></li>
    </ul>
  </div>

  <script>
    const lis = document.querySelectorAll('.tab-bar>li')

    lis.forEach(li => li.onclick = function () {
      lis.forEach(li => li.classList.remove('active'))
      li.classList.add('active')

      // 查找到之前激活的内容元素, 删除其激活状态
      const content_active = document.querySelector('.tab-content>li.active')
      content_active.classList.remove('active')

      // 读取自定义属性: 内容项的id - cid
      const cid = li.dataset.cid
      // 通过id查找到对应的内容项元素
      const content = document.getElementById(cid)
      content.classList.add('active')
    })
  </script>
</body>

</html>

轮播图

html
<!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>轮播图 14:41</title>

  <link rel="stylesheet" href="reset.css">
  <style>
    .banner {
      width: 600px;
      overflow: hidden;
      border: 2px solid green;
      position: relative;
    }

    .banner>.btns {
      position: absolute;
      left: 0;
      top: 50%;
      transform: translateY(-50%);
      width: 100%;
      display: flex;
      justify-content: space-between;
      padding: 0 10px;
    }

    .banner>.imgs {
      display: flex;
      transition: 0.3s;
    }

    .banner>.imgs>img {
      width: 100%;
    }

    .banner>ul {
      display: flex;
      position: absolute;
      bottom: 10px;
      left: 50%;
      transform: translateX(-50%);
    }

    .banner>ul>li {
      width: 12px;
      height: 12px;
      border-radius: 50%;
      background-color: white;
      margin: 4px;
    }

    .banner>ul>li.active {
      background-color: #0aa1ed;
    }
  </style>
</head>

<body>
  <div class="banner">
    <div class="imgs">
      <img src="imgs/banner1.png" alt="">
      <img src="imgs/banner2.png" alt="">
      <img src="imgs/banner3.png" alt="">
      <img src="imgs/banner4.png" alt="">
    </div>
    <!-- 页数指示点 -->
    <ul>
      <li data-x="0" class="active"></li>
      <li data-x="-100%"></li>
      <li data-x="-200%"></li>
      <li data-x="-300%"></li>
    </ul>

    <div class="btns">
      <button class="prev">上一页</button>
      <button class="next">下一页</button>
    </div>
  </div>

  <script>
    const lis = document.querySelectorAll('.banner > ul > li')

    lis.forEach(li => li.onmouseover = function () {
      lis.forEach(li => li.classList.remove('active'))

      li.classList.add('active')

      const x = li.dataset.x
      const div_imgs = document.querySelector('.banner>.imgs')
      div_imgs.style.transform = `translateX(${x})`
    })

    /////////////////////////////////////////
    //下一页: 
    const btn_next = document.querySelector('button.next')
    btn_next.onclick = function () {
      // 当前激活小圆点的 下一个点
      const li_active = document.querySelector('.banner li.active')
      // 下一个兄弟元素 nextElementSibling
      // next:下一个  element:元素  sibling:兄弟
      const li_next = li_active.nextElementSibling || document.querySelector('.banner li:first-child')

      console.log('当前激活圆点的下一个兄弟元素:', li_next);
      // 通过代码方式, 触发元素的 onmouseover 事件
      li_next.onmouseover()
    }

    ///////////////////////////////
    // 上一页:
    // 上一个兄弟元素: previousElementSibling
    const btn_prev = document.querySelector('.banner button.prev')
    btn_prev.onclick = function () {
      const li_active = document.querySelector('.banner li.active')

      const li_prev = li_active.previousElementSibling || document.querySelector('.banner li:last-child')

      li_prev.onmouseover()
    }

    // 定时器: 每隔4秒 自动触发下一页按钮的点击事件
    setInterval(() => {
      btn_next.onclick()
    }, 4000);

    // 逻辑短路语法 ||
    // 逻辑或: 值为从左到右 的首个 真值 true
    var res = null || 0 || '' || 11 || false
    console.log('res:', res);
  </script>
</body>

</html>

内容

html
<!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>标签内容 16:16</title>
</head>

<body>
  <div id="box1">Hello World!</div>

  <div id="box2">
    <a href="">Hello World!</a>
  </div>

  <script>
    const box1 = document.getElementById('box1')
    const box2 = document.getElementById('box2')

    console.log('box1.innerHTML:', box1.innerHTML)
    console.log('box1.innerText:', box1.innerText)

    console.log('box2.innerHTML:', box2.innerHTML)
    console.log('box2.innerText:', box2.innerText)

    // 总结:
    // inner: 内部的
    // innerHTML: 内部的html代码
    // innerText: 内部的文本内容

    // 如果: 标签中只有文本
    // 则 两个属性的 内容是相同的

    var words = '<h1>Nice To Meet You!</h1>'

    box1.innerHTML = words // 字符串会作为HTML代码被解析后显示
    box2.innerText = words // 字符串会作为普通文本, 不解析
  </script>
</body>

</html>

计数器

html
<!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>计数器 16:28</title>
</head>

<body>
  <div class="price" data-price="99">单价: ¥99</div>
  <div class="counter">
    <button>-</button>
    <span>10</span>
    <button>+</button>
  </div>
  <div class="total">总价格: ????</div>

  <script>
    const counter = document.querySelector('.counter')
    // children: 孩子们, 获取元素的 子元素们
    console.dir(counter) // 查看其 children 属性
    // 解构语法
    const [btn_minus, span_count, btn_add] = counter.children

    btn_add.onclick = function () {
      span_count.innerHTML++
      update()
    }

    btn_minus.onclick = function () {
      span_count.innerHTML--
      update()
    }

    // 用于更新页面上元素的状态
    function update() {
      // 读取单价 和 数量相乘, 结果赋值给总价
      const div_price = document.querySelector('.price')
      const price = div_price.dataset.price
      // 与数量相乘: 得到总价
      const total = price * span_count.innerHTML
      // 赋值到总价格元素中
      const div_total = document.querySelector('.total')
      div_total.innerHTML = `总价格: ¥${total}`

      // 根据数量的值, 来决定减按钮的不可用状态

      btn_minus.disabled = (span_count.innerHTML == 1)

      // span_count.innerHTML == 1 ? btn_minus.disabled = true : btn_minus.disabled = false

      // if (span_count.innerHTML == 1) {
      //   btn_minus.disabled = true
      // } else {
      //   btn_minus.disabled = false
      // }
    }

    /// 初始化显示时, 先更新一次
    update()
  </script>
</body>

</html>

购物车

html
<!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>购物车 17:15</title>

  <link rel="stylesheet" href="reset.css">
  <style>
    table {
      width: 800px;
    }

    thead {
      background-color: #eee;
    }

    td {
      padding: 10px 0;
      text-align: center;
      border: 1px solid gray;
    }
  </style>
</head>

<body>
  <table>
    <thead>
      <tr>
        <td>序号</td>
        <td>商品名</td>
        <td>单价</td>
        <td>数量</td>
        <td>小计</td>
      </tr>
    </thead>

    <tbody>
      <!-- <tr>
        <td></td>
        <td></td>
        <td></td>
        <td></td>
        <td></td>
      </tr> -->
    </tbody>

    <tfoot>
      <tr>
        <td colspan="5">总金额: ????</td>
      </tr>
    </tfoot>
  </table>

  <script>
    var data = [
      { name: "黄焖鸡", count: 3, price: 22 },
      { name: "炝莲白", count: 6, price: 17 },
      { name: "西红柿牛腩", count: 3, price: 52 },
      { name: "包子", count: 6, price: 3 },
      { name: "烤鸭", count: 1, price: 40 },
      { name: "麻辣烫", count: 13, price: 35 },
    ]

    var temp = data.reduce((box, value, index) => {
      const { count, name, price } = value

      return box + `<tr>
        <td>${index + 1}</td>
        <td>${name}</td>
        <td data-price="${price}">¥${price}</td>
        <td>
          <button>-</button>
          <span>${count}</span>
          <button>+</button>
        </td>
        <td></td>
      </tr>`
    }, '')

    const tbody = document.querySelector('tbody')
    tbody.innerHTML = temp


    // 1. 查询到所有的计数器, 然后挨个实现其 + 和 - 操作
    const counters = document.querySelectorAll('tbody td:nth-child(4)')

    console.log(counters)

    counters.forEach(counter => {
      const [btn_minus, span_count, btn_add] = counter.children

      btn_add.onclick = function () {
        span_count.innerHTML++
        update()
      }

      btn_minus.onclick = function () {
        span_count.innerHTML--
        update()
      }
    })

    // 页面的更新
    function update() {
      counters.forEach(counter => {
        const [btn_minus, span_count] = counter.children
        // 计数器td 的上一个兄弟
        const td_price = counter.previousElementSibling
        // 下一个兄弟: 小计
        const td_subtotal = counter.nextElementSibling

        // 单价:
        const price = td_price.dataset.price  // data-price
        // 小计 = 单价 x 数量
        const total = price * span_count.innerHTML
        td_subtotal.innerHTML = `¥${total}`
        // 减按钮的不可用
        btn_minus.disabled = span_count.innerHTML == 1
      })
    }

    update()
  </script>
</body>

</html>

知识点总结

属性操作的方案

  • 系统属性: 元素自带的属性, 各有各的功能
    • src: 图片的, 用来设置 img标签展示的图片地址
    • innerHTML: html内容的操作
    • innerText: 纯文本的内容操作
  • 自定义属性
    • 旧写法: 随便声明的属性, 名称不要跟系统属性重名即可
      • 通过固定的方法来读取和写入
      • getAttribute: 读取
      • setAttribute: 写入
    • 新写法
      • 固定格式: 使用 data- 开头来声明自定义属性
      • 存储在 dataset 属性中, 多个单词以小驼峰命名法进行保存

购物车

html
<!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>购物车 10:41</title>

  <link rel="stylesheet" href="reset.css">
  <style>
    table {
      width: 800px;
    }

    thead {
      background-color: #eee;
    }

    td {
      padding: 10px 0;
      text-align: center;
      border: 1px solid gray;
    }
  </style>
</head>

<body>
  <table>
    <thead>
      <tr>
        <td>序号</td>
        <td>商品名</td>
        <td>单价</td>
        <td>数量</td>
        <td>小计</td>
      </tr>
    </thead>

    <tbody>
      <!-- <tr>
        <td></td>
        <td></td>
        <td></td>
        <td></td>
        <td></td>
      </tr> -->
    </tbody>

    <tfoot>
      <tr>
        <td colspan="5">总金额: ????</td>
      </tr>
    </tfoot>
  </table>

  <script>
    var data = [
      { name: "黄焖鸡", count: 3, price: 22 },
      { name: "炝莲白", count: 6, price: 17 },
      { name: "西红柿牛腩", count: 3, price: 52 },
      { name: "包子", count: 6, price: 3 },
      { name: "烤鸭", count: 1, price: 40 },
      { name: "麻辣烫", count: 13, price: 35 },
    ]

    var temp = data.reduce((box, value, index) => {
      const { count, name, price } = value

      return box + `<tr>
        <td>${index + 1}</td>
        <td>${name}</td>
        <td data-price="${price}">¥${price}</td>
        <td>
          <button>-</button>
          <span>${count}</span>
          <button>+</button>
        </td>
        <td></td>
      </tr>`
    }, '')

    const tbody = document.querySelector('tbody')
    tbody.innerHTML = temp


    // 1. 查询到所有的计数器, 然后挨个实现其 + 和 - 操作
    const counters = document.querySelectorAll('tbody td:nth-child(4)')

    console.log(counters)

    counters.forEach(counter => {
      const [btn_minus, span_count, btn_add] = counter.children

      btn_add.onclick = function () {
        span_count.innerHTML++
        update()
      }

      btn_minus.onclick = function () {
        span_count.innerHTML--
        update()
      }
    })

    // 页面的更新
    function update() {
      var sum = 0 //存储总金额

      counters.forEach(counter => {
        const [btn_minus, span_count] = counter.children
        // 计数器td 的上一个兄弟
        const td_price = counter.previousElementSibling
        // 下一个兄弟: 小计
        const td_subtotal = counter.nextElementSibling

        // 单价:
        const price = td_price.dataset.price  // data-price
        // 小计 = 单价 x 数量
        const total = price * span_count.innerHTML

        sum += total // 小计累加给 总金额

        td_subtotal.innerHTML = `¥${total}`
        // 减按钮的不可用
        btn_minus.disabled = span_count.innerHTML == 1
      })

      // 查询到总金额 的 td, 进行赋值
      const td_sum = document.querySelector('tfoot td')
      td_sum.innerHTML = `总金额: ¥${sum}`
    }

    update()
  </script>
</body>

</html>

表单元素事件

  • focus: 获取焦点
  • blur: 失去焦点
  • change: 内容有变化
  • input: 实时输入
  • keyUp: 按键抬起
    • 利用事件参数中的 keyCode 识别具体的按键
html
<!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>表单元素事件 09:02</title>

  <style>
    #box {
      display: flex;
      flex-direction: column;
      align-items: flex-start;
    }
  </style>
</head>

<body>
  <!-- 表单元素: 和用户之间存在交互操作的元素.  按钮 输入框 下拉框 勾选框.. -->
  <div id="box">
    <input type="text">
    <input type="checkbox">
    <!-- 范围 1 - 100 -->
    <input type="range" min="1" max="100">
  </div>

  <script>
    const inp1 = document.querySelector('#box>input:first-child')

    // 1. 获得焦点 focus
    inp1.onfocus = function () {
      console.log('获得焦点 focus')
    }
    // 2. 失去焦点 blur
    inp1.onblur = function () {
      console.log('失去焦点 blur')
    }
    // 3. 内容变化 change
    // 内容变更后, 按回车 或 失去焦点时触发
    inp1.onchange = function () {
      // 单标签的值存储在 value 属性
      console.log('内容变化 change: ', this.value);
    }

    // 4. 内容输入 input
    inp1.oninput = function () {
      console.log('输入内容 input:', this.value)
    }

    // 5. 键盘事件 keyup
    // 事件参数: 由系统触发的事件, 默认会把本次事件相关联的信息 作为参数传递到事件方法中
    // 形参名: event事件. 习惯上会缩写为 e
    inp1.onkeyup = function (e) {
      console.log(arguments)
      console.log('按键抬起 keyup:', e)
      // 可以通过 keyCode 属性, 来分辨具体的按键
      // 回车的 keyCode : 13
      if (e.keyCode == 13) alert("回车按钮被点击!")
    }

    // 勾选框的 勾选状态
    const inp2 = document.querySelector('#box>input:nth-child(2)')
    inp2.onchange = function () {
      // checked: 此属性用于保存当前的勾选状态
      console.log('勾选框:', this.checked)
    }

    // 
    const inp3 = document.querySelector('#box>input:nth-child(3)')
    // change: 滑块松手后, 才会触发
    inp3.onchange = function () {
      console.log('change:', this.value)
    }
    // input: 实时的
    inp3.oninput = function () {
      console.log('input:', this.value)
    }
  </script>
</body>

</html>

练习: 输入框内容格式验证

html
<!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>练习 10:00</title>

  <style>
    input {
      padding: 5px;
      border: 1px solid #333;
      outline: none;
      border-radius: 3px;
    }

    input:focus {
      border-color: blue;
    }

    td {
      vertical-align: top;
    }

    /* 如果 输入框 带有 ok 样式, 则 div.ok 应该显示 */
    input.ok~div.ok {
      display: block;
    }

    /* ~ : 下方所有兄弟元素 */
    input.err~div.err {
      display: block;
    }

    input.err {
      border-color: red;
    }

    div.err {
      color: red;
      display: none;
    }

    div.ok {
      color: green;
      display: none;
    }
  </style>
</head>

<body>
  <!-- 对于存在隐藏效果的元素, 都应该先在HTML中书写 -->
  <table>
    <tbody>
      <tr>
        <td>手机号</td>
        <td>
          <input type="text" placeholder="可用于登录和找回密码">
          <div class="err">手机号码格式不正确</div>
          <div class="ok">手机号正确</div>
        </td>
      </tr>
    </tbody>
  </table>

  <script>
    // 当光标从输入框离开 - 失去焦点. 检查内容是否为手机号码格式
    const inp = document.querySelector('input')

    inp.onblur = function () {
      // 如果没有输入值, 则不进行检查!
      if (this.value == '') return

      // 利用 正则表达式 验证字符串格式
      if (/^1[3-9]\d{9}$/.test(this.value)) {
        this.classList.add('ok')
      } else {
        this.classList.add('err')
      }
    }

    // 获取焦点时: 清空之前的样式类
    inp.onfocus = function () {
      this.className = '' //因为是清空操作, 不需要什么技巧, 直接操作原始值
    }
  </script>

</body>

</html>

练习: 实时搜索

html
<!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>练习 11:00</title>
</head>

<body>
  <input type="text" placeholder="请输入商品名">

  <ul id="result"></ul>

  <script>
    // 监听输入框的 实时输入 操作, 到服务器查询相关的数据进行展示
    const inp = document.querySelector('input')

    inp.oninput = function () {
      const kw = this.value

      var url = `https://serverms.kunkun.cool/mall/search?type=1&kw=${kw}&page=1`

      var xhr = new XMLHttpRequest()
      xhr.open('get', url)
      xhr.onload = function () {
        var data = JSON.parse(xhr.response)
        console.log(data)

        const result = document.getElementById('result')
        result.innerHTML = data.data.reduce((box, value) => {
          return box + `<li>${value.name}</li>`
        }, '')
      }
      xhr.send()
    }
  </script>
</body>

</html>

勾选框联动

html
<!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>练习 11:25</title>

  <style>
    button {
      background-color: #72b134;
      color: white;
      width: 200px;
      border: none;
      border-radius: 2px;
      height: 34px;
    }

    /* 伪类选择器: 鼠标按下时 */
    button:active {
      opacity: 0.8;
    }

    /* 伪类选择器: 不可用时 */
    button:disabled {
      background-color: #ccc;
    }
  </style>
</head>

<body>
  <div>
    <label>
      <input type="checkbox">
      我已阅读并同意用户注册协议
    </label>
  </div>

  <button disabled>提交注册</button>

  <script>
    const chb = document.querySelector('input')
    const btn = document.querySelector('button')

    chb.onchange = function () {
      btn.disabled = !this.checked
      // if (this.checked) {
      //   // 勾选-真;   不可用-假
      //   btn.disabled = false
      // } else {
      //   btn.disabled = true
      // }
    }
  </script>
</body>

</html>

滑块: 颜色选择

html
<!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>练习 11:42</title>

  <style>
    #box {
      width: 200px;
      height: 200px;
      border: 2px solid gray;
    }
  </style>
</head>

<body>
  <table>
    <tbody>
      <tr>
        <td>Red</td>
        <td><input type="range" min="0" max="255" value="0"></td>
        <td>0</td>
      </tr>
      <tr>
        <td>Green</td>
        <td><input type="range" min="0" max="255" value="0"></td>
        <td>0</td>
      </tr>
      <tr>
        <td>Blue</td>
        <td><input type="range" min="0" max="255" value="0"></td>
        <td>0</td>
      </tr>
    </tbody>
  </table>

  <div id="box"></div>

  <script>
    const trs = document.querySelectorAll('tbody>tr')
    // 查询 3个 range 
    const ranges = document.querySelectorAll('[type=range]')

    // 给每个滑块都要添加 实时变更事件, 来更新页面内容
    ranges.forEach(range => {
      range.oninput = function () {
        update()
      }
    })

    // 更新页面
    function update() {
      ranges.forEach(range => {
        // 值所在的td, 是range标签的父元素的下一个兄弟
        // parentElement: 父元素
        const td_value = range.parentElement.nextElementSibling
        td_value.innerHTML = range.value
      })

      // 获取 rgb 三个值
      const red = ranges[0].value
      const green = ranges[1].value
      const blue = ranges[2].value

      const box = document.getElementById('box')
      box.style.backgroundColor = `rgb(${red},${green},${blue})`
    }
    update()
  </script>
</body>

</html>

事件的冒泡机制

当元素上触发一个事件之后, 会传递给父元素触发相同的事件

  • 事件参数中的target: 代表触发事件的当事元素
  • 事件参数中的stopPropagation 方法, 可以主动停止冒泡机制
html
<!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>事件的冒泡机制 14:01</title>

  <style>
    #red {
      background-color: red;
      width: 500px;
      height: 500px;
    }

    #green {
      background-color: green;
      width: 300px;
      height: 300px;
    }

    #blue {
      background-color: blue;
      width: 100px;
      height: 100px;
    }
  </style>
</head>

<body>
  <div id="red">
    <div id="green">
      <div id="blue"></div>
    </div>
  </div>

  <script>
    // 事件的冒泡机制: 子元素上发生一个事件后, 默认会传播到父元素, 触发相同的事件

    const red = document.getElementById('red')
    const green = document.getElementById('green')
    const blue = document.getElementById('blue')

    // 当事元素: 事件触发的当事'人'
    red.onclick = function (e) {
      console.log('red click!');

      //事件参数中的 target 属性, 存储的就是当事元素
      console.log('当事元素:', e.target)
    }

    green.onclick = function () {
      console.log('green click!');
    }

    blue.onclick = function (e) {
      // stopPropagation: 停止传播. 则不再冒泡到父元素
      e.stopPropagation()
      console.log('blue click!');
    }
  </script>
</body>

</html>

委托模式

html
<!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>事件委托 14:21</title>

  <style>
    ul {
      display: flex;
      border: 2px solid green;
      padding: 10px;
    }

    ul>li {
      list-style: none;
      background-color: #f5f5f5;
      padding: 10px 20px;
      margin: 5px;
    }

    .active {
      background-color: orange;
    }
  </style>
</head>

<body>
  <!-- 委托: 自己不亲自完成任务, 让其他人帮忙 -->
  <!-- 事件委托: 元素自身不处理事件, 而是让父元素帮忙处理 -->
  <ul id="box">
    <li>HTML</li>
    <li>CSS</li>
    <li>JS</li>
    <li>DOM</li>
    <li>Vue</li>
  </ul>

  <script>
    // 思想: 要操作哪些元素 就要先把它们查询出来, 再通过遍历的方式挨个捆绑事件
    // const lis = document.querySelectorAll('#box>li')

    // lis.forEach(li => li.onclick = function () {
    //   this.classList.add('active')
    // })

    // 代理方案: 每个li触发点击事件后, 都会传递给父元素. 那么直接让父元素处理点击事件
    const box = document.querySelector('#box')
    box.onclick = function (e) {
      console.log('当事元素:', e.target)

      // 问题: 必须判断 当事元素 是否是 我们要处理的元素
      // 当前场景中: 只处理 标签名 是  li 的元素
      console.dir(e.target) // 查看哪个属性代表 标签名

      // 如果标签名 不是 li , 则终止执行
      if (e.target.localName != 'li') return

      // 帮助当事元素添加激活类
      e.target.classList.add('active')
    }
  </script>
</body>

</html>

练习: 动态新增子元素

html
<!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>练习 14:45</title>
</head>

<body>
  <!-- 适合采用委托模式的场景: 动态新增的子元素 -->
  <button>新增元素</button>
  <ul>
    <li>HTML</li>
    <li>CSS</li>
    <li>JS</li>
  </ul>

  <script>
    const btn = document.querySelector('button')
    const ul = document.querySelector('ul')

    btn.onclick = function () {
      ul.innerHTML += '<li>新增...</li>'

    }
    // 点击li之后, 颜色变红  style.color = 'red'
    // 查询到所有li, 遍历增加点击事件, 点击后修改 style

    // 问题: 运行时只有3个li存在, 所以只影响了 这3个li
    // 对于后续增加的li, 并没有影响, 所以导致 没有点击效果

    // const lis = document.querySelectorAll('li')
    // lis.forEach(li => li.onclick = function () {
    //   li.style.color = 'red'
    // })


    // 委托: 给父元素增加任务.  不管什么时候增加的子元素, 都让父元素帮忙
    ul.onclick = function (e) {
      // 判定: 当事元素不是li的,不进行处理
      if (e.target.localName != 'li') return

      e.target.style.color = 'red'
    }
  </script>
</body>

</html>

练习: 网络请求+委托

html
<!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>练习 15:16</title>

  <style>
    #menu {
      display: flex;
      margin: 0;
      padding: 0;
    }

    #menu>li {
      list-style: none;
      margin: 6px;
      cursor: pointer;
    }

    #menu>li.active {
      color: orange;
      border-bottom: 2px solid orange;
    }
  </style>
</head>

<body>
  <ul id="menu">

  </ul>

  <script>
    var url = 'https://douyu.kunkun.cool/api/cate/recList'
    var ul = document.querySelector('#menu')

    var xhr = new XMLHttpRequest()
    xhr.open('get', url)
    xhr.onload = function () {
      var data = JSON.parse(xhr.response)
      console.log(data)

      ul.innerHTML = data.data.reduce((box, value, index) => {
        // 根据序号 给出不同的初始样式
        return box + `<li class="${index == 0 ? 'active' : ''}">${value.name}</li>`
      }, '')
    }
    xhr.send()

    // 委托模式: 通过父元素 ul 标签, 帮助子元素 li 实现点击事件的效果
    ul.onclick = function (e) {
      if (e.target.localName != 'li') return

      const li_active = document.querySelector('#menu>li.active')
      li_active.classList.remove('active')

      e.target.classList.add('active')
    }
  </script>
</body>

</html>

事件监听器

重复为相同的事件绑定方法

html
<!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>事件监听器 15:50</title>
</head>

<body>
  <button>Click Me!</button>

  <script>
    const btn = document.querySelector('button')
    // 通过属性来绑定事件的弊端: 同一时间只能绑定一个
    // 风险: 团队合作时, 可能不同的组员给同一个元素绑定了相同的事件, 导致BUG
    btn.onclick = function () {
      console.log(1)
    }

    btn.onclick = function () {
      console.log(2)
    }

    // 事件监听器: 为事件添加多个响应的方法, 按顺序执行 而不会覆盖
    // 参数1: 事件名. 比如 click blur focus change ...
    // 参数2: 关联的方法
    btn.addEventListener('click', function () {
      console.log(3)
    })

    btn.addEventListener('click', function () {
      console.log(4)
    })
  </script>
</body>

</html>

页面的滚动事件

scroll

通过 pageYOffset 读取滚动的距离

html
<!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>窗口的滚动事件 16:15</title>

  <style>
    #nav {
      padding: 20px;
      background-color: orange;
      position: fixed;
      top: 0;
      left: 0;
      width: 100%;
      display: none;
    }

    /* 当带有 class=show 时, 则显示 */
    #nav.show {
      display: block;
    }
  </style>
</head>

<body>
  <div id="nav">这是一个导航栏</div>

  <!-- 监听页面的滚动, 调整某些内容的显示和隐藏 -->
  <ul id="box"></ul>

  <script>
    const box = document.getElementById('box')
    for (let i = 0; i < 200; i++) {
      box.innerHTML += `<li>${i}</li>`
    }

    // 隐藏前缀: window 窗口
    // 滚动事件: scroll
    addEventListener('scroll', function () {
      console.log('滚动中...')
      // 读取竖向滚动距离
      console.log(pageYOffset)

      // 如果滚动距离 超出100像素, 就显示导航栏
      const nav = document.getElementById('nav')
      if (pageYOffset > 100) {
        nav.classList.add('show')
      } else {
        nav.classList.remove('show')
      }
    })
  </script>
</body>

</html>

createDOM

利用JS方式创建DOM元素

html
<!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>创建DOM元素</title>

  <style>
    #box {
      width: 400px;
      height: 300px;
      background-color: gray;
    }
  </style>
</head>

<body>
  <!-- 
    DOM: 文档对象模型
    - 书写HTML代码, 浏览器帮我们转化成document对象, 最终渲染到页面上

    HTML代码相当于 语法糖. 比存粹的document 代码更加易读
    但是转化过程 需要消耗额外的系统资源

    对于性能的极致追求: 抛开HTML代码, 直接用 document 方式来创建页面元素
    - 浏览器的解析操作 由 程序员来完成

    目前世界排名第一的前端框架 React, 就是利用这种思想实现. 0 HTML编程
   -->

  <script>
    // 创建1个 div 元素
    // createElement: 通过标签名 来创建元素
    const box = document.createElement('div')
    box.id = 'box'
    console.log(box)
    // body: 代表body标签
    // appendChild: 添加子元素
    document.body.appendChild(box)

    // 在box中新增超链接:
    const a_baidu = document.createElement('a')
    a_baidu.innerHTML = '百度一下'
    a_baidu.title = '百度一下, 你就知道'
    a_baidu.href = 'https://www.baidu.com'

    box.appendChild(a_baidu)

    const btn = document.createElement('button')
    btn.innerHTML = 'Click Me!'
    btn.addEventListener('click', function () {
      alert("Hello !")
    })
    box.appendChild(btn)
  </script>
</body>

</html>

封装技巧

体验封装的魅力

html
<!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>Query 17:11</title>

  <style>
    .warning {
      background-color: orange;
      padding: 5px;
      border-radius: 3px;
    }
  </style>
</head>

<body>
  <ul>
    <li>HTML</li>
    <li>CSS</li>
    <li>JS</li>
    <li>DOM</li>
  </ul>

  <script>
    // 原生DOM操作的缺点:
    // 1. 对于结果要遍历操作
    // 2. 方法名比较长
    // const lis = document.querySelectorAll('li')
    // lis.forEach(li => li.style.color = 'red')

    function $(selector) {
      return document.querySelectorAll(selector)
    }

    // 在原型中增加 css 方法, 用于设置style
    NodeList.prototype.css = function (name, value) {
      // element 元素,  缩写: el
      // [] : 方括号语法, 其中是JS代码, 此处是 name 变量的值
      this.forEach(el => el.style[name] = value)
    }

    NodeList.prototype.addClass = function (className) {
      this.forEach(el => el.classList.add(className))
    }

    const lis = $('li')
    console.log(lis)


    // 利用封装技巧: 预期效果如下:
    $('li').css('color', 'red')
    $('li').addClass('warning')
  </script>

  <script>
    // 对象的属性操作有两种语法: [] 和 . 

    var emp = { x: 20, ename: "小明", age: 19, [11 + 9]: 'xxx' }
    console.log(emp)
    // 方括号语法: []中书写JS代码
    let x = 'ename'
    console.log(emp[x]) //小明
    console.log(emp.x) // 20


    // 点语法:
    console.log(emp.ename, emp.age)
    emp.ename = '孙凯'
    emp.age = 33
  </script>
</body>

</html>

jQuery

在 2006年1月 发布, 在当年属于重量级的 javascript库, 利用封装技巧大大简化了 DOM操作的方式

  • 属于当时市场占有率第一的库

https://jquery.com/

理念: write less,do more 写得少,做的多

下载地址: https://jquery.com/download/

jQuery

2006年发布的 javascript 脚本库. 利用封装技巧把操作DOM的方式进行了简化

理念:write less, do more! 写的少,做的多

css方法

html
<!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>$ 09:03</title>
</head>

<body>
  <ul>
    <li>HTML</li>
    <li>CSS</li>
    <li>DOM</li>
  </ul>

  <!-- 使用脚本前, 必须先引入 -->
  <script src="jquery-3.6.4.js"></script>
  <script>
    console.log(window)
    // jQuery脚本在全局对象中添加了 $ 和 jQuery  函数

    // 查询到所有li元素
    console.log(
      // 参数是 css 选择器;   等价与 document.querySelectorAll
      $('li')
    );

    // 利用css方法: 可以自动遍历修改 所有查询到的元素 的 style 属性
    $('li').css('color', 'red')
    // 设置 padding: 10px
    $('li').css("padding", '10px')
    // 设置 margin: 10px
    // 简化: 与像素单位有关的, 如果不写单位 默认是 px
    $('li').css('margin', 10)
    // 边框:
    $('li').css('border', '4px solid gray')
    // 圆角: border-radius 修改为 10px
    $('li').css('border-radius', 10)
    // 多个单词组成的 css 属性, 有两种写法:  字符串 或 小驼峰
    $('li').css('backgroundColor', 'green')

    // 支持 函数重载机制 实现多功能函数效果
    // 根据实参的 个数 或 类型 不同, 完成不同的任务
    console.log(
      // 只有一个参数: 则读取查询到的首个元素的 指定样式的值
      $('li').css('color')
    );

    // 参数只有一个 && 对象类型: 可以同时设置多个属性
    $('li').css({
      'font-size': 25,
      listStyle: 'none',
      width: 200
    })
  </script>
</body>

</html>

事件

html
<!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>事件 09:33</title>
</head>

<body>
  <ul>
    <li>HTML</li>
    <li>CSS</li>
    <li>JS</li>
    <li>DOM</li>
  </ul>

  <script src="jquery-3.6.4.js"></script>
  <script>
    // 把点击触发的方法, 通过实参方式传入
    $('li').click(
      function () {
        console.log('this:', this)
        // 使用 jQuery 提供的 css 方法, 则比如把 元素 转为 jQuery 包装的
        console.log('$(this):', $(this))

        // $():属于多功能函数
        // $(字符串): 负责查询到 指定的元素
        // $(元素): 封装成 jQuery 类型的对象
        // $(this) : 把this进行封装后, 能够使用 jQuery提供的功能
        // 类似:  托尼斯塔克 穿上 钢铁侠的机甲, 就能飞天遁地
        $(this).css('color', 'red')
      }
    )
  </script>
</body>

</html>

class操作

html
<!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>class操作</title>

  <style>
    .danger {
      padding: 10px;
      border-radius: 3px;
      background-color: red;
      color: white;
      box-shadow: 0 0 2px 2px rgba(0, 0, 0, 0.3);
    }
  </style>
</head>

<body>

  <ul>
    <li>HTML</li>
    <li>CSS</li>
    <li>JS</li>
    <li>DOM</li>
  </ul>

  <script src="jquery-3.6.4.js"></script>
  <script>
    $('li').click(function () {
      $(this).addClass('danger')
    })
  </script>
</body>

</html>

练习: 选中效果

html
<!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>练习 10:10</title>

  <link rel="stylesheet" href="reset.css">
  <style>
    ul {
      display: flex;
    }

    li {
      margin: 4px;
      background-color: #eee;
      padding: 10px 20px;
    }

    li.active {
      background-color: orange;
      color: white;
    }
  </style>
</head>

<body>
  <h2>点击选择午餐:</h2>
  <ul>
    <li>KFC</li>
    <li>牛肉面</li>
    <li>麻辣烫</li>
    <li>黄焖鸡</li>
    <li>螺蛳粉</li>
  </ul>

  <script src="jquery-3.6.4.js"></script>
  <script>
    $('li').click(function () {
      // toggleClass: 切换样式
      $(this).toggleClass('active')
    })
  </script>
</body>

</html>

练习:单选效果

html
<!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>练习 10:21</title>

  <link rel="stylesheet" href="reset.css">
  <style>
    li {
      background-color: #eee;
      padding: 10px;
      width: 200px;
      margin-bottom: 10px;
      transition: 0.3s;
    }

    li.active {
      background-image: linear-gradient(to right, red, orange);
      color: white;
      box-shadow: 0 0 2px 2px rgba(0, 0, 0, 0.2);
      padding-left: 30px;
    }
  </style>
</head>

<body>
  <ul>
    <li class="active">阶段1:晓宇</li>
    <li>阶段2:亮亮</li>
    <li>阶段3:小新</li>
    <li>阶段4:王五</li>
    <li>阶段5:文华</li>
  </ul>

  <script src="jquery-3.6.4.js"></script>
  <script>
    $('li').click(function () {
      // 把之前激活的删除样式
      // $('li.active').removeClass('active')

      // siblings(): 兄弟元素们
      // 当前项.添加样式().兄弟项目们.删除样式()
      $(this).addClass('active').siblings().removeClass('active')
    })
  </script>
</body>

</html>

练习

html
<!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>练习 10:38</title>

  <link rel="stylesheet" href="reset.css">
  <style>
    ul {
      display: flex;
      background-color: #002c69;
      padding: 0 30px;
    }

    li {
      padding: 10px 30px;
      color: white;
      cursor: pointer;
    }

    li.active {
      background-color: orange;
    }
  </style>
</head>

<body>
  <ul>
    <li class="active">首页</li>
    <li>关于净美仕</li>
    <li>公司动态</li>
    <li>产品中心</li>
    <li>联系我们</li>
  </ul>

  <script src="jquery-3.6.4.js"></script>
  <script>
    $('li').click(function () {
      $(this).addClass('active').siblings().removeClass('active')
    })
  </script>
</body>

</html>

练习

html
<!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>练习 10:50</title>

  <link rel="stylesheet" href="reset.css">
  <style>
    ul {
      display: flex;
      background-color: #f5f5f6;
      padding: 30px;
    }

    li {
      background-color: white;
      margin: 6px;
      color: #4e6ef2;
      width: 40px;
      text-align: center;
      line-height: 40px;
      border-radius: 4px;
      cursor: pointer;
    }

    li.active {
      background-color: #4e6ef2;
      color: white;
    }
  </style>
</head>

<body>
  <ul>
    <li class="active">1</li>
    <li>2</li>
    <li>3</li>
    <li>4</li>
    <li>5</li>
    <li>6</li>
  </ul>

  <script src="jquery-3.6.4.js"></script>
  <script>
    $('li').click(function () {
      $(this).addClass('active').siblings().removeClass('active')
    })
  </script>
</body>

</html>

显示和隐藏

html
<!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>显示与隐藏 11:16</title>

  <style>
    #box {
      width: 200px;
      height: 200px;
      background-color: gray;
      margin-top: 20px;
    }
  </style>
</head>

<body>
  <button>显示</button>
  <button>隐藏</button>
  <button>切换</button>
  <div id="box"></div>

  <script src="jquery-3.6.4.js"></script>
  <script>
    // 隐藏
    console.log($('button'))

    // 获取查询出来的结果中, 序号1个的按钮
    console.log($('button')[1]) // 获取的是 原生的元素

    // eq(): 从查询结果中获取 指定序号的元素, 并封装成jQuery对象
    console.log($('button').eq(1)) // 获取的是 用jQuery包裹的 元素

    $('button').eq(1).click(function () {
      // hide:隐藏
      $('#box').hide()
    })

    $('button').eq(0).click(function () {
      $('#box').show() // 显示
    })

    $('button').eq(2).click(function () {
      $('#box').toggle() //切换
    })
  </script>
</body>

</html>

练习:菜单展开

html
<!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>练习 11:31</title>

  <link rel="stylesheet" href="reset.css">
  <style>
    #box {
      border: 2px solid gray;
      width: 400px;
    }

    #box>h3 {
      background-color: #eee;
      padding: 6px;
      cursor: pointer;
    }

    ul {
      display: none;
    }

    ul>li {
      padding: 5px;
      border-bottom: 1px dashed gray;
    }
  </style>
</head>

<body>
  <div id="box">
    <h3>点击查看一阶段内容</h3>
    <ul>
      <li>html</li>
      <li>css</li>
      <li>js</li>
      <li>sass</li>
    </ul>

    <h3>点击查看二阶段内容</h3>
    <ul>
      <li>node.js</li>
      <li>express</li>
      <li>云服务器</li>
      <li>mysql</li>
    </ul>

    <h3>点击查看三阶段内容</h3>

    <ul>
      <li>JSCORE</li>
      <li>DOM</li>
      <li>jQuery</li>
      <li>美食广场项目</li>
    </ul>
  </div>

  <script src="jquery-3.6.4.js"></script>
  <script>
    $('#box>h3').click(function () {
      // next(): 下一个兄弟元素
      // $(this).next().toggle()

      // 带有滑动动画的过渡效果, 实现显示切换
      // slideToggle
      // 参数1: 动画时长, 两种写法: 'fast' 或 'slow';  或写 毫秒数
      $(this).next().slideToggle(200)  // 1000毫秒 = 1秒
    })
  </script>
</body>

</html>

练习:底部弹窗

html
<!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>练习 11:50</title>
  <link rel="stylesheet" href="reset.css">
  <style>
    #ad {
      background-color: #eee;
      padding: 5px;
      position: fixed;
      bottom: 0;
      right: 0;
      width: 200px;
    }
  </style>
</head>

<body>
  <div id="ad">
    <button>关闭</button>
    <p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Velit, atque! Nam ratione inventore molestiae veniam quo
      perspiciatis exercitationem. Deserunt expedita cum rem fuga similique perspiciatis autem ipsa et aliquid dolor.
    </p>
  </div>

  <script src="jquery-3.6.4.js"></script>
  <script>
    $('#ad>button').click(function () {
      // $('#ad').hide()
      // 滑动收起
      // 回调函数: 当动画结束时会触发
      $('#ad').slideUp(function () {
        // alert("动画结束!")
        setTimeout(() => {
          $('#ad').slideDown() // 滑动展开
        }, 2000);
      })
    })
  </script>
</body>

</html>

自定义动画

html
<!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>动画效果制作 14:01</title>

  <style>
    #box {
      width: 100px;
      height: 100px;
      background-color: gray;
      /* 相对定位: 在调整位置时, 不会影响其他元素 */
      position: relative;
    }
  </style>
</head>

<body>
  <!-- 
    动画分三种制作方式:
    - transition: 过渡动画/补间动画
    - @keyFrame : 关键帧动画
    - JS动画: 捕捉频率刷新频率来实现
   -->
  <button>开始</button>
  <button>结束</button>

  <div id="box"></div>

  <script src="jquery-3.6.4.js"></script>
  <script>
    $('button').eq(0).click(function () {
      // animate: 用动画的方式, 调整元素的样式
      // - transform 和 颜色相关 不支持
      $('#box').animate({ left: 100, height: 50 })
        // 支持动画队列: 多个动画可以排队执行
        .animate({ top: 200, width: 50 })
        // 可以通过参数2 设置动画时长, 单位毫秒
        .animate({ left: 0 }, 1000)
        // 可以添加函数, 作为结束时的回调函数
        .animate({ top: 0, width: 100, height: 100 }, function () {
          // alert("success!")
        })
    })


    // 结束动画
    $('button').eq(1).click(function () {
      // 停止的默认设定: 立刻停止当前动画, 继续动画队列中后续的
      // 样式默认会保持在停止时的状态

      // 参数1: 是否要清空整个动画队列, 默认false 代表不清空
      // 参数2: 停止时是否要跳转到动画的最终效果: 默认false , 不跳转
      $('#box').stop(true, true)
    })
  </script>
</body>

</html>

练习: 标签栏切换

html
<!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>练习 14:29</title>

  <link rel="stylesheet" href="reset.css">
  <style>
    #banner {
      width: 700px;
      overflow: hidden;
    }

    #banner>div {
      display: flex;
      position: relative;
      left: -100%;
    }

    #banner>div>img {
      width: 100%;
      flex: none;
    }

    #banner>ul {
      display: flex;
      background-color: #000;
    }

    #banner>ul>li {
      flex: 1;
      text-align: center;
      line-height: 40px;
      color: #b1b2be;
      cursor: pointer;
    }

    #banner>ul>li.active {
      background-color: #262626;
      color: #f2c158;
    }
  </style>
</head>

<body>
  <div id="banner">
    <div>
      <img src="wzry_imgs/1.jpeg" alt="">
      <img src="wzry_imgs/2.jpeg" alt="">
      <img src="wzry_imgs/3.jpeg" alt="">
      <img src="wzry_imgs/4.jpeg" alt="">
      <img src="wzry_imgs/5.jpeg" alt="">
    </div>

    <ul>
      <li class="active">夏洛特语音爆料</li>
      <li>合金弹头4月上线</li>
      <li>城市主理人招募</li>
      <li>王者炸麦了</li>
      <li>荣耀大话王</li>
    </ul>
  </div>

  <script src="jquery-3.6.4.js"></script>
  <script>
    $('#banner>ul>li').mouseover(function () {
      $(this).addClass('active').siblings().removeClass('active')
      // index(): 获取元素在兄弟元素中的序号
      const i = $(this).index()
      console.log('i:', i)

      $('#banner>div').stop().animate({ left: `-${i}00%` })
    })
  </script>
</body>

</html>

练习

html
<!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>练习 15:15</title>

  <link rel="stylesheet" href="reset.css">
  <style>
    .tabs {
      width: 800px;
    }

    .tabs>ul {
      display: flex;
      border-bottom: 1px solid #e4393c;
      background-color: #f7f7f7;
    }

    .tabs>ul>li {
      padding: 15px 30px;
      cursor: pointer;
    }

    .tabs>ul>li.active {
      color: white;
      background-color: #e4393c;
    }

    .tabs>div>div {
      height: 300px;
    }
  </style>
</head>

<body>
  <div class="tabs">
    <ul>
      <li class="active">商品介绍</li>
      <li>规格与包装</li>
      <li>售后保障</li>
      <li>商品评价(20万+)</li>
      <li>商品问答</li>
    </ul>

    <div class="tab-content">
      <div style="background-color: green;"></div>
      <div style="background-color: red;"></div>
      <div style="background-color: blue;"></div>
      <div style="background-color: yellow;"></div>
      <div style="background-color: brown;"></div>
    </div>
  </div>

  <script src="jquery-3.6.4.js"></script>
  <script>
    $('.tabs>ul>li').click(function () {
      $(this).addClass('active').siblings().removeClass('active')

      // 根据当前点击项的序号, 找到对应序号的内容元素, 让其显示
      const i = $(this).index()

      // eq(): 从已查询到的元素中, 找到对应序号的
      $('.tabs>div>div').eq(i).show().siblings().hide()
    })

    // 初始化状态: 就应该触发第一个标签的点击事件
    // click(): 有两种用途
    // 1. 不传递参数: 就是触发事件
    // 2. 传递函数: 就是给元素绑定事件
    $('.tabs>ul>li').eq(0).click()
  </script>
</body>

</html>

练习

html
<!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>练习 15:44</title>
  <link rel="stylesheet" href="reset.css">
  <style>
    .small {
      display: flex;

    }

    .small>img.active {
      border: 4px solid brown;
    }

    .large>img {
      width: 600px;
    }
  </style>
</head>

<body>
  <div class="small">
    <img class="active" src="imgs/smallskin-1.jpg" alt="">
    <img src="imgs/smallskin-2.jpg" alt="">
    <img src="imgs/smallskin-3.jpg" alt="">
    <img src="imgs/smallskin-4.jpg" alt="">
    <img src="imgs/smallskin-5.jpg" alt="">
  </div>

  <div class="large">
    <img src="imgs/bigskin-1.jpg" alt="">
    <img src="imgs/bigskin-2.jpg" alt="">
    <img src="imgs/bigskin-3.jpg" alt="">
    <img src="imgs/bigskin-4.jpg" alt="">
    <img src="imgs/bigskin-5.jpg" alt="">
  </div>

  <script src="jquery-3.6.4.js"></script>
  <script>
    $('.small>img').mouseover(function () {
      $(this).addClass('active').siblings().removeClass('active')

      const i = $(this).index()
      $('.large>img').eq(i).show().siblings().hide()
    })

    //////////////
    $('.small>img').eq(0).mouseover()
  </script>
</body>

</html>

属性操作

html
<!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>属性操作 16:20</title>
</head>

<body>
  <!-- 
    元素具有两种属性:
    - 系统属性
    - 自定义属性
      -- 旧写法: 随便写. 用 getAttribute 和 setAttribute 操作
      -- 新写法: data- 开头;  用 dataset 属性操作
   -->
  <a data-x="10" data-nan-NAN="张三" suibian="随便" v-text="自定义属性" href="https://www.baidu.com" target="_blank">百度一下</a>

  <script src="jquery-3.6.4.js"></script>
  <script>
    // jQuery提供了 prop 和 attr 和 data 三种方法可以操作属性

    // prop: 操作系统属性
    console.log(
      $('a').prop('href'),
      $('a').prop('target'),
    )
    // 修改操作: 使用两个参数
    $('a').prop('href', 'https://www.jd.com')

    // 自定义属性: 万能用法 attr
    console.log(
      $('a').attr('suibian'),
      $('a').attr('v-text'),
      $('a').attr('data-x'),
      $('a').attr('href'),
    )

    $('a').attr('suibian', '修改一下')

    // data(): 只能用于读取 data- 定义的属性, 无法修改;  修改用attr
    console.log(
      $('a').data('x') //data-x
    );
  </script>
</body>

</html>

练习

html
<!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>练习 16:38</title>

  <link rel="stylesheet" href="reset.css">
  <style>
    #box {
      width: 300px;
      box-shadow: 0 0 2px 2px rgba(0, 0, 0, 0.1);
    }

    #box>img {
      width: 100%;
    }

    #box>div {
      display: flex;
    }

    #box>div>img {
      flex: 1;
      border: 2px solid transparent;
    }

    #box>div>img.active {
      border-color: orangered;
    }
  </style>
</head>

<body>
  <div id="box">
    <img src="imgs/1_lg.jpg" alt="">
    <div>
      <img data-lg="imgs/1_lg.jpg" class="active" src="imgs/1_sm.jpg" alt="">
      <img data-lg="imgs/2_lg.jpg" src="imgs/2_sm.jpg" alt="">
      <img data-lg="imgs/3_lg.jpg" src="imgs/3_sm.jpg" alt="">
      <img data-lg="imgs/4_lg.jpg" src="imgs/4_sm.jpg" alt="">
      <img data-lg="imgs/5_lg.jpg" src="imgs/5_sm.jpg" alt="">
    </div>
  </div>

  <script src="jquery-3.6.4.js"></script>
  <script>
    $('#box>div>img').mouseover(function () {
      $(this).addClass('active').siblings().removeClass('active')

      //读取自定义属性 data-lg 的值
      const lg = $(this).data('lg')
      // 设置给大图的 src 属性:  src属于系统属性, 使用prop方法操作
      $('#box>img').prop('src', lg)
    })
  </script>
</body>

</html>

标签内容

html
<!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>内容 17:10</title>
</head>

<body>
  <!-- innerHTML 和 innerText -->
  <div id="box1">Hello World!</div>

  <div id="box2">
    <a href="">Hello World!</a>
  </div>

  <ul></ul>

  <script src="jquery-3.6.4.js"></script>
  <script>
    console.log('box1 html:', $('#box1').html())
    console.log('box1 text:', $('#box1').text())

    console.log('box2 html:', $('#box2').html())
    console.log('box2 text:', $('#box2').text())

    // 赋值语法:
    var kw = '<h1>Hello</h1>'

    $('#box1').html(kw)
    $('#box2').text(kw)

    // 数组:
    var data = [
      '<li>HTML</li>',
      '<li>CSS</li>',
      '<li>JS</li>',
      '<li>DOM</li>',
    ]
    // 如果 参数是数组类型, 会自动利用 join('') 完成拼接后 再赋值
    $('ul').html(data)
  </script>
</body>

</html>

get请求

html
<!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>网络请求 17:23</title>
</head>

<body>
  <ul></ul>

  <script src="jquery-3.6.4.js"></script>
  <script>
    console.dir($) // 查看其中的属性, 比如  get 

    var url = 'https://api.kunkun.cool/car/news.json'
    // jQuery把 ajax 操作做了封装, 比如 get 方法专门负责get请求
    $.get(url, data => {
      console.log(data)

      // 用 map 把数据映射成 html代码组成的数组
      // const arr = data.data.list.map(value => {
      //   return `<li>${value.title}</li>`
      // })

      $('ul').html(
        data.data.list.map(value => {
          return `<li>${value.title}</li>`
        })
      )
    })
  </script>
</body>

</html>

练习

html
<!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>练习 17:40</title>
</head>

<body>
  <ul></ul>

  <script src="jquery-3.6.4.js"></script>
  <script>
    var url = 'https://mfresh.kunkun.cool/data/news_select.php'

    $.get(url, data => {
      console.log(data)

      $('ul').html(
        data.data.map(value => `<li>${value.title}</li>`)
      )
    })
  </script>
</body>

</html>

总结

jQuery: 2006年推出的 javascript 脚本库

  • write less, do More: 写的少,做的多

    • 通过封装, 提供的方法名很短
    • 方法基本上都自带遍历操作
    • 方法几乎都有函数重载机制: 根据实参的不同 产生不同的效果
  • $: 是jQuery脚本在 全局window对象中注入的一个属性, 是函数类型

    • $(参数)

      • 参数是字符串: 查找操作
      • 参数是元素: 元素封装成jQuery的对象
    • css: 操作 style 属性

    • class相关

      • addClass: 添加
      • removeClass: 删除
      • toggleClass: 切换
    • 事件相关: click mouseover

      • 事件名(): 触发事件
      • 事件名(函数): 给元素绑定事件
    • 显示和隐藏

      • show 和 hide : 简单的添加 display:none
      • slideToggle, slideDown, slideUp : 带有滑动动画的 显示/隐藏
    • 自定义动画 animate

      • 不适用于 颜色 和 transform
    • 属性相关

      • prop: 系统属性
      • attr: 万能方法, 适用于 系统 和 自定义
      • data: 仅适用于 data- 声明的属性 的 读取操作
    • 内容

      • html(): innerHTML
      • text(): innerText
    • 网络请求: get 方法

      • $.get(接口地址, 回调函数)

jQuery02

新增子元素

html
<!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>新增子元素 09:02</title>
</head>

<body>
  <button>新增</button>

  <ul>
    <li>HTML</li>
    <li>CSS</li>
    <li>SASS</li>
  </ul>

  <script src="jquery-3.6.4.js"></script>
  <script>
    $('button').click(function () {
      // append: 新增
      $('ul').append('<li>新元素</li>')
    })

    // 委托模式: 
    // 前提:事件的冒泡机制 -子元素上触发一个事件后, 会传递给父元素触发相同事件
    // 委托: 让父元素帮子元素完成事件的相关任务

    // 委托模式下: 需要分辨当事元素

    // 提供 on 方法, 特别适合委托场景:
    // 参数1: 事件名
    // 参数2: 可以书写css选择器, 来过滤出要操作的当事元素
    $('ul').on('click', '>li', function () {
      console.log('this:', this) // 代表触发事件的 当事元素

      $(this).css('color', 'red')
    })


    $('li').click(function () {
      $(this).css('color', 'red')
    })
  </script>
</body>

</html>

委托

html
<!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>委托模式 09:18</title>
</head>

<body>
  <div id="box">
    <ul class="boy">
      <li>富贵</li>
      <li>王五</li>
      <li>亮亮</li>
    </ul>

    <ul class="girl">
      <li>楠楠</li>
      <li>梦瑶</li>
      <li>燕子</li>
    </ul>
  </div>

  <script src="jquery-3.6.4.js"></script>
  <script>
    $('#box').on('click', '>.boy>li', function () {
      $(this).css('color', 'red')
    })

    $('#box').on('click', '.girl>li', function () {
      $(this).css('color', 'green')
    })
  </script>
</body>

</html>

练习

html
<!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>练习 09:27</title>

  <link rel="stylesheet" href="reset.css">
  <style>
    ul {
      display: flex;
      flex-wrap: wrap;
    }

    li {
      margin: 0 15px 15px 0;
      box-shadow: 0 0 2px 2px rgba(0, 0, 0, 0.2);
    }
  </style>
</head>

<body>
  <ul>
    <!-- <li>
      <img src="" alt="">
      <span></span>
    </li> -->
  </ul>

  <button>获取更多</button>

  <script src="jquery-3.6.4.js"></script>
  <script>
    // 场景: 一大堆代码,频繁执行, 只要小部分有变更
    // 封装技巧实现复用
    function getData(page) {
      var url = `https://douyu.kunkun.cool/api/room/list?page=${page}&type=yz`

      $.get(url, data => {
        console.log(data)
        // html: innerHTML = xxx 属于覆盖效果
        // append: 累加,新增
        $('ul').append(
          data.data.list.map(value => {
            return `<li>
            <img src="${value.roomSrc}" alt="">
            <span>${value.roomName}</span>
          </li>`
          })
        )

        // 在ul标签上,添加一个属性,存储当前页
        $('ul').attr('data-page', data.data.nowPage)
      })
    }

    // 初始化时, 获取第一页数据
    getData(1)

    // 获取更多
    $('button').click(function () {
      // 读取ul的自定义属性 data-page
      // 默认是字符串的值, 要 *1 转数字
      const page = $('ul').attr('data-page') * 1
      console.log(page)
      getData(page + 1)
    })
  </script>
</body>

</html>

在元素上保存数据 data

html
<!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>data方法 10:19</title>
</head>

<body>
  <!-- 
    data: 可以在元素上存储数据
    当使用data方法读取数据时, 拥有附加功能: 如果有 data- 自定义的属性, 也能帮忙读取
   -->
  <div id="box"></div>

  <script src="jquery-3.6.4.js"></script>
  <script>
    $('#box').data('x', 100)  //在元素上存储一个属性x, 值是100
    console.log($('#box')) // 展开查看, x存放在哪里

    console.log($('#box').data('x'))
  </script>
</body>

</html>

练习: 加载更多

html
<!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>练习 10:25</title>
</head>

<body>
  <ul>
    <!-- <li>新闻标题...</li> -->
  </ul>

  <button>获取更多</button>

  <script src="jquery-3.6.4.js"></script>
  <script>
    function getData(page) {
      var url = 'https://mfresh.kunkun.cool/data/news_select.php?pageNum=' + page

      $.get(url, data => {
        console.log(data)

        $('ul')
          .append(
            data.data.map(value => `<li>${value.title}</li>`)
          )
          .data('page', data.pageNum)
      })
    }

    getData(1)

    $('button').click(function () {
      // 每次点击都要触发相同的代码, 考虑封装
      const page = $('ul').data('page')
      getData(page + 1)
    })
  </script>
</body>

</html>

练习

html
<!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>练习 10:46</title>
</head>

<body>
  <ul></ul>
  <button>获取更多</button>

  <script src="jquery-3.6.4.js"></script>
  <script>
    function getData(page) {
      var url = 'https://serverms.kunkun.cool/mall?page=' + page

      $.get(url, data => {
        console.log(data);

        $('ul').append(
          data.data.map(value => {
            return `<li>${value.name}</li>`
          })
        ).data('page', data.page)
      })
    }

    getData(1)

    $('button').click(function () {
      const page = $('ul').data('page')
      getData(page + 1)
    })
  </script>
</body>

</html>

Node.js

检查node.js 的环境: node -v

  • 确保版本在 12+ 即可

下载地址: https://nodejs.org/zh-cn


npm: 包管理工具

  • 用于下载第三方模块代码, 需要修改下载源. 默认从国外下载, 下载速度慢 甚至无法下载

    设置中国镜像: npm set registry https://registry.npmmirror.com
    查看当前镜像: npm get registry

把项目包使用 node.js 管理

vscode 能够自动识别模块, 进而提供丰富的提示

在 项目包目录下, 执行初始化命令: npm init -y

安装模块: npm i jquery

外部js文件: 准备就绪

js
// 07.js

// 防御性编程: 通常出现在 多人协作 开发场景中
// 需要提前预判 使用者 有可能出现的问题, 提前预案解决

// 预判: 使用此js文件的人, 有可能在head中引入, 导致代码无法查询到DOM元素
// 解决: 监听 DOM元素加载完毕的时候, 再调用代码

// DOMContentLoaded: DOM的内容加载完毕 时触发
addEventListener('DOMContentLoaded', function () {
  // 问题: 在 .js 中书写 jQuery 代码, 没有提示!
  // 解决: 借助node.js的环境, 来提供代码提示
  $('ul').css('border', '2px solid gray')
  // 划线: 代表被抛弃, 不推荐使用.  但是这里是误报
  $('li').click(function () {
    alert("111")
  })
})

// jQuery做了封装, 简化上方的写法:
$(function () {
  $('ul').css('background-color', 'orange')
})

// $(实参):  函数重载有三种情况
// 字符串: 当做 css选择器 进行元素查找
// 元素: 封装成 jquery类型的对象
// 函数: 在DOM元素加载完毕后, 再调用
html
<!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>准备就绪 11:18</title>

  <script src="jquery-3.6.4.js"></script>
  <!-- 此处代码运行时, 下方的body 还没有开始加载, 导致查询不到对应元素 -->
  <script src="./07.js"></script>
</head>

<body>
  <ul>
    <li>HTML</li>
    <li>CSS</li>
    <li>DOM</li>
  </ul>

  <!-- 1个html文件, 可以由3种代码组成: css html js -->
  <!-- 都写在1个文件中, 会导致结构复杂 -->
  <!-- 所以往往把 css 和 js 拆分成外部文件 -->

  <!-- 脚本中依赖 jQuery 脚本, 则必须先引入 jQuery 再引入其他脚本 -->
  <!-- <script src="07.js"></script> -->


  <!-- <script src="jquery-3.6.4.js"></script> -->
  <!-- 正常人: -->
  <!-- <script src="07.js"></script> -->

  <script>
    $('li').css('color', 'red')
  </script>
</body>

</html>

load :加载外部html

html
<!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>加载外部html文件 14:02</title>

  <style>
    body>div {
      border: 3px solid black;
      margin-bottom: 20px;
      padding: 20px;
    }
  </style>
</head>

<body>
  <div id="demo01"></div>
  <div id="demo02"></div>
  <div id="demo03"></div>
  <div id="demo04"></div>

  <script src="jquery-3.6.4.js"></script>
  <script>
    // 此语法仅支持通过服务器运行的html文件
    // 参数1: 要加载文件地址
    // 参数2: 加载完毕后的回调函数
    $('#demo01').load('./02.delegate.html', function () {
      $('ul').css('border', '2px solid green')
    })

    // load操作是异步操作, 类似网络请求
    // 必须在 load 操作完毕后, 才能去操作加载过来的元素
    // $('ul').css('border', '2px solid green')

    // $('#demo02').load('./06.practice.html')

    // $('#demo03').load('./05.practice.html')
  </script>
</body>

</html>

BOM

DOM: Document Object Model 文档对象模型

  • 操作页面上的内容

BOM: Browser Object Model 浏览器对象模型

  • 操作浏览器的一些 API 合称 BOM

例如 location 用于操作浏览器地址栏相关, 属于 BOM 的范畴

location

html
<!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>位置信息 14:19</title>
</head>

<body>
  <button onclick="location.reload()">reload 刷新</button>

  <!-- 无法返回之前的页面 -->
  <button onclick="location.replace('http://www.baidu.com')">替换</button>

  <!-- assign: 指派;  带有历史操作, 能够返回上一页 -->
  <button onclick="location.assign('http://www.baidu.com')">指派新页面</button>

  <!-- 路径的格式:   路径?参数=值&参数=值... -->
  <a href="?name=kaikai&age=18&phone=18873734444">路径参数</a>

  <script>
    console.log(location)
    // URLSearchParams : 专门用于读取路径中参数的构造函数
    const params = new URLSearchParams(location.search)
    console.log(
      'name:', params.get('name')
    );
  </script>
</body>

</html>

刷新保持输入框内容

html
<!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>练习 14:41</title>
</head>

<body>
  <!-- 模拟百度的 搜索栏内容, 页面刷新不丢失的效果 -->
  <input type="text">
  <button>百度一下</button>

  <script src="jquery-3.6.4.js"></script>
  <script>
    // 页面初始化时, 立刻从地址栏中读取参数
    const wd = new URLSearchParams(location.search).get('wd')
    // 设置到 输入框中, 作为其默认值显示
    $('input').val(wd) // value = wd


    // 当点击 百度一下 按钮时, 把输入框的值读取 然后放到地址栏参数中
    $('button').click(function () {
      // 输入框的值: value
      // 作者简化成 val() 方法, 用于操作输入框的值
      const wd = $('input').val()
      console.log('wd:', wd)

      location.assign('?wd=' + wd)
    })
  </script>
</body>

</html>

全局css变量

html
<!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>css全局变量 15:14</title>

  <style>
    html {}

    /* 伪类选择器: 代表页面的根元素, 即 html 标签, 习惯在这里声明全局变量 */
    :root {
      /* css变量的格式: --变量名: 值; */
      --color-red: rgb(0, 200, 0);
      /* 在 html 标签下的所有后代元素中, 都可以使用此变量 */

      /* 全局变量的优点: 便于维护; 只要修改变量的值, 就可以影响到所有使用了此变量的元素 */
    }

    li:nth-child(2) {
      /* var(变量) : 此语法找到变量的值 */
      color: var(--color-red);
    }

    ul {
      border: 2px solid var(--color-red);
    }
  </style>
</head>

<body>
  <!-- 变量: 可以多次 重复的使用 -->

  <ul>
    <li>HTML</li>
    <li>CSS</li>
    <li>JS</li>
  </ul>
</body>

</html>

ToolChain

前端工具链

传统的web制作: 书写 HTML CSS JS ...

  • 小作坊, 手工业

现代的web制作采用工程化的模式

  • 流水线:使用一套规范的工具, 按照固定的流程进行开发
  • 有了规范之后, 就可以保障最终产物的质量 和 开发效率

Webpack

https://webpack.docschina.org/

传统开发的问题

关于使用外部的js脚本时, 如何知道引入的脚本 是否依赖其他的脚本呢?

  • 例如:引入demo.js之后, 还必须引入其依赖的 jQuery.js文件

  • 期望的解决方案: 外部js脚本 能够自己携带自身依赖的其他脚本, 不需要在html中引入

    自己的事情自己做!!

关于外部的scss文件

  • 在现代的开发中, 大量采用预编译的scss语言. 但是每次都要编译成css再运行.
  • 期望: 能否自动编译? 目前采用插件解决

关于服务器:

  • 目前用live server 插件解决

最终期望:能否有一个 一站式解决方案

  • 一款工具, 其中自动提供: 服务器 + scss编译 + 解决脚本的依赖问题

webpack工具诞生!

Webpack

是Node.js提供的模块

最基本的功能: 支持把Node.js 的模块化语法 转换成 普通的浏览器支持的脚本

  1. 创建项目包: webpack-demo

  2. 初始化成npm管理的环境: npm init -y

  3. 安装webpack模块:npm i webpack webpack-cli

总结

前端工具链: 把一系列的开发工具整合在一起, 有序的运行. 最终提供完善的代码产物

  • webpack: 实现工具链的一款工具
  • vite: 也是类似的工具
  • ...

第四阶段学习的 vue 框架:

  • 就是用 webpack 搭建而来的

第五阶段学习的 React 框架:

  • 也是用webpack搭建的

webpack是给高级/架构工程师使用的工具

js
// index.js
// 模块化语法: Node.js环境下提供的, 浏览器不支持
// 允许在JS文件中, 引入来自其他JS的模块
// webpack: 能够自动把 模块化语法转为普通的浏览器支持的脚本
// 执行命令: npx webpack
// 效果: 把 jQuery脚本的代码 和 当前文件的代码合并在一起, 存储在main.js中

// 安装jquery模块: npm i jquery
const $ = require('jquery')
const axios = require('./axios')

// 在js中引入外部的css文件
// webpack基础功能仅能打包JS类型的文件, 如果要操作其他类型文件, 则比如使用对应的 加载器- loader
// 1. 安装加载器模块: npm i css-loader style-loader
import './my.css'
// npm i sass-loader sass
import './my.scss'

// 图片类型的文件: 需要特殊配置后才能加载,  默认已经集成了加载器, 只要配置即可
import bigskin4 from './bigskin-4.jpg';
const img = new Image(200, 140)
img.style.border = '3px solid red'
img.src = bigskin4
$('body').append(img)

$('h1').css('color', 'red')

var url = 'https://api.kunkun.cool/car/news.json'
// 传统开发时, 如果使用来自外部脚本中的内容, 没有代码提示
// 但是在 模块化开发时, 会给出提示!  -- 重大升级
axios.get(url).then(res => res.json()).then(data => {
  console.log(data)
})

// 编译后, main.js 会自动更新
js
// index.js

// 关于开发环境:
// - 目前每次修改代码,都要手动执行 编译命令
// - webpack提供了监听模式, 自动监听页面的内容变化, 进行编译
// 需要在 package.json 中进行配置启动的脚本

// 关于开发服务器: webpack提供了热更新服务器
// npm i webpack-dev-server
// 执行命令: npm run start  启动服务器
// 特点: 比 live server 插件提供的服务器, 给出更多的报错提示. 辅助程序员写出更好的代码
// 从4阶段开始, 都会使用 webpack 自带的服务器来进行开发

// index.html 生成的插件
// npm i html-webpack-plugin

// ES6模块引入语法, 和 const $ = require('jquery') 作用一致
// 仅仅语法有所不同 
import $ from 'jquery'

$('h1').css('color', 'blue')

$('body').append(
  `<p>Hello !!</p>`
)
js
// ES6(ES2015)中提供的代码
// 兼容性问题: 这些语法无法适配2016年之前的浏览器

// babel: https://www.babeljs.cn/
// 可以把 ES6+ 的代码降级为 ES5  即 ES2009 的代码格式

// 官方文档
// https://webpack.docschina.org/loaders/babel-loader

// npm i babel-loader @babel/core @babel/preset-env
// 在配置文件中进行加载

var url = 'https://api.kunkun.cool/car/news.json'

fetch(url).then(res => res.json()).then(data => {
  console.log(data)
})

////////////////
class Emp {
  constructor(ename, age) {
    this.ename = ename
    this.age = age
  }
}

var emp1 = new Emp('小明', 22)
console.log({ emp1 })
html
<!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>
  <h1>Hello Webpack!</h1>

  <!-- <script src="./src/index.js"></script> -->
  <!-- 引入浏览器支持的脚本 -->
  <script src="./dist/main.js"></script>
</body>

</html>

面向对象 OOP

面相对象开发有三大特征: 封装, 继承, 多态

  • 封装: 中级程序员 需要掌握
    • function 或 class 把代码封装成一个整体使用
  • 继承: 高级程序员需要掌握, 在封装框架中使用
  • 多态: 封装框架时使用 - 高级程序员的领域

封装

html
<!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>封装 14:34</title>
</head>

<body>
  <script>
    // 封装: 把一些代码存储在一起, 后续可以复用
    function get(url, cb) {
      var xhr = new XMLHttpRequest()
      xhr.open('get', url)
      xhr.send()
      xhr.onload = function () {
        cb(JSON.parse(xhr.response))
      }
    }

    // 复用: 重复使用
    get('https://api.kunkun.cool/car/news.json', data => {
      console.log(data)
    })

    var emp = { ename: "小明", age: 22 }
  </script>
</body>

</html>

继承

html
<!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>继承 14:41</title>
</head>

<body>
  <!-- 继承: 1个人可以拥有另外一个人的所有资源 -->
  <!-- 面相对象中的继承: 子类 可以拥有 父类的 所有属性和方法 -->

  <script>
    // 提取公共的代码
    class Animal {
      constructor(name, category) {
        this.name = name
        this.category = category
      }
    }

    // 制作几个构造函数: 猫  狗  羊
    // extends: 继承; Cat就可以使用Animal中的方法
    class Cat extends Animal {
      // constructor(name, category) {
      //   this.name = name
      //   this.category = category
      // }

      cry() {
        console.log('喵喵...');
      }
    }


    class Dog extends Animal {
      // constructor(name, category) {
      //   this.name = name
      //   this.category = category
      // }

      cry() {
        console.log('汪汪...');
      }
    }

    class Sheep extends Animal {
      // constructor(name, category) {
      //   this.name = name
      //   this.category = category
      // }

      cry() {
        console.log('咩咩...');
      }
    }

    var cat_mimi = new Cat('咪咪', 4)
    var dog_wangcai = new Dog('旺财', 5)
    var sheep_duoli = new Sheep("多利", 3)
    console.log(cat_mimi);
    console.log(dog_wangcai);
    console.log(sheep_duoli);
  </script>
</body>

</html>

多态

html
<!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>多态 15:17</title>
</head>

<body>
  <!-- 继承: 1个人可以拥有另外一个人的所有资源 -->
  <!-- 面相对象中的继承: 子类 可以拥有 父类的 所有属性和方法 -->

  <script>
    // 提取公共的代码
    class Animal {
      constructor(name, category) {
        this.name = name
        this.category = category
      }
      // 公共的方法: 所有动物都会 叫, 但是具体如何叫要看具体的子类
      cry() { }
    }

    class Cat extends Animal {
      // 重写: override;  把父元素提供的属性 重新书写
      cry() {
        console.log('喵喵...');
      }
    }


    class Dog extends Animal {
      cry() {
        console.log('汪汪...');
      }
    }

    class Sheep extends Animal {
      cry() {
        console.log('咩咩...');
      }
    }

    var cat_mimi = new Cat('咪咪', 4)
    var dog_wangcai = new Dog('旺财', 5)
    var sheep_duoli = new Sheep("多利", 3)
    console.log(cat_mimi);
    console.log(dog_wangcai);
    console.log(sheep_duoli);
    // 同样的方法, 由于子类的重写, 使用时出现不同的状态: 多态
    cat_mimi.cry()
    dog_wangcai.cry()
    sheep_duoli.cry()
  </script>
</body>

</html>

继承 function

html
<!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>继承: function方案 15:24</title>
</head>

<body>
  <!-- 
    构造函数的发展过程:
    早期: 用 function 实现: 在实现继承时特别复杂.

    ES6之后: class 语法;  更简单 更易用, 广受欢迎

    实际开发中, 很少见到 用 function 实现继承的

    但是: 面试会考
   -->
  <script>
    function Animal(name, category) {
      this.name = name
      this.category = category
    }

    Animal.prototype.cry = function () { }

    // 继承写法:
    function Cat(name, category) {
      // 触发Animal的构造方法
      Animal.call(this, name, category)
      // call(): 触发函数, 并且指定函数中的this指向
      // 把 cat 对象传递给 Animal 进行初始化的赋值
    }

    // 修改Cat 的原型的原型 为 Animal 的原型
    // create: 创建一个对象, 对象的原型是 Animal.prototype
    Cat.prototype = Object.create(Animal.prototype)
    Cat.prototype.constructor = Cat  //设置原型的构造函数

    console.dir(Cat)
  </script>
</body>

</html>

对象的访问权限

html
<!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>对象访问权限 15:42</title>
</head>

<body>
  <script>
    'use strict'

    var emp = {
      ename: '小明',
      age: 33,
      salary: 30000
    }

    // 把薪资改为 25000
    // 默认设定: 如果点语法赋值, 属性名不存在 则为新增操作
    // 个性化要求: 只允许修改, 不允许新增属性
    // prevent阻止;  Extensions:扩展,扩充
    // Object.preventExtensions(emp)

    // 密封: seal;  不允许增删元素
    // Object.seal(emp)

    // freeze: 冻结;  不允许增删改
    Object.freeze(emp)
    emp.ename = '孙凯'

    // delete emp.age
    // emp.salay = 25000
    console.log(emp)
  </script>
</body>

</html>

Released under the MIT License.