JavaScript
约 36585 字大约 122 分钟
2025-08-25
轻量级的编程语言、行为(Behavior)
JavaScript - 文档
菜鸟教程 - JavaScript - 文档
w3cschool - JavaScript - 文档
在线工具 | 菜鸟工具
JS 模块化
复用性、可维护性
Commonjs 规范
//导出一个模块
module.exports = {};
//引入一个模块
const moduleName = require("xxx");es module 规范
// 导出 一个模块
export default {};
// 引入一个模块
import moduleName from "xxx";JS 基本语法形式
JavaScript 的语法形式 类似于 css 语法形式
- 行内式
在标签内部直接定义 js 代码 (实际项目中绝对禁止使用)
- 内部式
在 script 标签中 定义 js 代码
- 外部式
在 外部 js 文件中 定义 js 代码
通过 scirpt 标签 src 属性 导入外部 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>Document</title>
</head>
<body>
<!-- 1.行内式 -->
<div onclick="window.alert('我是js定义的代码内容!')">嘿呀😉😉😉</div>
<!-- 2.内部式 -->
<div>嘿呀😉😉😉</div>
<script>
// 在 script标签中 对 div标签 进行交互操作
const oDiv = document.querySelector("div");
oDiv.addEventListener("click", function () {
this.style.color = "red";
this.style.width = "400px";
this.style.height = "200px";
this.style.background = "pink";
});
// 内部式 总结
// 1. 计算机程序代码 默认的执行顺序是 从上至下 从左至右( 文档流顺序 )
// 2. script标签 style标签 理论上可以定义在 html文件的任意位置
// 3. 因为代码的执行顺序问题
// js程序要操作 html标签 就要先 定义生成html标签
// 实际项目中 一半都将 script标签 也就是 js程序 (定义在 body标签中 html标签下)
</script>
<!-- 3.外部式 -->
<script src="./demo.js">
// JavaScript代码的执行机制 script标签定义了src属性 这个内部式代码就不会执行了
window.alert('我是内部式程序');
</script>
</body>
</html>JS 变量
- 概念
存储在内存中的 带有名称的数据信息 其中存储的数据信息 在程序执行的过程中 是 可以改变的。
概念 理解
理解
容器
存储数据的容器
这个容器 带有名称
这个容器 是 在 内存中
这个容器 存储的数据 可以改变详细解释
在 计算机程序 中 数据是不能独立存储的
必须要有存储数据的容器
实际操作时 通过操作容器 解析其中存储的数据
再 操作这个容器中 存储的数据
在 JavaScript 程序中 存储 数据的容器 称为 变量
在 html程序中 存储 容器的容器 标签name属性的属性值- 内存和内存特点
内存 是 计算机的硬件组成部分;
特点是: 临时存储数据、程序执行时数据存在、程序执行结束 数据自动 删除/释放
硬盘 u 盘 移动硬盘等 是 永久存储数据
- 变量定义声明的语法
例如: var num = 999;
var num = 999; 详细解析
var
var 声明的关键词 JavaScript程序会做特殊处理(预解析/预编译/预解释)
定义声明有一个变量一定要有关键词num
变量名称
JavaScript命名规范
1. 【规则 必须遵守】
只能使用、英文、数字、下划线、$(不推荐使用)、
不能以数字开头、严格区分大小写、
不能使用 保留词 关键词
2. 【规范 建议遵守】
不要使用 中文特殊符号等内容 作为变量名称(当程序在服务器中运行时 一定会报错)、
推荐使用 小驼峰命名法、推荐定义变量名称 见名知意(语义化)=
= 在 计算机程序中 成为 赋值符号
将 右侧 表达式的结果 赋值给 左侧变量存储
一定是 先计算 右侧表达式的结果
将 表达式的结果 赋值给左侧变量存储999
右侧的表达式
所谓的表达式 就是 有结果的公式
可以是 计算公式 也可以是 一个数据 只要有 具体的结果 具体的值 就可以;
JavaScript代码都要以分号结束重复赋值
给 一个变量 多次赋值
eg: var num = 999; num = 100; num = 500;
运行最终的结果是 500JS 数据类型
对 JavaScript 数据类型的解析 有以下几点 :
- 数据在计算机内存中存储方式不同
- 按照不同的存储方式 将 数据进行 分类
- 不同存储方式的数据 分类不同
- 成为 数据的 数据类型(存储方式)
JavaScript 中数据类型 分类
JS数据类型 分类
基本数据类型 / 简单数据类型
布尔类型(boolean / bool) ===> (只有 true false)
数值类型(number)
整数类型(integer / int)
浮点数类型(float)
NaN
字符串类型(string / str)
null类型
undefined类型引用数据类型 / 复杂数据类型
函数
数组
对象对上面的 数据类型 进行解释说明
1. 布尔类型(boolean / bool):
布尔类型只有两个数值
布尔类型 往往是 比较判断的执行结果
true ===> 表示 真 / 正确
false ===> 表示 假 / 错误
true / false 是 JavaScript中的关键词(必须小写) 可以直接赋值2. 字符串类型(string / str)
一串 由 字符 数字 符号等等内容组成的数据
字符串类型的表示 必须有 定界符
定界符中定义的内容 就是 字符串的内容
// 只要是 定界符包裹的 就是 串
// 只要是 没有定界符包裹的 就是 值
定界符的分类
单引号'' 和 双引号""
实际项目中推荐使用的定界符 执行效率更高
单引号/双引号 不能 包裹/嵌套 自身
单引号 双引号 中 内容不能换行
单引号 双引号 中 不支持解析变量
若一定要 嵌套或者换行 就必须 使用 JavaScript转义符
常见的转义字符有:
// \' ===> 表示一个 单引号 '
// \" ===> 表示一个 双引号 "
// \\ ===> 表示一个 \
// \n ===> 表示一个 换行
反引号``(模板字符串)
是 ES6 新增的语法形式
反引号中不能 包裹/嵌套自身
反引号中可以换行
反引号中支持解析变量
如果没有变量需要解析 推荐使用 单引号双引号 执行效率更高
模板字符串 使用 ${} 包裹变量 可以解析变量
模板字符串 使用 ${} 还可以 运算 JavaScript表达式3. 数值类型
3.1 整数类型
负整数 0 正整数
其他进制整数的存储语法
JavaScript中数值的存储输出 默认都是 按照 十进制数值进行的
其他进制的数值 必须要 定义 标识符
JavaScript程序才会按照 指定的进制 进行数据的存储
3.2 浮点数
浮点数 就是 小数
核心问题1 有效数值
在 计算机程序中 不会允许小数的位数是 无穷多位
在 JavaScript程序中 浮点数的 有效数值 最多17位
从左起 第一个 非0的数字 开始计算有效数值的位数
也就是 所有的有效数值 最多 17个
核心问题2 溢出 / 精度丢失 / 误差
在 计算机程序中 浮点数的存储形式 是 以 近似值的形式存储数据
即 0.1 存储的数据可能是 0.100000000000002
在 浮点数直接运算 或者 比较时 一定会 出现 浮点数 的 误差 / 溢出 / 精度丢失
核心问题3 科学计数法
是 数值的一种 表现实行 表示语法
eg: 数e数 2e3 的结果是 6
科学计数法表达式的一切数值 都是 浮点数类型
// 科学计数法 不算 整数的语法形式
3.3 NaN
not a number
表达式运算结果 是 数值类型 但是 不是一个具体的数字
往往是 有 非数字 参与运算的结果
NaN 可以作为数据数值 直接赋值给变量
赋值时 一定是 严格遵守大小写
核心1
有 NaN 参与的运算结果 一定 还是 NaN
核心2
两个 NaN 比较 是不是 相等 结果是 false4. null 和 undefined
null 表示 空值
是 正确赋值 但是 赋值的数据 是一个 '空' 值
undefined 表示 应该赋值 没有正确赋值
在 定义变量时 应该给变量赋值存储的数据
如果 只是定义变量 没有赋值存储的数据
JavaScript程序 会给没有赋值数据的变量 存储 undefined 数据
null 和 undefined 都可以作为数值 直接赋值给变量
必须都是 小写字符JavaScript 中数据类型 检测
JS数据类型 检测
1.typeof
语法
typeof( 变量 / 数据 )
typeof 变量 / 数据
执行结果返回值是 数据的数据类型
返回值 是 字符串类型的内容
布尔类型(boolean)
数值类型(number)
整数 浮点数 NaN 科学计数法
在 JavaScript中 整数 浮点数的数据类型 没有区别
字符串类型(string)
undefined(undefined)
null / 数组 / 对象 (object)
函数(function)2. isNaN()
判断 数据/变量 是不是 NaN 类型
原理:
1, 将数据 自动转化为数值类型
2, 再去判断 转化的结果 是不是 NaN
结果:
如果 转化结果 是 NaN 返回值 是 true
如果 转化结果 不是 NaN 返回值 是 falseJavaScript 中数据类型 转化
JS数据类型 转化
数据类型的转化原理:
JavaScript是 弱类型 计算机语言 语法结构比较松散不严谨
定义变量时 不限制变量储存数据的数据类型
也就是 一个变量 可以存储任意类型的数据
在 定义变量时 可以 同时给变量赋值
如果是强类型的计算机语言 例如 java
是 先定义变量 设定变量存储数据的数据类型
再 对变量进行赋值
如果赋值的数据 和 变量设定的存储的数据的数据类型不同
不能对变量进行赋值
计算机程序执行时 有些程序执行 需要 指定类型的数据
如果变量中存储的是其他类类的数据
需要将数据转化为对应的数据类型转化方法:
自动转化 / 隐式转化
计算机程序执行时 自动完成的数据类型转化
强制转化 / 人为转化
程序员 通过 JavaScript定义的函数 完成的 数据类型转化1. 自动转化:
核心
什么时候转化
怎么转化
自动转化为 字符串类型
字符串拼接 时 触发自动转化
将 数据 转化为对应的 字符串内容
自动转化为 数值类型
算数运算符时触发
! 有字符串参与的 + 加号运算 执行的是 字符串拼接
1
true
0
false '' ' ' null
对应数值
字符串内容符合数字语法
NaN
undefined 字符串内容不符合数字语法
自动转化为 布尔类型
if语句 三元运算符 逻辑运算符
false
0 0.0000.. null undefined NaN ''
true
其他情况都转化为 true2. 强制转化:
转化为 布尔类型
Boolean( 变量 / 表达式 )
不会改变 变量中存储的原始数据
转化原则和自动转化完全相同
转化为 字符串类型
String( 变量 / 表达式 )
不会改变 变量中存储的原始数据
转化原则和自动转化完全相同
变量.toString()
不会改变 变量中存储的原始数据
转化原则和自动转化完全相同
! 如果变量中存储的是整数 可以设定转化的进制
范围是 2 - 36
! 如果变量中存储的是 null 或者 undefined 不支持
转化为 数值类型
Number( 变量 / 表达式 )
不会改变 变量中存储的原始数据
转化原则和自动转化完全相同
parseInt()
获取 整数部分
从左起 获取 数据中 符合整数语法的内容
如果左起第一个内容不符合整数语法 结果是 NaN
! true false undefined null 等 结果 都是 NaN
! '100px' 这样的字符串结果是 整数100
! 不能识别 字符串内容是 科学计数法的语法形式 '2e3' 获取的结果是 2
parseFloat()
获取 浮点数部分
从左起 获取 数据中 符合浮点数语法的内容
如果左起第一个内容不符合浮点数语法 结果是 NaN
! 可以识别 字符串内容是 科学计数法的语法形式 '2e3' 获取的结果是 2000JS 运算符
JS运算符 介绍
概念:
JavaScript程序定义的 带有特殊功能的符号分类:
1.字符串拼接运算符
+
符号两侧有字符串参与 执行的是字符串拼接运算
2.算数运算符
+ - * / %
/
如果除数是 0 执行结果是 Infinity
%
求余数 求模 运算
获取两个数相除的余数结果
获取的结果 符号和被除数相同
如果整除 余数是 0
3.赋值运算符
= += -= *= /= %=
4.比较运算符
> < >= <= == === != !==
5.自增自减运算符
++ --
6.逻辑运算符
&& || !
7.三元运算符
执行逻辑 就是 简单的 if...else语法
语法形式1 ===> 执行
表达式 ? 程序1 : 程序2 ;
表达式 结果是 true 执行程序1
表达式 结果是 false 执行程序2
语法形式2 ===> 赋值
var 变量 = 表达式 ? 数据1 : 数据2 ;
表达式 结果是 true 给变量 赋值 数据1
表达式 结果是 false 给变量 赋值 数据2
语法形式3 ===> 解析
` ${ 表达式 ? 数据1 : 数据2 } `
表达式 结果是 true 三元运算符在模板字符串中 解析的结果是 数据1
表达式 结果是 false 三元运算符在模板字符串中 解析的结果是 数据2JS 数据的存储原理
JS数据的存储 原理
基本数据类型:
存储在 栈 中
有序存储
先定义的变量 存储在 栈 的下方
后定义的变量 存储在 栈 的上方
变量中直接存储 数据
调用基本数据类型
直接 解析变量中存储的数据数值
引用数据类型:
存储在 堆 中
无序存储
在 堆 中开辟独立的存储空间
操作系统给存储空间赋值设定 内存地址
引用数据类型 数据数值/代码程序 存储在 存储空间中
变量/函数名称 存储在 栈 中
变量/函数名称 中 存储的是 引用数据类型的内存地址
调用 引用数据类型
解析读取 栈 中 变量/函数名称中 存储的内存地址
按照 内存地址找到 堆 中的存储空间
读取存储空间中 存储的 数据数值/代码程序
基本数据类型 比较判断的是 变量中存储的 数据数值
引用数据类型 比较判断的是 变量/函数名称中存储的 内存地址
**两个引用数据类型 直接比较判断 结果 永远是 false**JavaScript变量储存基本原理图
JS 函数
对比 break return throw
JS函数 介绍
函数
预解析 / 预编译 / 预解释
是 JavaScript程序一种执行机制
在 正式执行JavaScript程序之前 先将所有的js程序代码 预读 一遍
对 var 声明的变量 function声明的函数 做 特殊处理
处理的结果是
var 声明的变量 提前调用结果是 undefined
function 声明的变量 提前调用结果是 正常执行
声明式函数 / 普通函数 提前调用 可以正常执行
语法:
function 函数名称([形参 , 形参=默认值 ...]){
程序;
return 返回值;
}
赋值式函数 / 匿名函数 本质是在定义变量
提前调用 变量中存储的是 undefined 作为函数调用结果是报错
语法:
var 变量 = function([形参 , 形参=默认值 ...]){
程序;
return 返回值;
}变量的作用域
全局变量
定义在 函数外的变量 称为 全局变量
可以 在函数内和函数外 都能调用
局部变量 / 私有变量
定义在 函数内的变量 或者 形参 称为 局部变量
只能在函数内部调用
不能在函数外部直接调用函数中变量的调用原则
就近原则
自己有调用自己的
自己没有调用父级程序的
父级程序没有调用全局的
全局没有 调用结果是 报错
永远不会调用 后代程序的函数中变量的赋值原则
就近原则
自己有这个变量 对 自己的变量赋值
自己没有这个变量 对 父级变量赋值
父级程序没有这个变量 对 全局变量赋值
全局没有这个变量
变量的赋值语句 升级为变量的声明语句
声明一个 全局变量 在函数外可以调用
绝对不会对 后代变量 赋值函数的封装调用的基本过程
封装
1, 在 堆 中开辟一个独立的存储空间准备存储引用数据类型
操作系统给这个存储空间定义内存地址
2, 函数程序代码 以 字符串形式 存储在 存储空间中
3, 函数名称存储在 栈 中
函数名称中存储的是 函数的内存地址
调用
1, 在 栈 中 找到 函数名称 读取其中存储的内存地址
按照内存地址 找到 堆 中 对应的存储空间
解析其中存储的函数程序字符串
2, 给 函数的形参 赋值 实参
3, 预解析函数程序代码
4, 执行 函数程序一些面试题的核心
1. 预解析的面试题
变量名称重复
正常调用 调用的是 最后一个定义的
提前调用 结果是 undefined
函数名称重复
正常调用 调用的是 最后一个定义的
提前调用 调用的是 最后一个定义的
变量名称 和 函数名称重复
正常调用 调用的是 变量
提前调用 调用的是 函数
赋值式函数按照变量处理
2. 函数中数据的调用
(1) 先看 函数中 有没有 定义声明这个变量
如果定义声明了这个变量 一定只会调用函数中的这个变量
正常调用 就是 这个变量储存的数据
提前调用 就是 undefined
如果没有定义这个变量
找父级程序 / 全局变量 调用
(2) 如果 整个程序 都没有定义这个变量
看 有没有 赋值程序
如果有赋值程序 赋值程序 会 升级为变量的声明语句
正常调用 就是 这个变量赋值的数据
提前调用 就是 报错
如果没有赋值程序 调用都是报错名词解释
1.function
声明函数的关键词 告诉 JavaScript程序 我们在声明一个函数
2.形参
在 封装函数时 定义在 函数 () 中的
本质 就是 定义在函数中的变量
调用 函数时 可以通过实参 给 形参赋值实际的数据
在 定义形参时 也可以给 形参赋值默认值
函数可以有形参 可以没有形参 可以有多个形参
JavaScript函数中 形参还以有其他的语法形式
我们之后讲 展开合并运算符 时候讲
3.实参
在 调用函数时 定义在 函数 () 中的
本质是 给 形参赋值的数据
按照顺序 给 形参 一一对应 赋值
4.return
定义一个函数的 执行结果 / 返回值
定义在函数内部的数据 在函数外部不能直接调用
如果要调用函数内部的数据 需要通过 return 定义返回值
将 数据抛出函数之外
函数的执行结果/返回值 就是 return 定义的数据
return定义的数据 可以 任意的操作使用
return可以终止函数中 之后程序的执行
5.对比 break return throw
break 只能 终止 循环内的程序
return 只能 终止 函数中的程序
throw 能终止 整个 js程序的执行JS 数组
JS数组 介绍
概念:
存储数据的集合理解:
其他数据类型 一个变量中只能存储一个数据单元
在数组中可以同时存储多个数据单元语法:
字面量
var 变量 = [数据1 , 数据2 , 数据3....];
构造函数
var 变量 = new Array(数据1 , 数据2 , 数据3....);属性:
下标
在定义数组时 JavaScript程序 自动定义的从0开始的连续整数
如果下标是 数值类型 称为 索引下标
在 JavaScript中 下标只支持是数值类型的索引下标
在 其他计算机语言中 数组的下标可以是字符串类型 称为 关联下标
通过 索引下标 使用 []语法 操作 数组中的数据单元
length
数组的长度 也就是 数组中单元个数
语法 数组.length
索引下标是从0开始的连续整数
数组索引下标的范围是 0 至 数组.length-1数据存储的数据类型:
在 JavaScript语言中 数组中可以存储任意 JavaScript支持的数据类型
一个数组中 可以 存储不同类型的数据数组中存储函数:
var arr = [100,200,300,function(){} ];
一般情况下 数组中 不会存储函数
数组[索引下标] 获取的是 数组单元中存储的内存地址
数组[索引下标]() 是 调用执行 数组中存储的函数数组中存储数组:
多维数组的调用
var arr = [100,200,300,[400,500,600,[700,800,900]]];
使用 多个并列的 []语法
调用对应的多维数组 获取其中存储的数据
如果需要调用800
arr[3][3][1]
实际项目 会使用 解构赋值语法 对 多维数组进行数据获取数组中存储对象:
var arr = [
{id:1 , name:'张三' , age:18 , sex:'男'},
{id:2 , name:'李四' , age:19 , sex:'女'},
{id:3 , name:'王五' , age:20 , sex:'男'},
{id:4 , name:'赵六' , age:21 , sex:'女'},
{id:5 , name:'刘七' , age:22 , sex:'男'},
];
通过索引下标 获取 对应的对象
再通过对象的取值语法 获取具体的数据
如果需要获取王五
arr[2].name
arr[2]['name']数组的基本操作方法:
获取:
数组[索引下标]
如果 索引下标不存在 获取结果是 undefined
新增:
对 不存在的数组单元 进行赋值操作
执行效果是 给数组新增单元 并且设定新增单元的索引下标 和 存储的数据数值
新增单元的索引下标 必须是 连续的整数
如果不是连续整数 会生成 empty 空单元
调用 空单元 结果是 undefined
数组[索引下标] = 数值数据 ;
实际项目中 一般使用函数方法 给数组新增单元
修改:
对 已经存在的数组单元 进行 赋值操作
执行效果是 后赋值的数据覆盖之前存储的原始数据
数组[索引下标] = 数值数据 ;
索引下标 是 已经存在的数组的函数操作:
比较重要的
首位新增:
var 变量 = 数组.unshift( 数据1 , 数据2.... );
在数组的首位新增数据单元 可以 新增一个单元 也可以新增多个单元
执行结果返回值是 新增单元之后 新数组的length属性值
末位新增:
var 变量 = 数组.push( 数据1 , 数据2.... );
在数组的末位新增数据单元 可以 新增一个单元 也可以新增多个单元
执行结果返回值是 新增单元之后 新数组的length属性值
首位删除:
var 变量 = 数组.shift();
每次执行只能删除数组首位单元
执行结果返回值是 删除单元存储的数据数值
末位删除:
var 变量 = 数组.pop();
每次执行只能删除数组末位单元
执行结果返回值是 删除单元存储的数据数值
指定删除:
var 变量 = 数组.splice( 参数1 , 参数2 , 其他所有参数 );
参数1 删除起始位置的索引下标
0 整数 起始位置的索引下标
负数 从倒数第几个开始删除
参数2 删除数组单元的个数
0 整数 删除单元的个数
负数 没有执行效果 也就是没有删除的单元
其他所有参数
在删除位置写入的新增数据单元
如果删除的是 0 个单元 新增写入的单元 在 删除起始位置前新增单元
数组 ===> 字符串:
获取数组中每一个数据单元存储的数据数值 拼接成一个字符串
var 变量 = 数组.join();
没有设定参数 间隔符号 默认是 , 逗号
var 变量 = 数组.join('间隔符号');
设定间隔符号 以 间隔符号为间隔 拼接生成字符串
var 变量 = 数组.join('');
设定间隔符号 为 空字符串 也就是 没有间隔符号
字符串 ===> 数组:
将 字符串 按照 指定的内容 进行分割 将分割的单元作为数组的数据单元存储
var 变量 = 字符串.split();
没有设定参数 整个字符串作为一个整体 存储到 数组中
也就是 数组中 只会存储一个数据单元 是 整个的字符串
var 变量 = 字符串.split('间隔符号');
如果设定间隔符号 以 设定的间隔符号 分割数组
分割结果中 没有 间隔符号
var 变量 = 字符串.split('');
如果设定间隔符号 是 空字符串
以 每一个字符串 作为 一个单元 转化为数组
数组的查询操作:
var 变量 = 数组.indexOf(数据);
查询 指定 数据 在 数组中第一次出现的位置
如果有匹配的数据 返回值 是第一次出现位置的 索引下标
如果没有匹配的数据 返回值 是 -1
var 变量 = 数组.lastIndexOf(数据);
查询 指定 数据 在 数组中最后一次出现的位置
如果有匹配的数据 返回值 是最后一次出现位置的 索引下标
如果没有匹配的数据 返回值 是 -1
数组的排序:
数组.sort();
按照 首字符 顺序排序
本质上是按照 ASCII码表排序
所谓的 ASCII码 是 一种 编码格式
所有的字符 都有对应的 码表数值
数组.sort(function( 参数1 , 参数2 ){ return 参数1 - 参数2 })
数值 小 --- 大
数组.sort(function( 参数1 , 参数2 ){ return 参数2 - 参数1 })
数值 大 --- 小实际项目中经常经常使用的
数组的映射:
var 变量 = 数组.map( function( 参数1 , 参数2 , 参数3 ){
return 操作 ;
})
自动循环遍历原始数组的每一个单元
参数1 存储原始数组数据单元的 数据数值
参数2 存储原始数组数据单元的 索引下标
参数3 存储原始数组本身
将 设定的操作结果 通过 return 作为 范围值 生成 一个新的数组
一般情况下 操作是 针对原始数组单元 存储的数据数值的操作
也就是 针对 参数1 的操作
return 操作 ;
具体要执行什么操作 根据实际项目需求设定
设定的操作 执行结果是什么
return 的就是什么
新数组中存储的就是什么
数组的过滤:
var 变量 = 数组.filter(function(参数1 , 参数2 , 参数3){
return 条件 ;
})
自动循环遍历原始数组的每一个单元
参数1 存储原始数组数据单元的 数据数值
参数2 存储原始数组数据单元的 索引下标
参数3 存储原始数组本身
将 符合条件的数据单元 存储的数据数值 作为 返回值 生成 一个新的数组
! 返回的是所有符合条件的数据
数组的复杂数据查询:
var 变量 = 数组.find(function(参数1 , 参数2 , 参数3){
return 条件 ;
})
自动循环遍历原始数组的每一个单元
参数1 存储原始数组数据单元的 数据数值
参数2 存储原始数组数据单元的 索引下标
参数3 存储原始数组本身
将 符合条件的数据单元 存储的数据数值 作为 返回值
! 只是 返回的是 第一个符合条件的数据单元 存储的数据数值
var 变量 = 数组.findIndex(function(参数1 , 参数2 , 参数3){
return 条件 ;
})
自动循环遍历原始数组的每一个单元
参数1 存储原始数组数据单元的 数据数值
参数2 存储原始数组数据单元的 索引下标
参数3 存储原始数组本身
将 符合条件的数据单元 索引下标 作为 返回值
! 只是 返回的是 第一个符合条件的数据单元 索引下标
数组的判断 :
var 变量 = 数组.some(function(参数1 , 参数2 , 参数3){
return 条件 ;
})
自动循环遍历原始数组的每一个单元
参数1 存储原始数组数据单元的 数据数值
参数2 存储原始数组数据单元的 索引下标
参数3 存储原始数组本身
如果 数组中 有一个 符合条件的数据 返回值 是 true
如果 数组中 所有数据都不符合条件 返回值 是 false
var 变量 = 数组.every(function(参数1 , 参数2 , 参数3){
return 条件 ;
})
自动循环遍历原始数组的每一个单元
参数1 存储原始数组数据单元的 数据数值
参数2 存储原始数组数据单元的 索引下标
参数3 存储原始数组本身
如果 数组中 所有数据都符合条件 返回值 是 true
如果 数组中 有一个 不符合条件的数据 返回值 是 false
数组的叠加:
var 变量 = 数组.reduce( function( 参数1 , 参数2 , 参数3 ,参数4 ){
return 参数1 的 叠加 ;
})
自动循环遍历原始数组的每一个单元
参数2 存储原始数组数据单元的 数据数值
参数3 存储原始数组数据单元的 索引下标
参数4 存储原始数组本身
参数1 存储叠加结果
当前是 没有 设定 初始值
默认 获取 数组中第一个单元存储的数据 作为 参数1 的初始值
然后从 数组的第二个单元开始循环
循环次数是 数组.length-1
var 变量 = 数组.reduce( function( 参数1 , 参数2 , 参数3 ,参数4 ){
return 参数1 的 叠加 ;
} , 初始值 )
自动循环遍历原始数组的每一个单元
参数2 存储原始数组数据单元的 数据数值
参数3 存储原始数组数据单元的 索引下标
参数4 存储原始数组本身
参数1 存储叠加结果
当前是 设定 初始值
将 初始值 赋值给 参数1 作为 叠加的初始值
从 数组的第一个单元开始循环
循环次数是 数组.length次简单了解:
数组的反转:
数组.reverse()
数组的截取:
数组.slice( 参数1 , 参数2 );
参数1 截取数组起始位置的索索引下标
0 正数
起始位置的索引下标
负数
结果是 [] 空数组
参数2 截取数组结束位置的索索引下标
0 正数
结束位置的索引下标
负数
从起始位置 截取至 数组倒数第几个单元
也就是 从 起始位置开始 截取至 字符串末位 少截取的单元个数
不设定第二个参数
截取效果是 从起始位置 截取至 字符串末位
! 不会改变原始数组内容
! 参数2 设定为 0 正数 是 截取结束位置的索引下标
但是结果不包括 结束位置的数据
数组的拼接:
var 变量 = 数组.concat( 数组1 , 数组2 .... );
将 设定的 一个或者多个数组单元的数据 拼接到 一个数组中
不会改变原始数组内容 执行结果返回值 是 拼接内容后的新数组数组的循环语法:
for
执行效率是最高
本质上没有针对数组进行循环
只是 通过 for循环生成 数组中的索引下标
通过 索引下标 操作对应的数组单元
可以通过 控制循环变量 i 的数值 控制 循环的进程
也就是 控制 操作的 数组单元
可以通过 break 终止循环
for...in
一般是针对 对象 的循环
如果 数组中 有 字符串索引下标的数组单元
for...in 可以 循环获取数据
针对数组本身的循环遍历语法
JavaScript自动从数组的第一个单元 循环至 最后一个单元
每次获取 当前循环单元 的 索引下标 以字符串形式 存储到变量中
循环进程 一定是 按照顺序一个一个的循环数组的每一个单元
不能 控制 循环的进程
可以通过 break 终止循环
for...of
一般用于 数组的迭代操作
针对数组本身的循环遍历语法
JavaScript自动从数组的第一个单元 循环至 最后一个单元
每次获取 当前循环单元 的 数据数值 存储到变量中
循环进程 一定是 按照顺序一个一个的循环数组的每一个单元
不能 控制 循环的进程
可以通过 break 终止循环
forEach
执行效率最低的循环语法
同时指针操作 循环遍历数组的每一个单元
从数组的起始位置开始 判断 指针指向的是不是在数组范围内 有没有对应的数组单元
如果有 获取 数组单元的 数据数值 索引下标 原始数组
执行 设定的循环程序
将 指针向右 移动到下一个位置
再 继续判断 判断 指针指向的是不是在数组范围内 有没有对应的数组单元
执行对应程序
直到 指针移动到 数组范围之外 终止循环
循环进程 一定是 按照顺序一个一个的循环数组的每一个单元
不能 控制 循环的进程
不能 通过 break 终止循环数组坍塌:
概念:
执行数组单元删除操作时
删除单元之后的单元会向前移动
出现在删除单元位置上
造成数组单元长度减少的情况
称为数组的坍塌
影响:
在 循环遍历数组中
如果触发了数组删除 会 立即引起数组的坍塌
如果直接继续执行循环操作
坍塌过来的数组单元 没有执行 对应的操作
也就是 坍塌过来的单元 没有执行 应该执行的比较判断等操作
消除:
在 触发数组单元删除操作时
同时触发执行 循环变量-- 操作
for循环通过 控制循环变量的数值 控制 循环进程
执行 循环变量-- 目的是为了 让 触发循环的单元 再次执行 循环操作根据数组数据动态渲染生成页面:
实际项目中 页面 都是根据数据库返回的数据动态生成的
后端返回的数据 一般数据格式 都是 数组中存储对象
数组中每一个对象单元 对应的是 一个具体的数据内容
根据 具体的数据内容 动态生成页面中 对应的 标签
! 页面中的标签 和 数组中的对象 是 一一对应的关系
基本步骤:
1, 获取一个数组
实际项目中 是 通过数据交互 后端程序响应的结果
目前只能 自己定义一个数组 模拟后端响应的数据
2, 定义一个变量 存储 存储的字符串内容
3, 循环遍历数组
每一个数组单元 对应生成 标签和标签内容
标签的内容 是 数组中对象存储的具体数据
4, 通过 DOM 操作将 字符串内容 写入 对应的标签JS 数组方法
数组常见方法 介绍
知识点: 创建数组和数组方法, 因为我关注数组的方法较多, 所以先讲解数组的方法 再去讲解创建数组的方式.
一、数组方法
数组原型方法主要有以下这些
- join():用指定的分隔符将数组每一项拼接为字符串
- push() :向数组的末尾添加新元素
- pop():删除数组的最后一项
- shift():删除数组的第一项
- unshift():向数组首位添加新元素
- slice():按照条件查找出其中的部分元素
- splice():对数组进行增删改
- fill(): 方法能使用特定值填充数组中的一个或多个元素
- filter():“过滤”功能
- concat():用于连接两个或多个数组
- indexOf():检测当前值在数组中第一次出现的位置索引
- lastIndexOf():检测当前值在数组中最后一次出现的位置索引
- every():判断数组中每一项都是否满足条件
- some():判断数组中是否存在满足条件的项
- includes():判断一个数组是否包含一个指定的值
- sort():对数组的元素进行排序
- reverse():对数组进行倒序
- forEach():ES5 及以下循环遍历数组每一项
- map():ES6 循环遍历数组每一项
- copyWithin():用于从数组的指定位置拷贝元素到数组的另一个指定位置中
- find():返回匹配的值
- findIndex():返回匹配位置的索引
- toLocaleString()、toString():将数组转换为字符串
- flat()、flatMap():扁平化数组
- entries() 、keys() 、values():遍历数组
各个方法的基本功能详解
1. join()
join() 方法用于把数组中的所有元素转换一个字符串。
元素是通过指定的分隔符进行分隔的。默认使用逗号作为分隔符。
var arr = [1,2,3];
console.log(arr.join()); // 1,2,3
console.log(arr.join("-")); // 1-2-3
console.log(arr); // [1, 2, 3](原数组不变)通过 join()方法可以实现重复字符串 ,只需传入字符串以及重复的次数,就能返回重复后的字符串,函数如下:
function repeatString(str, n) {
//一个长度为n+1的空数组用string去拼接成字符串,就成了n个string的重复
return new Array(n + 1).join(str);
}
console.log(repeatString("abc", 3)); // abcabcabc
console.log(repeatString("Hi", 5)); // HiHiHiHiHi2. push() 和 pop()
push() 方法从数组末尾向数组添加元素,可以添加一个或多个元素。
pop() 方法用于删除数组的最后一个元素并返回删除的元素。
var arr = ["Lily","lucy","Tom"];
var count = arr.push("Jack","Sean");
console.log(count); // 5
console.log(arr); // ["Lily", "lucy", "Tom", "Jack", "Sean"]
var item = arr.pop();
console.log(item); // Sean
console.log(arr); // ["Lily", "lucy", "Tom", "Jack"]3. shift() 和 unshift()
shift() 方法用于把数组的第一个元素从其中删除,并返回第一个元素的值。
unshift() 方法可向数组的开头添加一个或更多元素,并返回新的长度。
var arr = ["Lily","lucy","Tom"];
var count = arr.unshift("Jack","Sean");
console.log(count); // 5
console.log(arr); //["Jack", "Sean", "Lily", "lucy", "Tom"]
var item = arr.shift();
console.log(item); // Jack
console.log(arr); // ["Sean", "Lily", "lucy", "Tom"]4. sort
sort() 方法用于对数组的元素进行排序。
排序顺序可以是字母或数字,并按升序或降序。
默认排序顺序为按字母升序。
var arr1 = ["a", "d", "c", "b"];
console.log(arr1.sort()); // ["a", "b", "c", "d"]
arr2 = [13, 24, 51, 3];
console.log(arr2.sort()); // [13, 24, 3, 51]
console.log(arr2); // [13, 24, 3, 51](元数组被改变)为了解决上述问题,sort()方法可以接收一个比较函数作为参数,以便我们指定哪个值位于哪个值的前面。
比较函数接收两个参数,如果第一个参数应该位于第二个之前则返回一个负数,如果两个参数相等则返回 0,如果第一个参数应该位于第二个之后则返回一个正数。以下就是一个简单的比较函数:
function compare(value1, value2) {
if (value1 < value2) {
return -1;
} else if (value1 > value2) {
return 1;
} else {
return 0;
}
}
arr2 = [13, 24, 51, 3];
console.log(arr2.sort(compare)); // [3, 13, 24, 51]如果需要通过比较函数产生降序排序的结果,只要交换比较函数返回的值即可:
function compare(value1, value2) {
if (value1 < value2) {
return 1;
} else if (value1 > value2) {
return -1;
} else {
return 0;
}
}
arr2 = [13, 24, 51, 3];
console.log(arr2.sort(compare)); // [3, 13, 24, 51]5. reverse()
reverse() 方法用于颠倒数组中元素的顺序。
var arr = [13, 24, 51, 3];
console.log(arr.reverse()); //[3, 51, 24, 13]
console.log(arr); //[3, 51, 24, 13](原数组改变)6. concat()
concat() 方法用于连接两个或多个数组。
该方法不会改变现有的数组,而仅仅会返回被连接数组的一个副本。
var arr = [1,3,5,7];
var arrCopy = arr.concat(9,[11,13]);
console.log(arrCopy); //[1, 3, 5, 7, 9, 11, 13]
console.log(arr); // [1, 3, 5, 7](原数组未被修改)从上面测试结果可以发现:传入的不是数组,则直接把参数添加到数组后面,如果传入的是数组,则将数组中的各个项添加到数组中。但是如果传入的是一个二维数组呢?
var arrCopy2 = arr.concat([9,[11,13]]);
console.log(arrCopy2); //[1, 3, 5, 7, 9, Array[2]]
console.log(arrCopy2[5]); //[11, 13]7. slice()
slice():返回从原数组中指定开始下标到结束下标之间的项组成的新数组。
slice()方法可以接受一或两个参数,即要返回项的起始和结束位置。
在只有一个参数的情况下, slice()方法返回从该参数指定位置开始到当前数组末尾的所有项。
如果有两个参数,该方法返回起始和结束位置之间的项,但不包括结束位置的项。
当出现负数时,将负数加上数组长度的值 来替换该位置的数
var arr = [1,3,5,7,9,11];
var arrCopy = arr.slice(1);
var arrCopy2 = arr.slice(1,4);
var arrCopy3 = arr.slice(1,-2);//相当于arr.slice(1,4)
var arrCopy4 = arr.slice(-4,-1);//相当于arr.slice(2,5)
console.log(arr); //[1, 3, 5, 7, 9, 11](原数组没变)
console.log(arrCopy); //[3, 5, 7, 9, 11]
console.log(arrCopy2); //[3, 5, 7]
console.log(arrCopy3); //[3, 5, 7]
console.log(arrCopy4); //[5, 7, 9]8. splice()
splice():很强大的数组方法,它有很多种用法,可以实现删除、插入和替换。
8-1. 删除元素,并返回删除的元素
可以删除任意数量的项,只需指定 2 个参数:要删除的第一项的位置和要删除的项数。例如, splice(0,2)会删除数组中的前两项。
var arr = [1,3,5,7,9,11];
var arrRemoved = arr.splice(0,2);
console.log(arr); //[5, 7, 9, 11]
console.log(arrRemoved); //[1, 3]8-2. 向指定索引处添加元素
可以向指定位置插入任意数量的项,只需提供 3 个参数:起始位置、 0(要删除的项数)和要插入的项。例如,splice(2,0,4,6)会从当前数组的位置 2 开始插入 4 和 6。
var array1 = [22, 3, 31, 12];
array1.splice(1, 0, 12, 35); //[]
console.log(array1); // [22, 12, 35, 3, 31, 12]8-3. 替换指定索引位置的元素
可以向指定位置插入任意数量的项,且同时删除任意数量的项,只需指定 3 个参数:起始位置、要删除的项数和要插入的任意数量的项。插入的项数不必与删除的项数相等。例如,splice (2,1,4,6)会删除当前数组位置 2 的项,然后再从位置 2 开始插入 4 和 6。
const array1 = [22, 3, 31, 12];
array1.splice(1, 1, 8); //[3]
console.log(array1); // [22, 8, 31, 12]9. indexOf()和 lastIndexOf()
接收两个参数:要查找的项和(可选的)表示查找起点位置的索引。
indexOf():从数组的开头(位置 0)开始向后查找。
lastIndexOf():从数组的末尾开始向前查找。
这两个方法都返回要查找的项在数组中的位置,或者在没找到的情况下返回-1。在比较第一个参数与数组中的每一项时,会使用全等操作符。
var arr = [1,3,5,7,7,5,3,1];
console.log(arr.indexOf(5)); //2
console.log(arr.lastIndexOf(5)); //5
console.log(arr.indexOf(5,2)); //2
console.log(arr.lastIndexOf(5,4)); //2
console.log(arr.indexOf("5")); //-110. forEach()
forEach():对数组进行遍历循环,对数组中的每一项运行给定函数。这个方法没有返回值。参数都是 function 类型,默认有传,。
参数分别为:遍历的数组内容;对应的数组索引,数组本身
var arr = [11, 22, 33, 44, 55];
arr.forEach(function(x, index, a){
console.log(x + '|' + index + '|' + (a === arr));
});
输出为:
11|0|true
22|1|true
33|2|true
44|3|true
55|4|true11. map()
map() 方法返回一个新数组,数组中的元素为原始数组元素调用函数处理后的值。
map() 方法按照原始数组元素顺序依次处理元素。
该方法不会改变原数组
var arr = [1, 2, 3, 4, 5];
var arr2 = arr.map(function(item){
return item*item;
});
console.log(arr2); //[1, 4, 9, 16, 25]12. filter()
filter():“过滤”功能,数组中的每一项运行给定函数,返回满足过滤条件组成的数组。
var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
var arr2 = arr.filter(function(x, index) {
return index % 3 === 0 || x >= 8;
});
console.log(arr2); //[1, 4, 7, 8, 9, 10]13. fill() es6 新增
fill()方法能使用特定值填充数组中的一个或多个元素。当只是用一个参数时,该方法会用该参数的值填充整个数组。
let arr = [1, 2, 3, 'cc', 5];
arr.fill(1);
console.log(arr);//[1,1,1,1,1];如果不想改变数组中的所有元素,而只是想改变其中一部分,那么可以使用可选的起始位置参数与结束位置参数(不包括结束位置的那个元素)
3 个参数: 填充数值,起始位置参数,结束位置参数(不包括结束位置的那个元素)
let arr = [1, 2, 3, 'arr', 5];
arr.fill(1, 2);
console.log(arr);//[1,2,1,1,1]
arr.fill(0, 1, 3);
console.log(arr);//[1,0,0,1,1];14. every()
every():判断数组中每一项都是否满足条件,只有所有项都满足条件,才会返回 true。
var arr = [1, 2, 3, 4, 5];
var arr2 = arr.every(function(x) {
return x < 10;
});
console.log(arr2); //true
var arr3 = arr.every(function(x) {
return x < 3;
});
console.log(arr3); // false15. some()
some():判断数组中是否存在满足条件的项,只要有一项满足条件,就会返回 true。
var arr = [1, 2, 3, 4, 5];
var arr2 = arr.some(function(x) {
return x < 3;
});
console.log(arr2); //true
var arr3 = arr.some(function(x) {
return x < 1;
});
console.log(arr3); // false16. includes() es7 新增
includes() 方法用来判断一个数组是否包含一个指定的值,如果是返回 true,否则 false。
参数有两个,其中第一个是(必填)需要查找的元素值,第二个是(可选)开始查找元素的位置
const array1 = [22, 3, 31, 12, 'arr'];
const includes = array1.includes(31);
console.log(includes); // true
const includes1 = array1.includes(31, 3); // 从索引3开始查找31是否存在
console.log(includes1); // false需要注意的是:includes使用===运算符来进行值比较,仅有一个例外:NaN 被认为与自身相等。
let values = [1, NaN, 2];
console.log(values.indexOf(NaN));//-1
console.log(values.includes(NaN));//true17. reduce()和 reduceRight()
这两个方法都会实现迭代数组的所有项(即累加器),然后构建一个最终返回的值。
reduce() 方法从数组的第一项开始,逐个遍历到最后。
reduceRight() 则从数组的最后一项开始,向前遍历到第一项。
4 个参数: 前一个值、当前值、项的索引和数组对象
var values = [1,2,3,4,5];
var sum = values.reduceRight(function(prev, cur, index, array){
return prev + cur;
},10); //数组一开始加了一个初始值10,可以不设默认0
console.log(sum); //2518. toLocaleString() 和 toString()
将数组转换为字符串
const array1 = [22, 3, 31, 12];
const str = array1.toLocaleString();
const str1 = array1.toString();
console.log(str); // 22,3,31,12
console.log(str1); // 22,3,31,1219. find()和 findIndex()
find()与 findIndex()方法均接受两个参数:一个回调函数,一个可选值用于指定回调函数内部的 this。
该回调函数可接受三个参数:数组的某个元素,该元素对应的索引位置,以及该数组本身。
该回调函数应当在给定的元素满足你定义的条件时返回 true,而 find()和 findIndex()方法均会在回调函数第一次返回 true 时停止查找。
二者的区别是:find()方法返回匹配的值,而 findIndex()返回匹配位置的索引。
let arr = [1, 2, 3, 'arr', 5, 1, 9];
console.log(arr.find((value, keys, arr) => {
return value > 2;
})); // 3 返回匹配的值
console.log(arr.findIndex((value, keys, arr) => {
return value > 2;
})); // 2 返回匹配位置的索引20. copyWithin() [es6 新增]
copyWithin() 方法用于从数组的指定位置拷贝元素到数组的另一个指定位置中。
该方法会改变现有数组
//将数组的前两个元素复制到数组的最后两个位置
let arr = [1, 2, 3, 'arr', 5];
arr.copyWithin(3, 0);
console.log(arr);//[1,2,3,1,2]默认情况下,copyWithin()方法总是会一直复制到数组末尾,不过你还可以提供一个可选参数来限制到底有多少元素会被覆盖。这第三个参数指定了复制停止的位置(不包含该位置本身)。
let arr = [1, 2, 3, 'arr', 5, 9, 17];
//从索引3的位置开始粘贴
//从索引0的位置开始复制
//遇到索引3时停止复制
arr.copyWithin(3, 0, 3);
console.log(arr);//[1,2,3,1,2,3,17]21. flat() 和 flatMap() es6 新增
flat() 方法会按照一个可指定的深度递归遍历数组,并将所有元素与遍历到的子数组中的元素合并为一个新数组返回。
该方法返回一个新数组,对原数据没有影响。
参数: 指定要提取嵌套数组的结构深度,默认值为 1。
const arr1 = [0, 1, 2, [3, 4]];
console.log(arr1.flat());
// expected output: [0, 1, 2, 3, 4]
const arr2 = [0, 1, 2, [[[3, 4]]]];
console.log(arr2.flat(2));
// expected output: [0, 1, 2, [3, 4]]
//使用 Infinity,可展开任意深度的嵌套数组
var arr4 = [1, 2, [3, 4, [5, 6, [7, 8, [9, 10]]]]];
arr4.flat(Infinity);
// [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
// 扁平化数组空项,如果原数组有空位,flat()方法会跳过空位
var arr4 = [1, 2, , 4, 5];
arr4.flat();
// [1, 2, 4, 5]flatMap() 方法对原数组的每个成员执行一个函数,相当于执行Array.prototype.map(),然后 对返回值组成的数组执行flat()方法。
该方法返回一个新数组,不改变原数组。
// 相当于 [[2, 4], [3, 6], [4, 8]].flat()
[2, 3, 4].flatMap((x) => [x, x * 2])
// [2, 4, 3, 6, 4, 8]22. entries(),keys() 和 values() 【ES6】
entries(),keys()和values() —— 用于遍历数组。它们都返回一个遍历器对象,可以用for...of循环进行遍历
区别是keys()是对键名的遍历、values()是对键值的遍历,entries()是对键值对的遍历
for (let index of ['a', 'b'].keys()) {
console.log(index);
}
// 0
// 1
for (let elem of ['a', 'b'].values()) {
console.log(elem);
}
// 'a'
// 'b'
for (let [index, elem] of ['a', 'b'].entries()) {
console.log(index, elem);
}
// 0 "a"
// 1 "b"如果不使用for...of循环,可以手动调用遍历器对象的next方法,进行遍历。
let letter = ['a', 'b', 'c'];
let entries = letter.entries();
console.log(entries.next().value); // [0, 'a']
console.log(entries.next().value); // [1, 'b']
console.log(entries.next().value); // [2, 'c']二、创建数组
1.使用数组字面量表示法
// 创建一个空数组
var arr4 = [];
// 创建一个包含1项数据为20的数组
var arr5 = [20];
// 创建一个包含3个字符串的数组
var arr6 = ["lily","lucy","Tom"];2.使用 Array 构造函数
2.1 无参构造
//创建一个空数组
var arr1 = new Array();2.2 带参构造
如果只传一个数值参数,则表示创建一个初始长度为指定数值的空数组
// 创建一个包含20项的数组
var arr2 = new Array(20);如果传入一个非数值的参数或者参数个数大于 1,则表示创建一个包含指定元素的数组
// 创建一个包含3个字符串的数组
var arr3 = new Array("lily","lucy","Tom");
// ["23"]
var array4 = new Array('23');3.Array.of 方法创建数组(es6 新增)
ES6 为数组新增创建方法的目的之一,是帮助开发者在使用 Array 构造器时避开 js 语言的一个怪异点。
Array.of()方法总会创建一个包含所有传入参数的数组,而不管参数的数量与类型。
let arr = Array.of(1, 2);
console.log(arr.length);// 2
let arr1 = Array.of(3);
console.log(arr1.length);// 1
console.log(arr1[0]);// 3
let arr2 = Array.of('2');
console.log(arr2.length);// 1
console.log(arr2[0]);// '2'4.Array.from 方法创建数组(es6 新增)
在 js 中将非数组对象转换为真正的数组是非常麻烦的。在 ES6 中,将可迭代对象或者类数组对象作为第一个参数传入,Array.from()就能返回一个数组。
//...args剩余参数数组,由传递给函数的实际参数提供
function arga(...args) {
let arg = Array.from(args);
console.log(arg);
}
arga('arr1', 26, 'from');
// 执行的结果为: ['arr1',26,'from']4.1 映射转换
如果你想实行进一步的数组转换,你可以向 Array.from()方法传递一个映射用的函数作为第二个参数。此函数会将数组对象的每一个值转换为目标形式,并将其存储在目标数组的对应位置上。
function arga(...args) {
return Array.from(args, value => value + 1);
}
let arr = arga('arr', 26, 'pop');
console.log(arr);// ['arr1',27,'pop1']如果映射函数需要在对象上工作,你可以手动传递第三个参数给 Array.from()方法,从而指定映射函数内部的 this 值
const helper = {
diff: 1,
add(value) {
return value + this.diff;
}
}
function translate() {
//arguments 是一个对应于传递给函数的参数的类数组对象
return Array.from(arguments, helper.add, helper);
}
let arr = translate('liu', 26, 'man');
console.log(arr); // ["liu1", 27, "man1"]JS 对象
JS对象 介绍
概念:
存储数据的集合
下标可以是 字符串类型
以 键值对的形式 存储数据
属性:属性值 键名:键值 键:值 key:value语法:
字面量
var 变量 = { 属性:属性值 , 键名:键值 , 键:值... };
构造函数语法
var 变量 = new Object( { 属性:属性值 , 键名:键值 , 键:值... } );操作:
取值语法:
点语法
对象.属性
[]语法
对象['属性']
点语法不支持 数值类型的属性
点语法不解析 变量作为属性获取属性值
[]语法支持 数值类型的属性
[]语法可以解析 变量作为属性获取属性值
修改操作:
对已经存在的属性进行重复数值
后赋值的数据 会覆盖之前存储的数据
! 对象中 一定不会存储重复的属性
如果设定的属性 是 已经存在的属性
执行的是 对象数据覆盖的效果
不是 新增对象单元的效果
新增:
对 不存在的属性进行赋值操作
执行效果是 给 对象 新增数据单元 设定 属性 和 属性值
删除:
delete( 对象.属性 )
delete( 对象['属性'] )
delete 对象.属性
delete 对象['属性']JS 字符串
JS字符串 介绍
1.包装数据类型:
字符串 是 包装数据类型
支持 []语法 通过索引下标 获取一个字符串
支持 length 属性 获取字符串长度 也就是 字符个数2.字符串循环:
for循环
生成 从 第一个字符串 到 最后一个字符的索引下标
0 至 字符串.length-1
for...in循环
变量中存储每一个字符的索引下标
for...of循环
变量中存储么一个字符3.字符串操作函数:
(1) 字符串查询类:
字符串.indexOf( 字符 );
如果有匹配的字符 返回值 是 这个字符第一次出现位置的索引下标
如果没有匹配字符 返回值 是 -1
字符串.search( 字符 / 正则 );
如果有匹配的字符 返回值 是 这个字符第一次出现位置的索引下标
如果没有匹配字符 返回值 是 -1
查询内容 支持 正则表达式
字符串.lastIndexOf( 字符 );
如果有匹配的字符 返回值 是 这个字符最后一次出现位置的索引下标
如果没有匹配字符 返回值 是 -1
字符串.includes( 字符 );
如果有匹配的字符 返回值 是 true
如果没有匹配字符 返回值 是 false
字符串.startsWith( 字符 );
如果 是以查询字符起始 返回值 是 true
如果 不是以查询字符起始 返回值 是 false
字符串.endsWith( 字符 );
如果 是以查询字符结束 返回值 是 true
如果 不是以查询字符结束 返回值 是 false
(2) 字符串截取:
字符串.substr( 参数1 , 参数2 )
参数1 截取起始的位置
0 正数
起始位置的索引下标
负数
从倒数第几个字开始截取
参数2 截取字符的个数
如果不设定
截取至字符末位
0 正数
截取字符的个数
负数
空字符串
字符串.substring( 参数1 , 参数2 )
参数1 截取起始的位置
0 正数
起始位置的索引下标
负数
从起始位置开始截取
参数2 截取结束的位置
如果不设定
截取至字符串末位
0 正数
结束位置的索引下标
负数
从起始位置 向 字符串起始方向截取
也就是 从 起始位置向 字符串 左侧截取
! 截取结果 包括起始字符 不包括 结束字符
字符串.slice( 参数1 , 参数2 )
参数1 截取起始的位置
0 正数
起始位置的索引下标
负数
从倒数第几个字符开始截取
参数2 截取结束的位置
如果不设定
截取至字符串末位
0 正数
结束位置的索引下标
负数
从起始位置开始截取至字符串末位
负数是少截取的字符个数
! 截取结果 包括起始字符 不包括 结束字符
(3) 字符串替换:
字符串.replace( 参数1 , 参数2 )
参数1 要替换的原始字符
参数2 写入的新字符
默认只会替换第一个符合的内容
实际项目中 通过 正则表达式完成
(4) 字符串大小写:
字符串.toLowerCase()
所有字符小写
字符串.toUpperCase()
所有字符串大写
(5) 字符串 -- 数组:
字符串.split()
(6) 字符串获取字符:
字符串.charAt(索引下标)
根据指定索引下标获取字符
字符串.charCodeAt(索引下标)
根据指定索引下标获取字符的ASCII码
(7) 字符串去除空格:
字符串.trim()
去除字符串两端的空格
字符串.trimStart() / 字符串.trimLeft()
去除字符串左侧的空格
字符串.trimEnd() / 字符串.trimRight()
去除字符串右侧的空格
(8) 字符串拼接:
字符串.concat( 字符1 , 字符2 ... )
将 设定的字符内容 拼接到一个字符串中JS Math内置对象
Math内置对象 介绍
Math内置对象:
是 JavaScript 定义好的对象
其中定好了属性和函数方法 我们直接调用属性属性值:
Math.PI
圆周率
Math.SQRT2
根号2函数方法:
Math.random()
0 - 1 的 随机小数
可以是 0 永远不会是 1
如果需要 生成 数值1 至 数值2 的 随机整数 包括 数值1 数值2
parseInt( Math.random() * ( 数值2 + 1 - 数值1 ) + 数值1 )
如果需要 获取 数组 / 字符串 中 任意一个字符
生成 所有的索引下标 中 的 随机数值
所有索引下标的范围是 0 至 数组.length-1 / 字符串.length-1
套用公式的结果是 parseInt( Math.random() * 数组.length / 字符串.length )
Math.min()
最小值
Math.max()
最大值
Math.round()
四舍五入取整
Math.floor()
向下取整 ---- 变小
Math.ceil()
向上取整 ---- 变大
变量.toFixed( 位数 )
按照指定的小数位数 保留小数
四舍五入保留小数位数
parseInt( 变量 / 100 ) / 100
保留指定的2位小数
Math.pow( 底数 , 指数 )
幂运算 / 乘方运算
Math.abs()
绝对值
Math.sqrt()
求平方根JS 时间对象
时间对象 介绍
1.创建时间对象:
var 变量 = new Date();
没有设定参数 获取的是 当前时间的时间对象
var 变量 = new Date('年 月 日 时:分:秒');
var 变量 = new Date('年-月-日 时:分:秒');
var 变量 = new Date('年,月,日 时:分:秒');
var 变量 = new Date('年/月/日 时:分:秒');
设定 一个字符串
字符串中设定 指定的时间
设定的时间不能超出正常值范围
var 变量 = new Date(年,月,日,时,分,秒);
设定 多个数值 作为时间数据
设定的数值 可以超出 正常值范围 会 自动进位
! 设定的月份数值 是 0-11 对应 1-12 月2.获取时间数据:
时间对象.get....()
获取 当前时区 时间数据
时间对象.getUTC....()
获取 世界标准时 时间数据
时间对象.getFullYear()
获取 4位 年份
时间对象.getMonth()
获取 月份
获取 结果是 0-11 的数字 对应 1-12 月
如果 需要的结果是 数字结果
获取结果 +1 是 正确的月份数字
如果 需要的结果是 文字结果
设定数组 数组的内容是 1月 - 12月 的 文字内容
使用 获取结果 作为 数组的索引下标
从 数组中 获取 对应的 文字内容
时间对象.getDate()
获取 日期
时间对象.getDay()
获取 星期
获取结果是 0 - 6 的数字 对应 星期日 - 星期六
如果 需要的结果是 文字结果
设定数组 数组的内容是 星期日 - 星期六 的 文字内容
使用 获取结果 作为 数组的索引下标
从 数组中 获取 对应的 文字内容
时间对象.getHours()
获取 小时
时间对象.getMinutes() ;
获取 分钟
时间对象.getSeconds() ;
获取 秒3.设定时间数据:
时间对象.set...()
设定的时间是 当前时区 时间
时间对象.setUTC...()
设定的时间是 世界标准时 时间
时间对象.setMonth()
设定月份
设定的数值是 0-11 对应的是 1-12 月
星期不能设定4.时间戳:
当前时间 距离 格林尼治标准时间1970年1月1日 0点0分0秒 的 时间差
在 JavaScript中 时间戳的单位是 毫秒
1 秒 = 1000 毫秒
获取 时间戳
时间对象.getTime()
设定 时间戳
时间对象.setTime()
时间戳 一般用于计算时间差JS 定时器 延时器
定时器 延时器 介绍
定时器:
根据设定的时间间隔 循环往复执行函数程序
如果不清除定时器 定时器会一直触发执行
! 定时器的一次调用执行 是在 设定的时间间隔之后
也就是 第一次的时间间隔内 定时器 是没有触发执行的程序
var 变量 = setInterval( 参数1 , 参数2 );
参数1 定时器触发的回调函数
回调函数可以是 匿名函数 / 函数名称
本质是 函数的内存地址
通过赋值的函数的内存地址 在 定时器中 触发调用函数程序
参数2 时间间隔
单位 毫秒延时器:
根据设定的时间间隔 延迟执行函数程序
设定的函数程序只会触发执行一次
var 变量 = setTimeout( 参数1 , 参数2 );
参数1 定时器触发的回调函数
回调函数可以是 匿名函数 / 函数名称
本质是 函数的内存地址
通过赋值的函数的内存地址 在 定时器中 触发调用函数程序
参数2 时间间隔
单位 毫秒清除 定时器/延时器:
var 变量 = setInterval();
var 变量 = setTimeout();
变量中 存储的是当前定时器延时器的序号编号
clearInterval( 参数 )
clearTimeout( 参数 )
设定的参数的是 定时器延时器 序号编号
定时器 延时器 函数程序 执行结果返回值 就是 这个定时器延时器的序号编号
可以使用变量储存
可以 清除 定时器也可以清除延时器异步程序的基本执行原理:
1.单线程 / 多线程:
单线程
同一个时间内 只能触发执行一个程序
之后的程序 要等之前的程序执行结束 再触发执行
多线程
同一个事件内 可以触发执行多个程序
每个程序按照设定的线程 执行
2.同步程序 / 异步程序:
同步程序 和 异步程序 是 单线程中 程序执行的顺序原理
先触发执行所有的同步程序
执行方式是 按照 单线程的原理 一个一个的执行
如果是异步程序 先存储到异步池中
所有同步成执行结束 同时触发执行 异步池中的所有异步程序
时间间隔短的先触发执行
时间间隔长的后触发执行
如果时间间隔相同 按照顺序执行
异步程序包括:
定时器 延时器
事件绑定
ajax
nodejsJS DOM 操作
DOM 操作 介绍
1.获取标签对象:
好用的方法:
var 变量 = document.querySelector('条件');
获取第一个符合条件的标签对象
获取结果是一个独立的标签对象
可以直接执行DOM操作
如果没有匹配的标签对象获取结果是 null
var 变量 = document.querySelectorAll('条件');
获取所有符合条件的标签对象
获取结果是一个伪数组
不能直接执行DOM操作
通过 []语法 获取 一个独立的标签对象进行DOM操作
通过 循环语法 对 伪数组中每一个标签对象进行操作
如果没有匹配的标签对象 获取结果是 一个 空数组
条件可以是所有html和css支持的语法形式
标签名称
属性属性值
伪类选择器
.....
不好用的方法:
var 变量 = document.getElementById('id属性值');
获取结果是一个独立的标签对象
var 变量 = document.getElementsByClassName('class属性值');
获取结果是一个伪数组
不能 forEach
var 变量 = document.getElementsByTagName('标签名称');
获取结果是一个伪数组
不能 forEach
var 变量 = document.getElementsByName('name属性值');
获取结果是一个伪数组
可以 forEach2.标签的精确获取方式:
var 变量 = document.querySelector('条件');
在 整个html文档中获取符合条件标签
var 变量 = 标签对象.querySelector('条件');
在 指定的标签对象中 获取 符合条件的标签3.特殊的标签的获取:
document
整个HTML文档
document.documentElement
整个html标签
document.body
整个body标签
document.title
title标签的内容
document.title = '内容' ;
设定 title标签内容4.内容操作:
标签对象.innerHTML
支持解析标签
标签对象.innerText
不能解析标签
写入的内容 必须是 字符串类型
如果不是字符串类型会自动转化为字符串类型5.数据的操作:
标签对象.value
input textarea select>option
这三个标签可以获取标签数据
通过js操作获取标签的数据 标签中不需要设定name属性
input>radio input>checkbox等
不能 通过输入 给标签设定数据
只能 通过给标签设定value属性 设定标签的数据
input>radio input>checkbox
标签 checked 属性 是 默认选中时 获取标签数据6.属性操作:
布尔属性:
标签对象.布尔属性
获取和设定的数据 都是 布尔类型
标签直接支持的属性:
标签对象.属性
每个标签直接支持的属性都不同
所有标签都直接支持的属性
标签对象.id
标签对象.className
标签对象.title
标签属性的通用操作语法:
标签对象.setAttribute( '属性' , 属性值 )
设定标签属性和属性值
标签对象.getAttribute( '属性' )
获取标签属性和属性值
标签对象.removeAttribute( '属性' )
删除标签属性和属性值
通用方法 不 操作 布尔类型7.class属性:
class属性的属性值 可以是 一个或者多个属性值
标签对象.classList.add( 属性值1 , 属性值2... );
新增
标签对象.classList.remove( 属性值1 , 属性值2... );
删除
标签对象.classList.toggle( 属性值1 , 属性值2... );
切换
有 --- 删除
没有 --- 新增
标签对象.classList.replace( 旧的 , 新的 );
替换8.标签的占位:
盒子模型的css属性 决定标签的文档流占位大小
标签对象.offsetWidth
标签对象.offsetHeight
内容+padding+border
标签对象.clientWidth
标签对象.clientHeight
内容+padding
标签对象.clientLeft
标签对象.clientTop
左 上 边框线宽度
获取的结果是 数值 没有px单位
如果标签有 display:none 获取的占位数值是 09.标签的间距:
和 JavaScript定位父级的间距
标签对象.offsetParent
当前标签 JavaScript定位父级
标签对象.offsetLeft
标签对象.offsetTop
和定位父级 左 上 的间距
null
当前标签有 固定定位
视窗窗口 左上角
body
当前标签有 定位属性
HTML的body标签 也就是 HTML页面左上角
标签
有父级标签 并且 父级标签有定位属性JS BOM 操作
BOM 操作 介绍
window是 BOM 操作的顶级对象
document 是 DOM 操作的顶级对象
JavaScript语法规范规定
window 可以 省略不写浏览器三大弹窗:
window.alert()
警告框
window.prompt()
输入框
输入的数据可以使用变量储存
输入的数据以字符串类型存储
window.confirm()
确认框
执行结果可以使用变量储存
点击 确定 执行结果是 true
点击 取消 执行结果是 false视窗窗口宽度高度:
包含 滚动条的宽度高度
window.innerWidth
window.innerHeight
不包含 滚动条的宽度高度
有 文档类型声明
document.documentElement.clientWidth
document.documentElement.clientHeight
没有 文档类型声明
document.body.clientWidth
document.body.clientHeight
浏览器视窗窗口监听事件
浏览器视窗窗口大小改变时触发
window.addEventListener( 'resize' , 回调函数 )页面滚动:
有文档类型声明
document.documentElement.scrollTop
document.documentElement.scrollLeft
没有 文档类型声明
document.body.scrollTop
document.body.scrollLeft
页面滚动监听事件
页面滚动时触发
window.addEventListener( 'scroll' , 回调函数 )
! 鼠标滚轮滚动一次 页面滚动监听事件 触发多次浏览器配置信息:
window.navigator.appName
Netscape 网景公司
window.navigator.appVersion
5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36
浏览器版本信息
window.navigator.userAgent
Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36"
浏览器版本信息浏览器历史记录:
window.history.length
当前窗口浏览的页面个数
window.history.go()
按照设定的方向和次数 跳转显示的页面
正数
向前跳转
负数
向后跳转
window.history.back()
返回上一个浏览的页面
window.history.go(-1) 的效果相同
window.history.forward()
返回下一个浏览的页面
window.history.go(1) 的效果相同浏览器地址栏数据信息:
window.location的配置信息:
host: "127.0.0.1"
服务器主机 域名 / IP地址
当前是本地服务器 IP地址是 127.0.0.1
hostname: "127.0.0.1"
服务器主机 名称
href: "http://127.0.0.1/demo.html"
当前文件 url地址
一个完整的url地址 包括 协议 路径 端口 三个部分
origin: "http://127.0.0.1"
当前服务器 根目录 路径地址
pathname: "/demo.html"
当前文件 在 根目录 中的 路径地址
port: ""
端口号
window.location的常用函数方法:
window.location.href = 'url地址' ;
设定 当前页面 跳转的url路径地址
可以 触发 后退操作
window.location.replace('url地址')
使用 设定的url地址 替换 当前的url地址
执行 效果 和 页面跳转效果相同
不能 触发 后退操作
window.open('url地址');
新窗口 打开 url地址
window.location.reload()
刷新当前页面
window.close()
关闭当前窗口前端传参方式:
所谓的传参 本质是 向指定的url地址发送请求同时携带需要的数据参数
方式1: 通过html标签完成
form:
action 发送请求的url地址
method 发送请求的方式
get 显性传参 / 明文传参
携带的参数会显示在浏览器地址栏中
post
隐性传参 / 密文传参
携带的参数不会显示在浏览器地址栏中
会 通过 请求体 携带参数
enctype 上传文件时必须设定的属性
其他传参标签的设定
input textarea select>option
name属性 存储数据的容器
如果通过form标签
直接向 后端程序文档 发送请求
也就是 action 中 发送请求的地址是一个 后端程序 例如 PHP java....
如果 多个标签 键名相同 键值不同 需要给标签name属性添加[]
王者荣耀<input type="checkbox" name="hobby[]" value="王者荣耀">
英雄联盟<input type="checkbox" name="hobby[]" value="英雄联盟">
绝地求生<input type="checkbox" name="hobby[]" value="绝地求生">
和平精英<input type="checkbox" name="hobby[]" value="和平精英">
input标签 name 属性之后 添加 []
后端程序 会 自动以 数组的形式 存储 相同键名 不同键值的数据
如果是向 html文件发送请求不需要
value属性 设定标签存储的数据
a:
通过固定的语法形式 以 get 方式传参
在 超链接标签 href属性中 以键值对形式携带参数
携带参数和url地址之间 使用 ? 问号间隔
键值对之间 使用 & 符号间隔
<a href="url地址?键名=键值&键名=键值..."></a>
方式2: window.location.href
以固定的语法形式 使用 get 方式 发送请求 同时携带参数
携带参数和url地址之间 使用 ? 问号间隔
键值对之间 使用 & 符号间隔
window.location.href = "url地址?键名=键值&键名=键值..." ;
方式3 ajaxJS 节点 操作
节点 操作 介绍
页面中的所有内容都可以视为节点
通过获取节点操作html页面中的内容
获取节点:
标签对象.childNodes
获取所有节点
标签对象.children
获取所有标签节点
标签对象.firstChild
获取第一个节点
标签对象.lastChild
获取最后一个节点
标签对象.firstElementChild
获取第一个标签节点
标签对象.lastElementChild
获取最后一个标签节点
标签对象.previousSibling
获取上一个节点
标签对象.nextSibling
获取下一个节点
标签对象.previousElementSibling
获取上一个标签节点
标签对象.nextElementSibling
获取下一个标签节点
标签对象.parentNode
获取父级节点
标签对象.attributes
获取属性节点创建节点:
var 变量 = document.createElement('标签名称');
创建的是 一个 标签对象
是 一个 空标签对象设定节点:
可以 通过 所有JavaScript支持的 DOM操作方式
设定节点的 内容 样式 属性....
可以给 创建的标签节点 直接 绑定事件写入节点:
创建的是 标签对象 不能 使用 DOM操作 写入创建的节点
也就是 不能使用 标签对象.innerHTML 语法写入
必须使用 节点操作方式 写入 创建的标签对象
标签对象.appendChild( 新节点 );
在 标签对象 末位 写入新节点
标签对象.insertBefore( 新节点 , 指定节点 );
在 标签对象中 指定节点 之前 写入 新节点克隆节点:
var 变量 = 标签对象.cloneNode();
只克隆标签本身 不会克隆标签内容
var 变量 = 标签对象.cloneNode(true);
标签本身 和 标签内容 都会 克隆
不会克隆 标签绑定的事件替换节点:
标签对象.replaceChild( 新节点 , 旧节点 );
将 标签对象中 指定节点替换为新节点
新节点 必须是 标签对象形式 不能是 字符串形式删除节点:
标签对象.remove()
删除这个节点本身
标签对象.removeChild( 节点 )
删除标签对象中指定的节点JS 常见事件类型
事件类型 常见
鼠标事件:
click 左键单击
dblclick 左键双击
contextmenu 右键单击
默认会弹出 右键菜单栏
可以通过 阻止默认事件 阻止弹出
mousedown 按键按下
mouseup 按键抬起
鼠标按键按下一次 抬起一次 完成 一个完成的鼠标点击操作
鼠标按键一直按下 只会触发一次 鼠标按下事件
mousemove 鼠标移动
在 绑定事件的标签对象 也就是 事件源 范围内 移动 触发事件
一直移动一直触发事件
mouseover 鼠标移入
mouseout 鼠标移出
事件源 和 事件源的后代标签 都会触发事件
经过 标签的边界线时 会 触发一次
mouseenter 鼠标移入
mouseleave 鼠标移出
只有 事件源标签 可以触发事件
经过 标签的边界线时 会 触发一次键盘事件:
keydown 键盘按键按下
所有的按键都会触发事件
键盘按键按下会一直触发
keyup 键盘按键抬起
keypress 键盘按键按下
有些特殊的按键不会触发
键盘事件不是所有的标签都支持
可以支持直接绑定事件的标签
input select>option textarea button a
一般是 直接给 整个文档绑定事件表单事件:
focus 获取焦点
不是所有标签都支持直接绑定 获取焦点事件
可以支持直接绑定事件的标签
input select>option textarea button a
blur 失去焦点
只要失去焦点就会触发
change 失去焦点并且输入数据改变
失去焦点 同时 数据和获取焦点时输入的数据不同
input 输入数据事件
在 标签中输入数据时触发
如果 按键没有插入数据 不会触发事件
submit 表单提交事件
给 form标签 绑定的事件
form标签 提交时 触发事件触摸事件:
touchstart 触摸开始
touchend 触摸结束
touchmove 触摸移动
.....
只有移动端设备支持事件特殊事件:
transitionstart 过渡开始
transitionend 过渡结束
animationstart 动画开始
animationend 动画结束
每一个属性的 过渡/动画 都会触发事件
实际项目中 使用 js完成动画/过渡效果事件对象:
在 事件处理函数中 设定的形参
这个形参 一帮定义的名称是
event 或者 e
当 触发事件时 JavaScript程序 会自动向形参存储实参
实参是 触发事件的相关数据
事件对象.target
触发事件的标签对象
触发事件的标签对象 和 事件源标签对象
有可能相同 有可能不同
事件对象.target.tagName
触发事件的标签对象的标签名称
是 大写的英文字符默认事件和阻止默认事件:
常见的默认事件
a标签
a标签 点击 默认事件是 跳转页面
给 a标签 绑定 click点击事件
触发点击事件时 阻止默认事件执行
form标签
from标签 点击 button提交 默认事件是 跳转页面
给 form标签 绑定 submit提交事件
触发 提交时 阻止默认事件
鼠标右键
鼠标点击右键 触发 弹出 右键菜单栏
给 整个文档 绑定 鼠标右键事件
触发 鼠标右键时 阻止默认事件
语法形式
事件对象.preventDefault();事件的传播和阻止事件的传播:
当前标签 和 父级标签绑定相同类型的事件
当前标签 触发 父级标签也会触发这个事件
传播方式:
冒泡传播:
当前所有浏览器默认的事件传播方式
先触发执行 当前标签的事件
再触发执行 父级标签的事件
传播顺序是 当前标签 ---> 父级标签
捕获传播:
现在没有浏览器执行了
先触发执行 父级标签的事件
再触发执行 当前标签的事件
传播顺序是 父级标签 ---> 当前标签
需要给 事件监听语法 第三个参数 设定为 true
阻止传播:
事件对象.stopPropagation();事件委托:
不是给标签直接绑定事件
通过 给 父级标签绑定事件
然后 使用 事件对象.target 判断 触发事件的标签对象
触发事件的标签对象不同 触发执行不同的程序代码
动态生成的标签 一般都需要使用 事件委托的语法形式 绑定事件
事件委托和不同的事件绑定对比:
1. 普通绑定事件 给 所有的标签都绑定事件
事件委托 给 一直存在的一个父级标签绑定事件
2. 普通绑定事件 需要通过 循环遍历操作 给 所有标签都绑定
事件委托 只需要给一个标签绑定事件 不需要循环遍历
3. 普通绑定事件 如果页面再次动态生成 需要再次绑定事件
事件委托 父级标签一直存在 页面再次动态生成 不影响绑定的事件
4. 普通绑定事件 通过循环遍历绑定事件 标签一般不需要定义特殊的属性
事件委托 需要在动态生成标签时 定义 属性 区分触发事件的标签 存储特殊的数据信息JavaScript事件绑定语法:
on语法
标签对象.on事件类型 = 回调函数 ;
事件监听语法
标签对象.addEventListener( '事件类型' , 回调函数 );
! 当前项目开发中推荐使用的语法形式
! on语法 本质是 赋值操作
后赋值绑定的 函数程序 会 覆盖之前绑定的函数程序
效果是 一个标签 相同的事件类型 只能 绑定触发 一个函数程序
! 事件监听语法 绑定执行函数的原理 和 on语法 完全不同
效果是 一个标签 相同的事件类型 可以 同时绑定触发 多个函数程序JavaScript事件删除语法:
on语法
一般是 赋值 null 或者 空函数
就会覆盖之前赋值绑定的函数
执行效果就是 没有执行的函数程序
达到删除事件的效果
事件监听语法
标签对象.removeEventListener( 事件类型 , 回调函数 );
! 事件监听语法 绑定的函数程序 如果需要删除
只能删除 绑定的是 函数名称的回调函数
如果绑定的是 匿名函数 不能 删除
原理
1. 回调函数 本质是 赋值的函数的内存地址
本质是 将 匿名函数的内存地址 或者 函数名称中 存储的内存地址
赋值给 事件监听语法
事件监听语法 触发事件时 通过 赋值的内存地址 找到对应的函数程序 调用执行
00ff11 00ffaa 00ff15
2. 删除绑定的函数程序 是 通过 删除程序中 赋值的 内存地址
找到 绑定的 函数程序的内存地址
删除 和 赋值要删除的内存地址相同的 绑定的内存地址
00ffaa
3. 同一个变量中存储的内存地址是相同的内存地址
也就是绑定时 和 删除时 执行的是相同的内存地址
两个匿名函数 即使程序代码完全相同
也是 两个 独立的 引用数据类型
会 使用 两个不同的内存地址 存储 两个匿名函数程序
绑定的 和 删除的 内存地址 是 不同的 内存地址JS ES6语法
ES6语法 常见
1.模板字符串:
支持换行 支持解析变量的字符串语法
` ${变量} `2.let/const关键词定义变量:
let:
用于 定义 基本数据类型
定义的变量 不会 预解析
定义的变量 变量名称不能重复
如果定义在 { } 中 作用域 就是 在 { } 中
在 { } 中 可以调用
在 { } 外 不能直接调用
在 循环中 定义 循环变量
每次循环都是一个独立的作用域
生成一个独立的循环变量
存储独立的当前循环的循环变量数值
const:
用于 定义 引用数据类型
定义的变量 不会 预解析
定义的变量 变量名称不能重复
如果定义在 { } 中 作用域 就是 在 { } 中
在 { } 中 可以调用
在 { } 外 不能直接调用
const 定义的变量 不能重复赋值
不能用于定义循环变量3.解构赋值:
将 数组/对象 中 存储的数据 赋值给 变量储存
数组的解构赋值
let [ 变量 , 变量... ] = 数组 ;
一维数组解构赋值
let [ 变量 , [变量...] ] = 数组 ;
多维数组解构赋值
对象的解构赋值
let { 属性 , 属性:变量... } = 对象 ;
一维对象
let { 属性 , 属性:{ 属性 , 属性:变量 } ... } = 对象 ;
多维对象4.展开合并运算符:
展开运算符
在 调用函数赋值实参时 将 数组展开
数组中的每一个单元存储的数据按照顺序一一对应的赋值给形参
函数调用(...数组);
合并运算符
在 定义函数形参时 将 对应的实参
以 数组的形式 存储
function 函数名称( 形参 , 形参 , ...形参 ){ }
调用函数( 实参 , 实参 , 实参 , 实参 , 实参 , 实参.... );
从 第三个实参开始 剩余的所有实参
都已 数组的形式存储到 展开运算符 定义的第三个形参中
arguments
是 每一个函数 天生就有的属性
以 伪数组的形式存储所有实参ES6结构图 详解
双向数据绑定原理图
ES5 ES6构造函数 对比 详解
ES5 ES6构造函数 对比图
JS ES6 Map 与 Set
ES6 Map 与 Set 介绍
Map数据类型:
是 ES6 新增的数据类型
类似于 对象形式 存储数据
创建:
const 变量 = new Map( [ [ 键名 , 键值 ] , [ 键名 , 键值 ] , [ 键名 , 键值 ] .... ] );
以 二维数组的形式定义 Map数据类型存储的数据数值
二维数组中 第一个单元 存储 键名
二维数组中 第二个单元 存储 键值
新增:
Map对象.set( '键名' , 键值 );
获取:
Map对象.get( '键名' );
删除:
Map对象.delete( '键名' );
清空:
Map对象.clear();
判断有没有:
Map对象.has( '键名' );
循环:
Map对象.forEach(function( 参数1 , 参数2 , 参数3 ){})
参数1 键值
参数2 键名
参数3 原始MapSet数据类型:
是 ES6 新增的数据类型
类似于 数组形式 存储数据
不会存储重复的数据
创建:
const 变量 = new Set( 数组 );
以 数组的形式 赋值 Set数据类型要存储的数据
新增:
Set对象.add( 数据 );
删除:
Set对象.delete( 数据 );
清空:
Set对象.clear( 数据 );
循环:
Set对象.forEach(function(参数1 , 参数2 , 参数3){})
参数1 数据数值
参数2 数据数值
参数3 原始set
一般会将 Set数据类型转化为数组
使用 展开运算符 将 Set类型的数据 作为数值单元的数据存储
const 变量 = [ ...Set对象 ] ;
可以使用Set数据类型执行 数组去重操作
const 新数组 = [ ...new Set( 原始数组 ) ];JS ES6 箭头函数
ES6 箭头函数 介绍
是 ES6 新增的语法形式
为了 配合 面向对象编程 定义的
语法形式
使用 箭头函数() => {} 替换 匿名函数function(){}
简写语法形式
如果 只有一个参数 () 可以不写
如果 只有一行代码 {} 可以不写JS this指向
this指向 介绍
每一个函数天生就有的 属性
触发执行函数是 JavaScript程序 给 this设定 指针指向
this指向谁 在函数中 就可以 通过 this 操作谁普通函数:
window
赋值式函数 声明式函数
定时器 延时器
forEach循环 ....
事件源
事件绑定中 事件处理函数
事件处理函数中 不管是什么标签触发的事件
this指向永远是 事件源
数组/对象
存储在 数组/对象 中的函数
this指向是存储这个函数的 数组/对象箭头函数:
this指向 不能改变
是 父级程序的this指向
如果 父级程序没有this指向 或者 没有赋值程序
箭头函数的this指向是window改变this指向:
在函数执行时 改变this指向:
函数.call( 新this指向 , 实参1 , 实参2... );
第一个实参是 新this指向
其他实参是 原始函数执行需要的实参
函数.apply( 新this指向 , [ 实参1 , 实参2... ] );
第一个实参是 新this指向
第二个实参是 数组形式 定义的是原始函数执行需要的实参
call 和 apply 执行效果完全相同
只是 给 原始函数赋值实参的语法形式不同
在函数封装时 改变this指向:
const 新函数名称 = 函数.bind( 新this指向 );
创建一个新函数 将 内存地址赋值给 变量储存
如果只设定 新this指向 调用函数时 需要赋值 实参
const 新函数名称 = 函数.bind( 新this指向 , 实参1 , 实参2... );
如果封装新函数时 绑定了实参
调用函数时 只会按照绑定的实参 执行程序构造函数中改变this指向:
在 构造函数中 因为 和 new 关键词一起调用
构造函数中 this指向 都是 实例化对象
在 构造函数中 操作调用的 都是 实例化对象中 属性存储的数据
this指向 是 实例化对象
this.属性 才能 从 实例化对象中调用数据改变this指向的方法:
第一类在某个独立的函数中:
方法1 匿名函数 修为 箭头函数
方法2 定义变量 提前存储this指向
在 构造函数的函数方法中 this指向是 实例化对象
在 独立的函数中 this指向 不是 实例化对象
第二类回调函数:
以 回调函数的语法形式 执行 构造函数中的函数方法
整个 函数方法 this指向 都是 undefined
方法3 赋值 回调函数时 使用 bind语法 绑定this指向JS 面向对象编程
面向对象编程 介绍
1. 概念:
通过对于具有相同属性和函数方法的一类事物的描述 实现代码功能
对于具有相同属性和函数方法的抽象类的描述 实现代码功能
理解
! 定义一个对象
利用对象的属性存储的数据
将 程序执行需要的数据都以属性的形式
存储在对象中
利用函数方法存储程序代码
将 执行程序需要的代码
以 函数方法的形式存储
调用执行函数方法时
使用的数据是 对象属性中存储的数据2. 语法形式:
工厂模式:
function 函数( 形参 , 形参... ){
const obj = {} ;
创建一个空对象
obj.属性 = 形参;
obj.属性 = 形参;
....
设定对象的属性和属性值
obj.函数方法 = function(){}
obj.函数方法 = function(){}
....
设定对象的函数方法
return obj ;
返回值是这个创建的对象
}
工厂模式 是 创建 属性和函数方法相同
属性值不同的 对象
创建的对象 都 相同的属性 不同的属性值
还有 完全相同的函数方法
每一个对象都存储了完全相同的函数方法
造成有 冗余的代码程序
ES5语法:
function 构造函数( 形参 , 形参... ){
this.属性 = 形参;
this.属性 = 形参;
....
在 构造函数中 通过 this 指向 设定操作对象的属性
}
构造函数.prototype.函数方法 = function(){}
构造函数.prototype.函数方法 = function(){}
....
在 构造函数外 在 构造函数的prototype中 定义 函数方法
const 实例化对象 = new 构造函数( 实参 , 实参... );
通过 new 关键词 调用 构造函数 创建实例化对象
ES6语法:
使用class关键词 定义 构造函数
class 构造函数{
在构造器中定义 对象的属性属性值
constructor( 形参 , 形参... ){
this.属性 = 形参 ;
this.属性 = 形参 ;
....
}
在构造器外 定义 函数方法 js程序自动定义在 prototype 中
函数方法(){}
函数方法(){}
....
}
const 实例化对象 = new 构造函数( 实参 , 实参... );
通过 new 关键词 调用 构造函数 创建实例化对象总结
1. 构造函数:
专门创建实例化对象 设定实例化对象 返回实例化对象的函数
构造函数 必须使用 大驼峰命名法
构造函数 一般都是和new关键词一起调用
构造函数 中 不会 独立设定 return
2. 实例化对象:
通过 构造函数 创建的对象
通过 实例化对象 可以 调用 构造函数prototype中存储的函数方法
3. new:
调用构造函数时使用的关键词
作用1
调用函数程序时 自动在函数程序内 创建一个 实例化对象
自动 return 返回这个 实例化对象
作用2
设定 一起调用的构造函数 this 指向是这个 实例化对象
4. 实例化对象的属性:
实例化对象的属性 用于 存储 数据
构造函数中 形参的数据 必须要使用 实例化对象的属性存储
构造函数中 多个函数程序都需要使用的数据
5. 函数方法:
函数方法 不是 直接存储在 实例化对象中
是 存储在 构造函数 prototype 中
这个构造函数 创建的实例化对象 都可以调用使用
函数方法中调用的数据 都是 实例化对象 属性中存储的数据
this.属性
6. prototype:
是 每一个 函数天生就有的属性
是 一个公共的存储空间 可以 存储 数据 和 函数方法
构造函数 创建的实例化对象 可以 调用 prototype 中 存储的数据和函数方法
7. this指向:
构造函数 中 this指向 必须是 实例化对象
构造函数 和 构造函数prototype中存储的函数方法 this指向都是 实例化对象
设定 和 操作的数据 都是 实例化对象属性中存储的属性值
this指向 必须是 实例化对象
构造函数中 其他的函数程序 this 不一定是 实例化对象
解决方法1
将 匿名函数 设定为 箭头函数
比较简单实用的方法 是 常用方法
解决方法2
提前定义一个变量 存储 this指向是 实例化对象
将 其他函数中要使用的this 替换为 这个变量
解决方法3
使用 call() apply() bind() 等 方法 设定this指向JS 原型 原型链
原型 原型链 介绍
面向对象编程中:
构造函数 创建的实例化对象
可以 调用 构造函数中 prototype 中 存储的数据
执行的是 JavaScript中 原型链的数据调用机制
之前 接触过 函数中数据的访问机制
面向对象中 数据的访问机制
prototype:
原型 / 原型对象
是 每一个 函数天 生就有的属性
本质上是一个 对象 数据类型
是一个 公共的存储空间
可以 存储属性属性值
可以 存储函数方法
实际项目中 一般只会存储 函数方法
__proto__:
原型属性
是 每一个 对象 天生就有的属性
低版本浏览器 对象中 显示 __proto__
高版本浏览器 直接显示成 [[Prototype]]
对象的 __proto__ 本质上 是 一个 对象
对象的 __proto__ 指向的是 生成这个对象的构造函数的 prototype
也就是 实例化对象 本质上 是 通过 __proto__
访问 构造函数的 prototype
因为 实例化对象 有 __proto__ 才能 访问 构造函数的 prototype
! 原型链访问机制:
实例化对象 通过 __proto__ 访问 构造函数 prototype
因为 __proto__ 指向 构造函数 prototype
这个数据的访问调用机制 是 原型链最基本的数据访问机制JS 继承
继承 介绍
面向对象的继承:
创建一个新的 构造函数
从 已经定义好的构造函数中 继承 属性的定义 和 调用函数方法
为了优化代码 减少冗余的程序
适用于 大型项目开发
实际项目中 使用 模块化开发方式
适用于 继承语法进行开发ES5 继承语法:
属性继承
核心原理
在 子类构造函数中调用父类构造函数
通过 call / apply 语法 改变父类构造函数 this指向
同时 给 父类构造函数 赋值实参
函数方法继承
核心原理
定义 子类构造函数 prototype 赋值存储 父类实例化对象
子类构造函数 prototype 也就是 父类实例化对象
可以 调用 父类构造函数 prototype 中的 函数方法
组合继承
两种继承方法一起使用ES6 继承语法:
通过 关键词 完成 面向对象继承
extends
定义 继承的父类构造函数
定义了 extends 自动继承父类构造函数所有的属性和函数方法
super
在 构造器中 定义 父类构造函数 赋值的实参JS 正则表达式
正则表达式 介绍
1. 概念:
正则表达式是JavaScript中一种数据类型
用于 对字符串内容进行验证2. 语法:
字面量语法
let 变量 = /正则/ ;
推荐使用的语法形式
不能解析变量
如果正则表达式中包含变量
需要使用模板字符串解析
通过 eval() 来执行
构造函数语法
let 变量 = new RegExp( 正则 , 'g' 或者 'i' );
可以解析变量
如果需要定义 修饰符 g / i
需要定义在 参数2 中3. 元字符:
\d 数字
\D 非数字
\w 数字 字母 下划线
\W 非数字 字母 下划线
\s 空格
\S 非空格
. 任意内容(非换行)4. 边界符:
^ 起始符号
$ 结束符号5. 限定符:
限定符 一般都是配合边界符使用的
* 0 至 正无穷个
+ 1 至 正无穷个
? 0 至 1 个
{n} n个
{n,} n 至 正无穷个
{n,m} n 至 m 个6. 特殊符号:
| 逻辑或
() 将其中的内容作为一个整体
[] 其中任意一个内容就可以
n-m n至m个
[^] 取反7. 修饰符:
写在 正则表达式外 一般是配合函数等使用
/ /i
忽略大小写
大小写不敏感
/ /g
全局匹配8. 正则表达式函数方法:
正则.test(字符串);
验证字符串内容 是不是 符合 正则表达式
如果 符合 结果是 true
如果 不符合 结果是 false
正则.exec(字符串);
捕获字符串中 符合 正则表达式的内容
如果 正则表达式 没有 / /g 全局匹配
如果没有匹配内容 获取结果 null
如果有匹配内容 获取结果是 一个 数组
数组中带有字符串索引的单元
索引下标是 0 的单元 存储 捕获的数据
! 多次捕获 获取结果都是 第一个符合正则的内容
如果 正则表达式 有 / /g 全局匹配
如果没有匹配内容 获取结果 null
如果有匹配内容 获取结果是 一个 数组
数组中带有字符串索引的单元
索引下标是 0 的单元 存储 捕获的数据
! 多次捕获 是 从字符串中 每次获取一个符合的内容
直到获取结果是 null 从字符串起始 再次开始 重新获取9. 和正则表达式配合的字符串函数:
字符串.search( /正则/i );
查询匹配内容
支持 正则表达式 忽略大小写
字符串.replace( /正则/g , 新内容 );
字符串替换
支持 正则表达式 替换所有匹配的内容
字符串.match( /正则/ 或者 /正则/g );
字符串捕获
如果 是 没有 全局匹配的正则表达式
获取结果 和 正则.exec(字符串) 执行结果相同
如果 是 有 全局匹配的正则表达式
获取结果 是 一个数组
数组单元是所有符合结果的数据JS 递归函数
递归函数 介绍
递归函数 是 函数的一种调用方式
通过在函数内部 调用 函数自己
! 必须要确定 什么时候 终止递归
也就是 return 的是 一个数据 不是 递归调用
! 递归嵌套的进入 是 从 外层递归 进入 内层递归
! 递归嵌套的结束 是 从 内层地址 返回 外层递归
function 函数(){
函数()
}JS 闭包
闭包 介绍
概念:
函数的一种应用方式
将 容易被污染的全局变量 定义成局部变量
在函数中通过返回值是匿名函数的方式
操作函数中的局部变量函数的基本执行原理:
封装:
1 在 堆 中 创建 存储空间 准备存储函数程序
操作系统给存储空间分配内存地址
2 将 函数程序 以 字符串形式存储到 存储空间中
3 将 函数名称 存储 到 栈 中
函数名称中存储的是 函数的内存地址调用执行:
1 根据 栈 中 存储的函数名称
解析其中存储的内存地址
找到对应的存储空间
读取其中存储的函数程序代码
2 给 形参赋值实参
3 函数程序 预解析
4 执行函数
在函数的存储空间中 创建一个 执行空间
将函数的局部变量都定义在 执行空间中
在函数执行是 执行空间存在 存储 函数的局部变量
函数执行结束 执行空间销毁 局部变量也会被销毁
这样的程序执行方式 称为 JavaScript内存回收机制生成不会被销毁的执行空间:
函数的返回值 是 引用数据类型
在函数外 有 对应的变量 存储 函数返回值
也就是 存储 引用数据类型的内存地址
引用数据类型的内存地址 正在函数外被使用
执行空间就不会被销毁闭包的基本语法:
function 函数(){
局部变量
return function(){
操作局部变量
}
}闭包的优点缺点
(1)优点:
- 函数内部的其他的变量不会对代码全局环境造成污染
(2)缺点:
- 函数执行完后,函数内部的局部变量没有释放,占用内存时间会变长,
导致变量不会被垃圾回收机制回收,造成内存消耗
- 不恰当的使用闭包可能会造成内存泄漏的问题JS 常见的模式
常见的模式 介绍
1.单利模式:
改造 构造函数
不是在实例化对象中存储数据
是在调用执行函数时 输入对应的实参
通过 闭包的语法形式
将 关键变量 存储到函数中
防止全局变量污染
通过 调用 return 的匿名函数
操作调用 被保护的全局变量
单利模式 创建的实例化对象 都是 同一个实例化对象2.设计模式(策略模式):
将 多种情况的if判断 进行修改
将 每一种条件 情况 设定成 列表
通过 列表 动态渲染生成页面
新增情况 在列表中 新增 对象/数组单元
删除情况 在列表中 删除 对象/数组单元 或者 修改 对象/数组单元属性
核心
将 多种情况的 if判断 或者 switch判断
写成 通过属性调用属性值
减少 if判断 提高程序的执行效率3.观察者模式(发布订阅模式):
主体数据状态改变 通知相关个体数据状态也执行相应的改变
需要配合 三阶段的双向数据绑定执行
需要一个消息盒子
以对象的形式存储 事件类型 和 事件处理函数
对象的键名是 事件类型
对象的键值是 事件处理函数
事件处理函数 以 数组的形式存储
需要一个新增方法
通过新增方法 向 消息盒子中 新增 事件类型和事件处理函数
参数1 事件类型名称
参数2 以 展开合并运算符形式 使用 数组存储新增事件处理函数
如果 要新增的事件类型 不存在
直接给 消息盒子新增 事件类型和事件处理函数
如果 要新增的事件类型 已经存在
循环遍历 要新增的事件处理函数 没有存储在 消息盒子中
再 向 消息盒子中的对应数组 新增事件处理函数
需要一个删除方法
通过删除方法 删除消息盒子中 指定 事件类型 中 存储的事件处理函数
参数1 事件类型名称
参数2 以 展开合并运算符形式 使用 数组存储新增事件处理函数
循环遍历方法1 参数2
需要判断 如果 消息盒子对应的数组中 存储了 要删除的事件处理函数
再执行 数组删除操作
如果直接删除 查询结果是 -1 删除的最后一个单元
循环遍历方法2 消息盒子数组
触发删除操作的数组 和 循环遍历的数组是同一个数组
防止数组坍塌的影响
需要一个执行方法
通过执行方法 执行消息盒子中 指定 事件类型 中 存储的事件处理函数
参数1 事件类型名称
参数2 以 展开合并运算符形式 使用 数组存储新增事件处理函数
循环遍历
参数2 数组中存储的事件处理函数
如果 在 消息盒子数组中存储
触发执行 参数2 中 存储的事件处理函数http协议
http协议 介绍
概念:
是 客户端 和 服务器 执行数据交互时 遵守的有线网络传输协议
http协议 是 目前使用最广泛的 网络传输协议
https协议 是 加密的 网络传输协议
每一个 客户端和服务器 都有独立的 加密的 密钥三次握手:
客户端和服务器 正式创建连接之前 触发执行的程序
目的是为了确保 客户端和服务器 都能正常工作
第一次握手:
客户端 ---> 服务器 发送请求
客户端
客户端 正常 发送请求
服务器
服务器 正常 接收请求
客户端 正常 发送请求
第二次握手:
服务器 ---> 客户端 发送请求
服务器
服务器 正常 发送请求
客户端
客户端 正常 接收请求
服务器 正常 发送请求
服务器 正常 接收请求
第三次握手:
客户端 ---> 服务器 发送请求
服务器
客户端 正常 接收请求四次挥手:
客户端 和 服务器 正式断开链接之前 触发执行的程序
第一次挥手:
客户端 ---> 服务器 发送请求
客户端 告诉 服务器 客户端准备断开连接
第二次挥手:
服务器 ---> 客户端 发送请求
服务器 告诉 客户端 服务器知道客户端准备断开链接
服务器 向客户端发送 询问 等待 客户端回应
如果 客户端没有回应
表示 客户端要继续执行 断开连接操作
服务器 继续执行 四次挥手
第三次挥手:
服务器 ---> 客户端 发送请求
服务器 告诉 客户端 服务器 正式关闭
服务器 不在 发送请求
服务器 可以 接收请求
第四次挥手:
客户端 ---> 服务器 发送请求
客户端 告诉 服务器 客户端知道服务器已经关闭了
客户端 也关闭了
客户端 不再 接收请求
客户端 可以 发送请求请求报文:
请求报文是发送请求时执行的http协议程序请求行:
POST
请求方式
/demo.php
请求url地址
HTTP/1.1
请求协议 以及 协议版本请求头:
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,;q=0.8,application/signed-exchange;v=b3;q=0.9
希望的 后端程序响应的文件格式
Host: 127.0.0.1
服务器 IP地址 或者 域名
Origin: http://127.0.0.1
服务器 根目录地址
Referer: http://127.0.0.1/demo.html
当前 发起请求的文件路径
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36
当前 浏览器配置信息
Content-Type: application/x-www-form-urlencoded
! post请求 特有的 请求头设定请求空行:
请求头 和 请求体 之间的空行请求体:
post方式才有请求体
get方式通过浏览器地址栏携带参数 没有请求体响应报文:
响应报文是发送响应时执行的http协议程序响应行:
HTTP/1.1
协议和版本
200
http状态码
OK
http状态描述响应头:
Server: nginx/1.15.11
服务器类型和版本
Date: Thu, 22 Sep 2022 08:19:52 GMT
服务器时间 世界标准时
Content-Type: text/html; charset=UTF-8
响应内容 编码格式
X-Powered-By: PHP/7.3.4
后端语言和版本响应体:
后端程序输出的内容http状态码:
100 - 199
连接成功 等待下一步操作
200 - 299
请求成功
300 - 399
重定向请求
400 - 499
请求错误 原因是 客户端 引起的
500 - 599
请求错误 原因是 服务器 引起的ajax
ajax 介绍
ajax交互
通过 js 技术 向 指定的服务器路径 发送请求
使用的是http协议
天生就是异步程序
可以通过设定参数 设定为 同步程序
ajax请求 是 局部数据交互
不用跳转页面就可以实现数据交互
步骤过程
1. 创建 ajax实例化对象
2. 设定 请求方式 和 请求地址
3. 发送 配置好的请求
4. 接收 请求结果promise
promise 介绍
回调地狱:
异步程序中 回调函数 往往需要嵌套执行
1, 异步程序如果定义的并列的形式
异步程序会同时执行
2, 不同的异步程序之间数据的调用
因为并列语法的异步程序是同时执行
上一个异步程序还没有执行结束
下一个异步程序已经开始执行
无法获取上一个异步程序的数据promise:
ES6新增语法 专门解决回调地狱
创建 promise实例化对象
通过 promise执行异步程序
通过 promise实例化对象中 .then() 方法 定义 异步程序执行成功 触发的函数程序
通过 promise实例化对象中 .catch() 方法 定义 异步程序执行失败 触发的函数程序
promise的三个状态
状态1 异步请求还在执行
peading
状态2 异步请求执行成功
fulfilled
状态3 异步请求执行失败
rejected
promise的执行状态一旦确定 就不能更改了
基本语法
const p = new Promise(function( fulfilled , rejected ){
异步程序;
})
p.then(function(){ 成功执行的程序 });
p.catch(function(){ 失败执行的程序 });
promise的函数封装
function fun( 异步程序参数 ){
const p = new Promise(function( fulfilled , rejected ){
异步程序;
})
return p ;
}
fun( 异步程序参数 )
.then(function(){
成功的程序
return fun()
})
.then()async与await:
ES7新增语法 专门配合 promise使用的语法形式
通过 async 声明 函数
在 async声明的函数中 通过 await 调用封装的promise函数
执行结果是 异步请求的响应体数据
async function fun(){
const res1 = await 封装的promise函数();
const res2 = await 封装的promise函数();
const res3 = await 封装的promise函数();
}JS 同源/跨域 请求
同源/跨域 请求 介绍
同源请求 跨域请求
发送请求时 请求由 3部分 组成
请求协议
请求地址
服务器端口
http://localhost:8888/test/fourth
http
请求协议
localhost
请求地址
8888
服务器端口
如果不写端口默认端口是 80
发送请求时 文件地址路径 和 接受请求的服务器地址路径
如果 这三项配置有一项不同
就是 跨域请求
如果 这三项配置都相同
就是 同源请求服务器执行的同源策略:
所谓的服务器同源策略 指的是
只有同源请求 服务器才会允许
请求才能正常执行
如果是跨域请求 服务器默认会阻止这个请求
发送的跨域请求就不能正常执行解决跨域访问的方法:
1. cors:
后端程序 通过 设定 cors代码程序
告诉浏览器 当前后端程序 支持 任意请求访问
对于这个后端程序 服务器不会阻止跨域请求
2. jsonp:
本质 前端定义函数
后端程序调用函数 同时赋值后端程序的实参
(1) 前端程序定义好一个函数
函数必须有一个形参
形参存储后端程序赋值的实参数据
函数的程序 就是 操作形参 也就是 操作后端的实参数据
(2) 定义一个script节点标签
(3) 设定script节点 src属性
也就是后端程序的数据接口地址
携带后端程序需要的数据参数
其中一定有一个键名传参函数名称
(4) 写入script标签
(5) 删除script标签
3. proxy服务器代理方法:
通过服务器设定代理配置
通过服务器运行文件 使用 代理 发送请求浏览器 本地存储
浏览器 本地存储 介绍
localStorage:
浏览器提供的本地存储方式
以 键值对 的形式 存储数据
可以使用 JavaScript定义的函数方法操作
可以使用 对象操作语法形式操作
设定:
window.localStorage.setItem( 键名 , 键值 );
window.localStorage.键名 = 键值 ;
获取:
let 变量 = window.localStorage.getItem( 键名 );
let 变量 = window.localStorage.键名 ;
删除:
window.localStorage.removeItem( 键名 );
delete( window.localStorage.键名 );
清空:
window.localStorage.clear();cookie:
浏览器提供的本地存储方式
cookie的主要属性包括
键名 键值 路径 时效
路径
符合路径的文件才能访问cookie
时效
超过时效 浏览器会自动删除cookie
判断 两个cookie是不是相同
需要判断 cookie 的 键名 和 路径
cookie的设定语法:
设定
document.cookie = '键名=键值;path=路径;expires=时效';
键名 键值 是 必须设定的属性
路径
如果不设定路径 JavaScript程序自动设定路径
设定成当前文件所在的文件夹
如果设定路径 / 设定是服务器根目录
实际项目中 每个服务器都有自己的根目录
时效
如果不设定时效 JavaScript程序自动设定时效
设定成会话时效 session时效
浏览器执行 cookie存在
浏览器关闭 cookie删除
如果设定时效 设定的是 时间时效
设定的时间时效 必须是 世界标准时
如果要删除 cookie 只要设定cookie时效过期
获取:
let 变量 = document.cookie
获取的结果是 键值对 字符串
需要转化为 对应的 对象cookie和localStorage的区别:
1 cookie运行必须要通过服务器
localStorage可以本地运行 不经过服务器
2 cookie前端程序 后端程序 都可以操作
localStorage只能前端程序操作
3 cookie有路径 符合路径的文件才能访问
localStorage路径就是根目录路径
4 cookie有时效 不符合时效的cookie会自动删除
localStorage没有时效 除非手动删除 不然会一直存在
5 cookie存储文件比较小 一般是 4K 左右
localStorage存储文件比较大 一般是 4M 左右
6 cookie只能存储字符串
localStorage可以存储字符串和json字符串实际项目中:
使用 cookie 存储简答的字符串数据
使用 localStorage 存储 复杂数据转化的json字符串
cookie 和 localStorage 都是本地存储
安全性不高 不会存储 关键数据信息nodejs
nodejs天生就是异步程序
nodejs 介绍
概念:
基于 chrome v8 引擎的 JavaScript运行环境
将 浏览器 chrome V8 引擎 剥离出来
生成一个独立的运行环境 运行 JavaScript程序浏览器运行和nodejs运行的对比:
浏览器 运行 JavaScript
可以
ECMAScript
DOM
BOM
不能
I/O 线程操作
nodejs 运行 JavaScript
可以
ECMAScript
I/O 线程操作
不能
DOM
BOMnodejs运行方式:
1. window操作系统自带的 powershell
模拟 cmd
有些命令和cmd不同
不推荐使用
2. cmd 命令行
3. vscode 终端
设定终端是 cmd 方式nodejs运行JavaScript程序方式:
1. 直接运行程序
在 任意路径下 输入 node 回车
进入 node编辑环境
直接输入 js程序
输入的程序 执行的结果 不会保存
关闭 cmd 程序和结果就会丢失
2. 运行 外部文件
切换路径到文件所在文件夹
盘符: 回车
切换盘符
cd 空格 路径 回车
切换路径
node 文件名 回车
运行对应的文件模块化开发
模块化开发 介绍
将 计算机程序 以 导出导入的方式加载
防止全局变量污染
依赖清晰后端模块化:
nodejs 的 commonJs
导出
module.exports = { }
导入
const 变量 = require( 路径 )前端模块化:
IIFE:
通过立即执行函数 向 window顶级对象 添加 数据
(function(){
定义数据
window.键名 = { };
})()
AMD:
前置依赖:
执行程序时会按照顺序 加载执行所有的 模块化文件
初次打开页面 会比较卡顿
之后程序程序会比较流畅
需要导入一个 require.js 文件
语法形式:
独立模块:
define(function(){
定义数据
return { 导出的数据 }
})
依赖模块:
define( [ 路径1 , 路径2 ... ] , function( 形参1 , 形参2 ...){
定义数据
return { 导出的数据 }
})
整合模块:
require( [ 路径1 , 路径2 .... ] , function( 形参1 , 形参2 ...){
执行的程序
})
CMD:
即时依赖:
需要哪个模块在加载哪个模块
初次打开页面执行流畅
之后页面滚动 触发加载新的模块 会出现卡顿
需要 sea.js 外部文件
语法形式:
独立模块:
define(function(参数1 , 参数2 , 参数3){
定义数据
参数3.exports = { }
})
依赖模块:
define(function(参数1 , 参数2 , 参数3){
const 变量 = 参数1( 地址路径 );
定义数据
参数3.exports = { }
})
整合模块:
seajs.use([ 路径1 , 路径2 ....] , function( 形参1 , 形参2 ... ){
要执行的程序
})
ES6:
设定 script标签 type属性为 module
需要通过 服务器运行 文件
语法形式:
html标签 script标签
<script type="module"></script>
js文件:
直接定义 数据
整个导出
export default { 导出的数据 }
整个导入
import 变量 from 路径
! 以对象的形式 导出导入数据
独立导出
export 变量1 = 数据 ;
export 变量2 = 数据 ;
export 变量3 = 数据 ;
独立导入
import 变量1 , 变量2 , 变量3 from 路径
! 以变量的形式 导出导入数据内置模块
内置模块 介绍
fs:
读取 设置 文件内容
导入:
const fs = require('fs');
使用:
fs.readFile( '路径' , '编码格式' , function( 参数1 , 参数2 ){
参数1 路径
需要读取文件的路径
参数2 编码格式
读取文件获取内容的编码格式
默认是 buffer格式
以 十六进制 形式 表示 字符内容
可以设定 utf8 / utf-8 格式
参数3 回调函数
回调函数的参数1
存储报错信息
如果 正确读取文件 存储 null
如果 文件不能正常读取 存储 报错信息
回调函数的参数2
存储读取文件内容信息
如果 争取读取文件 存储 读取的文件信息
如果 文件不能正确读取 存储 undefined
})
fs.writeFile( '路径地址' , '写入内容' , function(){
参数1 要写入内容的文件的路径地址
如果 有这个文件 就 对这个文件写入内容
如果 没有这个文件 就 创建这个文件 写入内容
写入操作 一定会执行成功
参数2 要写入的内容
写入内容必须是字符串格式
参数3 回调函数
写入完成 执行的回调函数
})url:
解析 浏览器地址栏 url地址信息
导入
const url = require('url');
使用
const 变量 = url.parse( 'url地址' ) ;
解析 url 中 地址信息
const 变量 = url.parse( 'url地址' , true ) ;
深度解析 url 中 地址信息
实际项目中使用 深度解析 解析url地址
将 携带的参数 转化为 对象形式
重要的两个属性
query 携带的参数 深度解析是以对象的形式存储
pathname 路径地址
protocol: 'http:',
请求协议
slashes: true,
使用 url地址 标准语法 也就是 /语法
auth: null,
授权
host: '127.0.0.1:5500',
域名 / IP地址 + 端口号
port: '5500',
端口号
hostname: '127.0.0.1',
域名 / IP地址
hash: null,
哈希值
search: '?category=%E7%AB%A5%E8%A3%85%E7%8E%A9%E5%85%B7',
携带的参数 有问号的
query: 'category=%E7%AB%A5%E8%A3%85%E7%8E%A9%E5%85%B7',
携带的参数 没有问号的 也就是 键值对
query: [Object: null prototype] { category: '童装玩具' },
如果是 深度解析 以 对象形式存储 携带参数
pathname: '/week7/day04/02_%E4%BB%A3%E7%A0%81/shop_demo3/pages/list.html',
路径的名称
path: '/week7/day04/02_%E4%BB%A3%E7%A0%81/shop_demo3/pages/list.html?category=%E7%AB%A5%E8%A3%85%E7%8E%A9%E5%85%B7',
路径名称 + 携带参数
href: 'http://127.0.0.1:5500/week7/day04/02_%E4%BB%A3%E7%A0%81/shop_demo3/pages/list.html?category=%E7%AB%A5%E8%A3%85%E7%8E%A9%E5%85%B7'
完成的url地址 协议 + 域名/IP地址 + 端口号 + 路径 + 携带参数path:
操作路径
导入
const path = require('path');
使用
let 变量 = path.join();
将 设定的内容拼接为地址字符串
let 变量 = path.resolve();
将 设定的内容拼接为地址字符串
如果不设定根目录路径 会自动添加跟目录路径
let 变量 = path.parse();
解析 设定的地址路径
{
root: '',
根目录信息
如果是 url网址 没有根目录信息
dir: 'http://127.0.0.1:5500/week7/day04/02_%E4%BB%A3%E7%A0%81/shop_demo3/pages',
当前文件的详细信息
base: 'list.html?category=%E7%AB%A5%E8%A3%85%E7%8E%A9%E5%85%B7',
文件名 和 携带的参数
ext: '.html?category=%E7%AB%A5%E8%A3%85%E7%8E%A9%E5%85%B7',
文件的扩展名 和 携带的参数
如果没有携带参数 就是 .html 扩展名本身
name: 'list'
文件名称
}http:
设定一个本地服务器 响应前端请求
响应前端请求 根据前端请求的内容 返回响应的 静态文件 / 数据数值
发送的是 静态请求 响应结果是 html css js 图片等文件内容
发送的数 数据请求 响应结果是 数据数值 一般是 json字符串
导入
const http = require('http');
使用
步骤1 创建服务器
const server = http.createServer(function( 参数1 , 参数2 ){
定义服务器的响应内容
参数1 存储请求中相关内容
参数1.url
发送的请求的相关地址信息
参数2 存储响应的相关内容
参数2.end()
设定响应给浏览器的内容
});
步骤2 设定监听端口
server.listen( 端口号 , 回调函数(){})
端口号的范围是 0 - 65535
只要不和其他程序的端口好冲突 就可以服务器的核心原理:
根据 前端发送来的不同 请求地址
响应 不同内容结果( 静态文件 / 数据数值 )后端程序的报错优先:
后端程序 执行的是 本地文件或者数据库操作
执行的都是 敏感操作
会执行 报错优先原则
方式1
有报错 就会执行之后所有程序的执行
方式2
默认不执行程序
有 之前程序执行成功的返回结果
再 执行后续的程序创建 服务器
创建 服务器 介绍
内置模块http:
通过 内置模块http 理解服务器搭建的基本执行原理
导入内置模块http:
const http = require( 'http' );
创建服务器:
const server = http.createServer(function( request , response ){
设定 服务器的各种响应
这个 所谓的响应 是 针对前端程序发送的请求的响应
本质上是在设定 网址 url地址
request.url 存储 前端发送请求的url地址
request.method 存储 前端发送请求的方式
request.headers 存储 请求头信息
request.headers 中 content-type 属性 存储 请求头编码格式
application/x-www-form-urlencoded
普通的键值对字符串传参
application/json
json字符串传参
请求头 常见格式
https://blog.csdn.net/qq_24147051/article/details/90477756
request.url 请求地址可以使用 内置模块url深度解析
const result = url.parse( request.url , true );
解析的结果 是一个 对象类型
result.query 是 对象形式存储 携带的参数
如果没有携带参数 结果是 {} 空对象
result.pathname 是 地址路径
如果 pathname 是 / 请求的是 首页面
使用 fs模块 按照 相对路径 读取 首页面内容响应
如果 pathname 是 /static 开头 请求的是 静态资源(返回结果是文件内容)
按照 文件的扩展名 和 文件名称
拼接 相对路径 ( 扩展名对应的文件夹 + 文件名 )
使用 拼接的相对路径 通过 fs模块 读取 对应的文件内容
如果 pathname 是 /api 开头 请求的是 数据接口(返回结果是数据数值)
按照 请求地址 设定 响应内容
如果 pathname 是 /api/register 执行注册操作
例如 执行 get操作
判断 请求方式 是不是 get
判断 携带参数
...
获取携带参数 操作数据库 响应 操作结果数据
! get方式获取参数
解析浏览器地址栏中携带的键值对字符串就可以了
如果 pathname 是 /api/login 执行登录操作
例如 执行 post操作
判断 请求方式 是不是 post
判断 携带参数
...
获取携带参数 操作数据库 响应 操作结果数据
! post方式获取参数
let str = '' ;
request.on( 'data' , function( 形参 ){
str += 形参
})
获取的携带参数结果是 buffer字符串
request.on( 'end' , function(){
根据 request.headers 中 content-type 存储的请求头编码格式 进行 解析
如果是 application/x-www-form-urlencoded
在 结果前拼接 ? 问号
写成 浏览器地址栏携带参数的语法形式 ?键名=键值&键名=键值...
使用 内置模块url的 url.parse() 进行深度解析
解析结果中 query属性 存储的是 参数对象
如果是 application/json
使用 JSON.parse() 解析
根据 请求参数 执行数据库操作 响应结果
})
如果 pathname 不是/ 不是/static开头 不是/api 开头 地址错误
});
设定监听端口:
server.listen( 8088 , function(){ console.log( '服务器开启 监听 8088 端口' ) })创建 服务器 代码
// 导入依赖包
const express = require('express');
// 创建服务器
const server = express();
// 静态资源配置
// 根据设定的网址 按照 参数1 设定的 截取之后的地址
// 参数2 设定的地址之前 拼接 参数1 截取的地址
// 按照拼接的路径读取文件 响应内容
server.use( '/static' , express.static('./src') );
/*
数据接口配置
server.get( '请求地址' , function( 参数1 , 参数2 ){})
响应get请求方式
参数1 request 存储的是请求的相关信息
参数2 response 设定的是响应的相关信息
response.send() 设定响应的内容
server.post( '请求地址' , function( 参数1 , 参数2 ){})
响应post请求方式
参数1 request 存储的是请求的相关信息
参数2 response 设定的是响应的相关信息
response.send() 设定响应的内容
*/
// 设定 get 响应
server.get( '/api/register1' , function(request , response){
// 设定响应内容
response.send( '我是 注册1 响应的内容' );
});
// 设定 post 响应
server.post( '/api/register2' , function(request , response){
// 设定响应内容
response.send( '我是 注册2 响应的内容' );
});
// 设定监听端口
server.listen( 8088 , function(){ console.log('服务器启动成功 监听 8088 端口') });
// 启动服务
node 文件名称.后缀
eg: server.js 启动服务: node server.jsnpm
npm 介绍
1. 配置npm:
配置下载地址 修改成 淘宝镜像
npm config set registry https://registry.npm.taobao.org
下载配置 cnpm
npm install -g cnpm --registry=https://registry.npm.taobao.org
下载 安装 cnpm全局依赖包
配置下载地址是 淘宝镜像2. 使用 npm / cnpm 下载:
2-1 初始化 项目 文件夹
切换目录到 指定的项目文件夹
项目文件夹 不能带有 中文,空格,特殊符号等
npm init -y
自动初始化文件夹
自动生成 package.json 文件
会自动存储记录 项目下载的 依赖包
{
"name": "gy_2201",
项目名称 默认使用 文件夹名称
"version": "1.0.0",
项目版本号
"description": "",
项目描述
"main": "banner.js",
入口文件(之后会配置项目的入口文件)
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
测试命令(之后会定义项目的测试命令)
"keywords": [],
项目关键词
"author": "",
开发者名称
"license": "ISC"
项目证书
}
2-2 下载命令
全局依赖包
在 任意路径下安装
安装到 nodejs 指定的文件夹中
C:\Users(用户)\Administrator(用户名)\AppData\Roaming\npm\node_modules\npm
在任意路径下 所有的项目文件都可以调用使用
npm install --global 包名
默认安装最新版本
npm install --global 包名@版本号
安装指定版本
npm i -g 包名
npm i -g 包名@版本号
项目依赖包
项目依赖
开发时 测试时 上线时... 都会一直使用的依赖包
npm install 包名
npm install 包名@版本号
npm i 包名
npm i 包名@版本号
开发依赖
只有开发时使用 测试 上线等 都不会在使用的依赖包
npm install --save-dev 包名
npm install --save-dev 包名@版本号
npm i -D 包名
npm i -D 包名@版本号
项目依赖 和 开发依赖 在 package.json 文件中存储方式不同
"dependencies": {
"jquery": "^3.6.1"
},
项目依赖的存储
"devDependencies": {
"bootstrap": "^5.2.2",
"swiper": "^8.4.3"
}
开发依赖的存储
一个依赖包 npm 只会存储一个版本
新下载的版本 不管版本号是多少 一定会覆盖原始版本
2-3 查看依赖包的安装位置和版本号
npm ls 包名 --global
npm ls 包名 -g
查看全局依赖包版本
npm ls 包名
npm ls 包名
查看项目依赖包版本
`-- jquery@3.5.1
有版本号 证明依赖包安装成功
`-- (empty)
没有版本号 证明依赖包安装失败
2-4 查看 依赖包的版本号
npm view 包名 version
查看依赖包最新版本号
npm view 包名 versions
查看依赖包所有版本号
2-5 删除 依赖包
npm uninstall --global 包名
npm un -g 包名
删除全局依赖包
npm uninstall 包名
npm un 包名
删除项目依赖包
2-6 清除缓存
npm在下载依赖包的过程中 会 生成缓存文件
如果 依赖包下载错误或者意外中断
会生成错误的缓存文件
再次下载依赖包 会从 错误的缓存继续下载
这个依赖包的下载就会一直报错
如果 下载发生错误 或者 意外中断
一定要 先清除 npm 缓存 再重新下载依赖包
npm cache clear --force
npm cache clear -f
nodejs 15以前的版本
会有一个专门的缓存文件夹
C:\Users(用户)\Administrator(用户名)\AppData\Roaming\npm\node_modules\npm-cache
可以找到直接删除这个缓存文件夹
2-7 新项目安装依赖包
创建新项目文件夹
复制粘贴之前文件夹的 package.json 文件
执行 安装命令 安装 package.json 文件 中 记录的依赖包
npm install
npm i
安装 记录的所有依赖包
开发依赖 项目依赖
npm install --production
npm i --production
安装 开发依赖
不会安装 项目依赖nodemon依赖包:
第三方依赖包
作用是 nodejs 的 热启动
下载安装全局依赖包
npm i -g nodemon
使用方法
之前使用 node 文件名 执行 外部js文件
现在使用 nodemon 文件名 执行 外部js文件
文件内容更新 nodemon 自动重启文件express依赖包:
下载成项目依赖包:
npm i express
导入express依赖包:
const express = require('express');
创建服务器:
const server = express();
配置静态地址:
根据 服务器设定的 请求地址
配合 server.use() 参数1 参数2 拼接生成新的 fs读取文件的地址
express自动读取文件内容 通过 response.send() 响应给前端
server.use( 参数1 , express.static() )
参数1 一般是服务器定义的 分类关键词
/static 静态资源
/api 数据接口
参数2 文件夹路径
例如 输入的是 规定好的 http://127.0.0.1/:8088/static/pages/index.html
参数1 需要设定为 /static
获取的是 /static 之后的 字符串截取
/pages/index.html
参数2 设定为 ./src
拼接的结果是 参数2设定内容 拼接上 参数1 截取的内容
./src/pages/index.html
这个拼接结果 应该是 你要访问的html文件的路径
数据接口响应:
server.get( 请求地址 , function(request , response){});
只能响应 get请求方式
如果是 post 请求方式 执行会报错
server.post( 请求地址 , function(request , response){});
只能响应 post请求方式
如果是 get 请求方式 执行会报错
回调函数参数1 request
存储请求信息
回调函数参数2 response
设定响应信息
response.send()
设定 响应内容
设定监听端口:
server.listen( 8088 , function(){})路由表
路由表 介绍
作用:
实际项目中 服务器要定义的 路由(数据接口) 非常多
如果 使用普通的语法形式 需要 写非常多的请求路径判断
普通的语法形式
server.get( '地址1' , 回调函数(){})
server.get( '地址2' , 回调函数(){})
server.post( '地址3' , 回调函数(){})
server.post( '地址4' , 回调函数(){})
server.get( '地址5' , 回调函数(){})
server.get( '地址6' , 回调函数(){})
........
执行结果是 server.js 这个程序中要定义的 路由(数据接口) 就非常多
造成这个文件内容就 非常的臃肿
一个请求 需要执行非常多的 请求地址 判断
请求响应的时间就会非常长定义 路由表的语法形式:
例如 前端发送请求 输入的地址是后端定义好的地址
/api/user/login
通过 服务器中的判断程序 执行对应的 程序文件
server.use('/api' , handel );
router.use( '/user' , userHandel );
router.post( '/login' , login );
server.js中 server.use() 判断 如果 请求地址是 /api 开头
触发执行 handel函数程序
handel函数程序是 加载的 index.js 文件 模块化开发语法 导出的程序
const handel = require('./api/index.js');
继续执行 index.js 中 导入的 router 中 设定的程序
router.use( '/user' , userHandel );
router.use( '/cart' , cartHandel );
继续判断 请求的路径地址
当前输入的地址是 /api/user/login
触发执行的是 /user 地址 对应的 userHandler 程序
也就是 通过 const userHandel = require('./user.js');
导入的 user.js 文件 导出的程序
也就是 执行 user.js 中 导入的 router 程序
继续执行 user.js 中 导入 router 中 设定的程序
router.post( '/login' , login );
router.post( '/register' , register );
继续判断 请求的路径地址
当前输入的地址是 /api/user/login
触发执行的是 /login 地址 对应的 login 程序
login程序 是 const { login } = require('./user/login.js');
导入的 login.js 文件中 导出的程序
login.js文件中 定义的 是 登录请求 执行的程序总结:
之前的程序
发送请求地址
/api/user/login
server.js文件中执行
server.post( '/api/user/login' , function(){ 登录程序 })
路由的程序
发送请求地址
/api/user/login
server.js文件中执行
server.use( '/api' , 去执行对应的一级路由表index.js )
index.js文件中
router.use( '/user' , 去执行对应的二级路由表 user.js )
user.js文件中
router.post( '/login' , 去执行对应的路由文件 login.js )
login.js
function login(){ 登录程序 }
server.js 服务器文件中 只需要导入 一级路由表
不用定义所有的 路由(数据接口)程序语法:
当前这个项目的路由表的结构是
总表 ---> 一级表 ---> 二级表 ---> 路由
服务器文件 server.js
server.use( '路径' , 程序... )
前端发送的请求 按照 匹配的 '路径' 触发程序
情况1 参数2程序 是 一个 路由表
handler
按照匹配的路径 触发执行 路由表规范
情况2 参数2程序 是 一个 静态文件加载
express.static('./src')
截取 /static 之后的地址
在这个底之前 拼接 .src
按照拼接完成的地址 加载 静态文件路由表文件:
一级 index.js
二级 user.js goods.js cart.js
创建一个表结构
const router = express.Router();
向这个表结构添加路由规范
一级表 导入的是 二级表
router.use( '路径1' , 二级表1 );
router.use( '路径2' , 二级表2 );
router.use( '路径3' , 二级表3 );
二级表 导入的是 路由
导出这个表结构
module.exports = router ;路由表和中间件图
中间件
中间件 介绍
所谓的中间件就是函数程序
定义在 路由表中的函数程序
通过 中间件程序 触发执行 下一步程序
全局中间件
定义在 服务器文件server.js 中
静态请求 数据请求都会触发
请求中间件
定义在 一级表index.js 中
所有 数据请求都会触发
路由中间件
定义在 二级表cart.js 中
复杂项目中 也可以是 定义在 三级表结构中
某一类请求 都会触发
错误中间件
定义在 全局中间件中
在 其他任意程序中都会触发执行
专门执行验证判断
如果 报错 触发执行 全局中间件中定义好的程序参数解析
使用依赖包 进行 参数解析
使用的 express 自带的依赖包 body-parser
如果 node_modules 中 已经下载这个 依赖包
不需要重复下载
如果 node_modules 中 没有下载这个 依赖包
需要自己手动下载
npm i body-parser
导入这个依赖包
const 变量 = require('body-parser');
使用是固定的语法形式
server.use(body({extended:false}));
解析 post方式中 键值对字符串 参数
server.use(body.json());
解析 post方式中 键值对json字符串 参数
get方式发送的请求
依赖包 会 在 request.query 中 以对象形式存储携带参数
post方式发送的请求
依赖包 会 在 request.body 中 以对象形式存储携带参数
如果没有携带参数 调用结果是 空字符串
body-parser依赖包的执行原理
就是我们自己写的 get / post 参数 在 http模块中 的解析原理 完全相同
将 get请求携带的参数 定义在 request.query 中
将 post请求携带的参数 定义在 request.body 中http 数据解析原理
get 请求方式 只能 以 键值对 字符串形式 在 浏览器地址栏中 携带参数
post 请求方式 在 请求体 中
可以 是 键值对字符串
可以 是 键值对json字符串
可以 是 二进制文本流
.........
多种方式 携带参数
application/json
json字符串 的请求头设定
application/x-www-form-urlencoded
普通键值对字符串 的请求头设定
multipart/form-data
二进制文本流/二进制数据流 的请求头设定
text/xml
文字或者xml文件 的请求头设定
post解析参数 根据参数的不同语法形式 需要执行不同的程序过程
目前只需要掌握 普通键值对字符串 和 json字符串 解析的原理就可以
get 解析参数
解析浏览器地址栏中携带的数据参数
const res = url.parse( request.url , true ).query
url模块中 parse() 方法 进行 深度解析
解析的内容是 request.url 中 存储的浏览器地址栏数据信息
解析结果是一个对象 其中 query属性 以对象形式 存储 get方式携带的参数
post 解析参数
1, 获取请求体中携带的数据参数
{"name":"张三","pwd1":123,"pwd2":123,"nickname":"张三"}
请求体中 json字符串 携带的参数
name=张三&pwd1=123&pwd2=123&nickname=张三
请求体中 普通字符串 携带的参数
这些参数存储在 情头报文的请求体中
需要先解析到 js程序中
let str = '' ;
request.on( 'data' , function( val ){
str += val
})
等于是 给 request 添加 data类型的程序
作用是 解析 request 请求报文中 请求体携带的参数
post方式携带参数 这个参数可能非常大
data方法 对于 post携带的 大参数 不能一次完全都获取到
需要执行多次 才能完整获取整个 携带参数
每次获取的结果都存储在 形参val中
将每次获取到的结果 拼接到 变量str
生成一个完整的 post方式携带的参数
获取的结果 是 buffer格式的数据
request.on( 'end' , function(){})
data事件执行结束 触发执行end事件
end事件 将 获取到 buffer格式的数据 还原为对应的数据格式
最终的解析结果是 以对象形式存储 键值对
如果请求头是 application/json
表示携带的参数 是 json字符串形式
直接使用 JSON.parse() 就可以解析还原为对应的对象
const valObj = JSON.parse( str );
如果请求头是 application/x-www-form-urlencoded
表示携带的参数 是 普通字符串形式
在 结果前拼接 ? 使用 url模块的 parse() 方法 深度解析
解析的结果是一个 对象
其中 query属性 以 对象形式 键值对
const valObj = url.parse( '?' + str , true ).query ;路由表和中间件图
