JavaScript学习
三.JS进阶
9.解构赋值
(2)对象解构
基本语法:
- 赋值运算符 = 左侧的{}用于批量声明变量,右侧数组的单元值将被赋值给左侧的变量
- 对象属性的值将被赋值给与属性名相同的变量
- 注意解构的变量名不要和外面的变量名冲突否则报错
- 对象中招不到与变量名一致的属性时变量值为undefined
如:const { uname, age } = { uname: ‘name’, age: 18}
可以从一个对象中提取变量并同时修改新的变量名
如:const { uname: name, age } = { uname: ‘name’, age: 18},此时name=‘name’
(3)数组对象解构
基本语法:const [{uname, age}] = [{uname: ‘name’, age: 18}]
同样可以改名
(4)多级对象解构
const { name, family: { mom, dad, son } } = { name: ‘name’, { mom: ‘mom’, dad: ‘dad’, son: ‘son’ } }
10.forEach遍历数组
基本语法:arr.forEach(funtion(item, index){ 代码 })
注意:
- forEach主要是遍历数组
- item是必写的,index可选
可以写成箭头函数形式:arr.forEach(item =>{代码})
11.筛选数组filter方法
作用:筛选数组符合条件的元素,并返回筛选之后元素的新数组
语法如:array.filter(funtion(item, index){return item >= 20}, thisValue)
同样可以写成箭头函数:arr.filter(item => item >= 20)
注意:
- 返回数组
- item必写
- index、thisValue可选
- thisValue作为该执行回调时使用,传递给函数,用作 “this” 的值。如果省略了 thisValue ,“this” 的值为 “undefined”
12.创建对象
- 利用对象字面量来创建:const obj = {…}
- 用new Object创建一个空对象:const obj = new Object({…})
- 利用构造函数创建对象
构造函数
构造函数是一种特殊的函数,主要用来初始化对象
构造函数有两个约定:
- 他们的命名以大写字母开头
- 他们只能由new操作符来执行
代码例:
说明:
- 使用new关键字调用函数的行为称为实例化
- 实例化构造函数时没有参数可以省略()
- 构造函数内部无需写return,返回值即是新创建的对象
- 构造函数内部的return的返回值无效
- new Object(),new Date()也是实例化构造函数
13.实例化执行过程
说明:
- 创建新空对象
- 构造函数this指向新对象
- 执行构造函数代码,修改this,添加新的属性
- 返回新函数
14.实例成员和静态成员
实例成员:实例对象中的属性和方法称为实例成员(实例属性和实例方法)
说明:
- 为构造函数传入参数,创建结构相同但值不同的对象
- 构造函数创建的实例对象彼此独立互不影响
静态成员:构造函数的属性和方法被称为静态成员(静态属性和静态方法)
说明:
- 静态成员只能构造函数来访问
- 静态方法中的this指向构造函数
比如:Date.now(),Math.PI
15.内置构造函数
基本类型的底层代码如:const str = new String(‘str’)
字符串、数字、布尔等基本类型都有专门的构造函数,这些我们称为包装类型
JS中几乎所有的数据都可以基于构成函数创建
引用类型:Object、Array、RegExp、Date等
包装类型:String、Number、Boolean等
(1)Object
三个静态方法(静态方法就是只有Object可以调用的)
Object.keys(对象名)作用:获取对象所有属性名,返回数组
Object.Values(对象名)作用:获取对象所有属性值,返回数组
Object.assign(拷贝的, 被拷贝)作用:静态方法常用于拷贝对象,使用场景:给对象添加属性
(2)Array
数组常见实例方法:
方法 | 说明 |
---|---|
forEach | 不返回数组,常用于查找遍历数组元素 |
filter | 返回新数组,返回的是筛选满足条件的数组元素 |
map | 返回新数组,返回的是处理之后的数组元素 |
reduce | 返回累计处理的结果,常用于求和等 |
reduce语法:arr.reduce(function(上一次值, 当前值){}, 起始值)
求和:
const total = arr.reduce((pre, current) => prev + current, 起始值)
reduce执行过程:
- 如果没有起始值,则上一次值为数组的第一个数组元素的值
- 每一次循环,把返回值给做为下一次循环的上一次值
- 如果有起始值,则起始值为上一次值
如果对象的第一个值为字符串,我们可以设初始值为0
其它常见方法:
部分图解:
join语法:arr.join(separator),separator是指定分隔符
find语法:arr.find(function(item){})
返回数组中第一个满足条件的值
箭头函数写法:const i = arr.find(item => item.name === ‘i’)
every语法:arr.every(funtion(item, index){}, thisArg)
箭头函数写法:arr.every(item => item > 10 )
Array.form(伪数组名)作用:将伪数组转换为真数组(伪数组不能用常见数组方法)
(3)String
字符串常见实例方法:
split语法:str.split(‘符号’)
注意:substr方法不建议使用
substring语法:str.substring(strat, end),end可选
startWith语法:str.startWith(要搜索的字符串, 搜索开始的位置),默认从0开始
includes语法(Array也有includes方法):str.includes(要搜索的字符串, 搜索开始的位置),默认从0开始
注意:includes区分大小写
(4)Number
常用的只有一个方法
toFixed的方法语法:number.toFixed(num),指定number保留小数的位数
注意:四舍五入
常用
callback:回调函数
注意小数的计算精度问题:0.1 + 0.2 = 0.300000000004
解决方案:(0.1*100 + 0.2*100)/100 === 0.3
String(num)和num.toString()都可以将数字型转换为字符串
16.原型
构造函数存在因地址不同导致的内存浪费问题
构造函数是通过原型分配的函数是所有对象共享的
JavaScript规定,每一个构造函数都有一个prototype属性,指向另一个对象,所以我们也称为原型对象
这对象可以挂载函数,对象实例化不会多次创建原型上函数,节约内存
我们可以把那些不变的方法,直接定义在prototype对象上,这样所有对象的实例就可以共享这些方法
构造函数和原型对象中的this都指向实例化的对象
代码实例:
也可以给数组扩展方法:
17.constructor属性
每个原型都有一个constructor属性
作用:该属性指向该原型对象的构造函数
图解:
注意:给prototype直接赋值之后,要重新给constructor赋值
17.对象原型
对象都会有一个属性__proto__指向构造函数的prototype原型对象,之所以我们对象可以使用构造函数prototype原型对象的属性和方法,就是因为有__proto__的存在
图解:
注意:
- __proto__的__是两条线
- __proto__是JS非标准属性
- [[prototype]]和__proto__意义相同
- 用来表明当前实例对象指向哪个原型对象prototype
- __proto__对象原型里也有一个constructor属性,指向构造函数
18.原型继承
可以通过原型继承对象实例,别忘了给constructor重新赋值:
function Person() {
this.name = 'uname'
}
Man.prototype = new Person()
Man.prototype.constructor = Man
19.原型链
基于原型对象的继承使得不同构造函数的原型对象关联在一起,并且这种关联的关系是一种链状的结构,我们将原型对象的链状结构关系称为原型链
Object是内置构造函数,同样有原型对象
只要是对象就有__proto__,指向原型对象,只要是原型对象就有constructor,指向构造函数
其中,ldh对象实例的__proto__指向Star原型对象,Star原型对象prototype的__proto__指向Object原型对象,Object原型对象prototype的__proto__指向null的这种关系就叫原型链
原型链-查找规则
原型链就是一个查找规则
①当访问一个对象的属性(包括方法)时,首先查找这个对象自身有没有该属性
②如果没有就查找它的原型(也就是__proto__指向的prototype原型对象)
③如果还没有就查找原型对象的原型(Object的原型对象)
④以此类推一直找到Object为止(null)
⑤__proto__对象原型的意义就在于为对象成员查找机制提供一个方向,或者说一条路线
⑥可以使用instanceof运算符用于检测构造函数的prototype属性是否出现在某个实例对象的原型链上,语法:o instanceof b
常用
在构造函数中创建需要加this
构造函数封装-模态框:
样式:
20.浅拷贝
首先浅拷贝和深拷贝只针对引用类型
浅拷贝:拷贝的是地址
常用方法:
- 拷贝对象:Object.assign()或展开运算符{…obj}拷贝对象
- 拷贝数组:Array.prototype.concat()或者[…arr]
问题:如果对象中有对象,则修改可能会影响原对象
21.深拷贝
深拷贝:拷贝的是对象,不是地址
常见方法:
-
通过递归方式实现深拷贝
-
js库lodash库里面cloneDeep内部实现了深拷贝,详见lodash官网
-
用JSON实现深拷贝
const o = JSON.parse(JSON.stringify(obj))
22.异常处理
异常处理是预估代码执行过程中可能出现的异常,然后最大程度的避免错误的发生导致整个程序无法继续运行
(1)throw抛出异常
throw抛出异常信息,程序会终止执行
throw后面跟的是错误提示信息
Error对象配合throw使用,能够设置更详细的错误信息
代码实例:
(2)try/catch捕获异常
可以通过try/catch捕获错误信息(浏览器提供的错误信息),try试试catch拦住finally最后
try…catch用于捕获错误信息
将预估可能会发生错误的代码写在try中
如果try代码段中出现错误后,会执行catch代码段,并截获到错误信息
finally不管是否有错误,都会执行
代码实例:
(3)debugger
手动断点,调试用的
在开发者工具中的Sources可以debug
23.this指向
普通函数的this指向调用者
普通函数没有调用者时this指向window,严格模式(在代码开头写’use strict’)下指向undefined
箭头函数的this与普通函数完全不同,事实上箭头函数中并不存在this
箭头函数会默认绑定外层的this
注意情况:
- DOM事件回调函数里面如果需要DOM对象的this,则不推荐使用箭头函数
- 基于原型的面向对象也不推荐使用箭头函数
24.改变this
JavaScript中有3个方法可以动态指定普通函数中的this
(1)call-了解
使用call方法调用函数,同时指定被调用函数中this的值
语法:fun.call(thisArg, arg1, arg2, …)
thisArg:在fun执行时指定的this值
arg1, arg2:执行函数时要传递的参数
代码实例:
(2)apply-有点重要
使用apply方法调用函数,同时指定被调用函数中this的值
语法:fun.apply(thisArg, [argsArray])
thisArg:在fun执行时指定的this值
argsArray: 传递的值,必须包含在数组里面
返回值就是函数的返回值
代码实例:
(3)bind()-重点
bind()方法不会调用函数,但能改变函数的this的值
语法:fun.bind(thisArg, arg1, arg2, …)
thisArg:指定的this值
arg1, arg2:执行函数时要传递的参数
返回由指定的this值和初始化参数改造的原函数拷贝(新函数)
因此当我们只想改变this指向,但不想调用这个函数时,可以使用bind,比如改变定时器内部的this指向
代码实例:
改变定时器的this指向:
25.防抖-debounce
防抖:单位时间内,频繁触发事件,只执行最后一次
lodash里面有一个防抖函数
封装一个防抖函数:
26.节流-throttle
节流:单位时间内,频繁触发事件,只执行一次
lodash里面有一个节流函数
封装一个节流函数:
注意:在setTimeout中是无法清除定时器的
常用
两个事件:
①ontimeupdate事件在视频/音频当前的播放位置发送改变时触发
②onloadeddata事件在当前帧的数据加载完成且还没有足够的数据播放视频/音频的下一帧时触发
vue学习
四.开发须知
6.混入
混入(mixins):是一种分发vue组件中可复用功能的非常灵活的方式。混入对象可以包含任意组件选项。当组件使用混入对象时,所有混入对象的选项将被混入该组件本身的选项。
使用方法:在根目录创建一个mixins文件夹,里面放要混入的js文件,然后在要混入的vue文件中导入并写mixins: [混入文件名]
示例代码js文件部分:
示例代码vue文件部分:
注意:
- 如果混入文件和组件内,提供了同名的data或methods,则组件内的优先级更高
- 如果编写了声明周期函数,则mixins中的生命周期函数和组件的生命周期函数,会用数组管理统一执行
7.打包
说明:vue脚手架只是开发过程中,协助开发的工具,当真正开发完了,脚手架不参与上线
打包的作用:
- 将多个文件压缩合并成一个文件
- 语法降级
- less、sass、ts语法解析
打包后,可以生成浏览器能够直接运行的网页
说明:vue脚手架工具已经提供了打包命令,直接使用即可
命令:npm build
结果:在项目的根目录会自动创建一个文件夹’dist’,dist中的文件就是打包后的文件,只需要放到服务器中即可
配置:默认情况下,需要放到服务器根目录打开,如果希望双击运行,则需要配置publicPath配成相对路径
8.异步导入
说明:在打包构建应用时,JavaScript包会变得非常大,影响页面加载。如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,这样就更高效了
步骤1:异步组件改造,将原先的导入换成以下代码
步骤2:路由中应用
常用
actions在带命名空间的模块内访问全局内容:dispatch(‘mutation名’, [], root: true)
代码实例:
五.vue3初识
1.使用create-vue创建项目
create-vue是Vue官方新的脚手架工具,底层切换到了vite,为开发提供极速响应
前提环境条件:node.js版本在16.0以上(命令:node -v)
创建一个vue应用:npm init vue@latest,这一指令将会安装并执行create-vue
2.项目目录和关键文件
关键文件:
vite.config.js中有vue3官网地址
注意:vuter插件是vue2的,vue3对应的是volar
main.js中的mount是设置挂载点,#app(id为app的盒子,在index.html中)
template不再要求唯一根元素
对创建实例进行封装:createApp()、createRouter,保证每个实例的独立封闭性
六.组合式API
3.setup选项
setup选项的写法和执行时机:
setup函数中获取不到this
数据和函数需要再setup函数中return才能到模板中应用,很麻烦
语法糖写法:
语法糖原理(就是return):
4.reactive和ref函数
reactive()作用:接受对象类型的数据,并返回一个响应式的对象
语法:
步骤:
- 从vue包中导入reactive函数
- 在<script setup>中执行reactive函数并传入类型为对象的初始值,并使用变量接收返回值
ref()作用:接收简单类型或者对象类型的数据并返回一个响应式的数据
ref()本质:在原有传入数据的基础上(不管是简单还是复杂数据类型),包了一层对象,再借助reactive实现的响应式
语法:
注意:
- 访问数据需要通过.value
- 在template中,.value不需要加
推荐:声明数据统一用ref()
5.computed
计算属性基本思想和vue2的完全一致,组合式API的计算属性只是修改了写法
语法:
注意:
- 计算属性中不应该有异步请求、修改dom等操作
- 避免直接修改计算属性的值
6.watch函数
作用:侦听一个或多个数据的变化
监听一个数据的语法:
监听多个数据的语法:
[newCount, newValue]可以写成newArr
(1)immediate配置项
说明:在侦听器创建时,立刻触发回调
语法:
(2)deep配置项
说明:深度监听,默认是false,浅层监听无法监听复杂类型内部数据的变化
语法:
(3)精确监听某个数据
语法:
7.生命周期函数
注意:vue3中也可以写vue2的生命周期函数
vue3中的生命周期函数:
示例:
注意:生命周期函数可以调用多次,按照顺序依次执行
8.父子通信
(1)父传子
- 父组件中给子组件绑定属性
- 子组件通过props选项接收
代码实例:
注意:
- 由于写了setup,所以无法直接配置props选项,所以此处需要借助于编译器宏函数接收父组件传递的数据
- 对于props传递过来的数据,模板中不用写props.
编译器宏defineProps原理:就是编译阶段的一个标识,实际编译器解析时,遇到后会进行编译转换
(2)子传父
- 父组件中给子组件标签通过@绑定事件
- 子组件内部通过emit方法触发事件
代码实例:
9.模板引用
概念:通过ref标识获取真实的dom对象或者组件实例对象
步骤:
- 调用ref函数生成一个ref对象
- 通过ref标识绑定ref对象
注意:获取模板引用的时机是组件挂载完毕的时候,即onMounted
代码实例:
注意:默认情况下在<script setup>语法糖下组件内部的属性和方法是不开放给父组件访问的,可以通过defineExpose编译宏指定哪些属性和方法允许访问
代码示例:
10.provide和inject
作用:顶层组件向任意的底层组价传递数据和方法,实现跨层组件通信
(1)传递普通数据
- 顶层组件通过provide函数提供数据
- 底层组件通过inject函数获取数据
(2)传响应式数据
在调用provide函数时,第二个参数设置为ref对象
(3)传方法
顶层组件可以向底层组件传递方法,底层组件调用方法修改顶层组件中的数据
11.defineOptions
背景:在有<script setup>之前,如果要定义props、emits可以轻而易举地添加一个与setup平级的属性,但是用了<script setup>后,就没法这么干了setup属性已经没有了,自然无法添加预期平级的属性
在vue3.3中新引入了defineOptions宏。主要是用来定义Options API的选项。可以用defineOptions定义任意的选项,props、emits、expose、slots除外
代码实例:
12.defineModel
在vue3中,自定义组件上使用v-model,相当于传递modelValue属性,同时触发update:modelValue事件,我们需要先定义props,再定义emits。其中有许多重复的代码。如果需要修改此值,还需要手动调用emit函数,非常麻烦
从 Vue 3.4 开始,推荐的实现方式是使用 defineModel() 宏
代码示例:
defineModel() 返回的值是一个 ref。作用:与父组件的数据双向绑定
七.Pinia
1.初识
pinia是vuex的替代品(去掉了mutation和modules)
2.手动安装Pinia
- 使用vite创建一个空的vue3:npm create vue@latest
- 安装官方文档安装pinia到项目中:npm i pinia,再导入和实例化
详见官网
3.基本使用
创建仓库:
在vue文件中使用:
详见官网
记得数据和方法要暴露出去,不要直接解构传过去的数据
4.异步写法
编写方式:异步action函数的写法和组件中获取异步数据的写法完全一致
代码实例:
5.storeToRefs方法
问题:直接解构传过去的数据,数据会丢失响应式
解决方法:
6.持久化
作用:一旦有数据传输,自动把pinia文件里的数据存到本地
pinia官网:https://pinia.vuejs.org/zh/
步骤:
八.开发须知
1.pnpm包管理器
优点:快、节省磁盘空间
官网:https://www.pnpm.cn/
安装:npm i -g pnpm
创建项目:pnpm create vue
安装依赖:pnpm install
注意:pnpm创建项目时不应该往根目录去建
2.ESlint配置代码风格
配置文件.eslint.cjs
- prettier风格配置
- vue组件名称多单词组成(忽略index.vue)
- props解构(关闭)
给prettier配置规则:
给ESlint配置规则:
前置条件:
- vscode上不要装prettier插件,装上ESlint插件
- 设置里的打开设置(json)设置"source.fixAll": always,“editor.formatOnSave”: false(实现自动格式化修复和关闭自动格式化
3.提交前做代码检查
husky是一个git hooks工具(git的钩子工具,可以在特定的时机执行特定的命令)
husky配置:
- 初始化git仓库,执行git init即可
- 初始化husky工具配置,执行pnpm dlx husky-init && pnpm install即可
- 修改.husky/pre-commit文件
问题:pnpm lint是全量检查,费时且可能会有历史问题
暂存区eslint校验
-
安装lint-staged包pnpm i lint-staged -D
-
配置package.json
-
修改.husky/pre-commit文件
AJAX学习
一.初识
常用
Bootstrap的Modal提供了一个弹窗的模板
元素.show()使元素显示,元素.hide()可使元素隐藏
10.图片上传
①获取图片文件对象
②使用FormData携带图片文件
③提交到服务器,获取图片url网址使用
二.XMLHttpRequest
1.基本使用
XMLHttpRequest(XMR)对象用于与服务器交互。通过XMLHttpRequest可以在不刷新页面的情况下请求特定URL,获取数据。这允许网页在不影响用户操作的情况下,更新页面的局部内容。XMLHttpRequest在AJAX编程中被大量使用
关系:axios内部采用XMLHttpRequest与服务器交互
使用方法:
- 创建XMLHttpRequest对象
- 配置请求方法和URL地址
- 监听loadend事件,接收响应结果
- 发起请求
代码示例:
2.查询参数
定义:浏览器提供给服务器的额外信息,让服务器返回浏览器想要的数据
语法:http://xxx.com/xxx/xxx?参数名1=值1&参数名2=值2
代码实例:
快速将对象转换成查询参数的格式的方法:
注意:传递的url网址遇中文会转换成URL编码,paramsObj.toString()方法也会转化
3.数据提交
setRequestHeader方法第一个参数是请求头的属性名,第二个参数是请求体数据类型
代码实例:
三.promise
1.认识promise
promise对象用于管理一个异步操作的最终完成(或失败)及其结果值
代码示例:
2.promise的三种状态
- 待定(pending):初始状态,既没有被兑现,也没有被拒绝
- 已兑现(fulfilled):意味着操作成功完成
- 已拒绝(rejected):意味着操作失败
作用:状态改变后,调用关联的处理函数
代码示例:
注意:promise的状态一旦被接受或拒绝,就无法改变
使用promise和XHR获取列表:
注意:错误对象要用console.dir详细打印
3.同步代码和异步代码
同步代码:逐行执行,需原地等待结果后,才会继续向下执行
异步代码:调用后耗时,不阻塞代码继续执行,在将来完成后触发一个回调函数
异步代码例子:setTimeout/setInterval,事件,AJAX
4.回调函数地狱
概念:在回调函数中嵌套回调函数,一直嵌套回调函数就形成了回调函数地狱
缺点:可读性差,异常无法捕获,耦合性差
5.链式调用
作用:解决回调函数地狱
概念:依靠then()方法会返回一个新生成的promise对象特性,继续串联下一环任务,直到结束
思路:
代码实例:
6.async函数和await
定义:async函数是使用async关键字声明的函数。async函数时AsyncFunction构造函数的实例,并且其中允许使用await关键字。async和await关键字让我们可以用一种更简洁的方式写出基于promise的异步行为
代码实例:
捕获错误
使用try/catch语句标记要尝试的代码块
代码实例:
7.事件循环(EventLoop)
概念:JavaScript有一个基于事件循环的并发模型,事件循环负责执行代码、收集和处理事件以及执行队列中的子任务
原因:JavaScript单线程(某一时刻只能执行一行代码),为了让耗时代码不阻塞其它代码运行,设计了事件循环模型
事件循环就是先执行调用栈里的不耗时的代码,再执行在任务队列里的耗时代码
8.宏任务与微任务
ES6之后引入了promise对象,让JS引擎也可以发起异步任务
异步任务分为:
- 宏任务:有浏览器环境执行的异步代码
- 微任务:由JS引擎环境执行的异步代码
任务 | 执行所在环境 |
---|---|
JS脚本执行事件(Script) | 浏览器 |
setTimeout/setInterval | 浏览器 |
AJAX请求完成事件 | 浏览器 |
用户交互事件 | 浏览器 |
promise对象.then() | JS引擎 |
promise本身是同步的,而then和catch回调函数时异步的
执行顺序图解:
经典面试题:
答案:1756234
解析:先打印17,再执行微任务,打印56,再执行位置靠上的宏任务,打印2,再执行宏任务里的微任务,打印3,在执行位置靠下的宏任务,打印4
9.promise.all静态方法
作用:将多个promise对象合并,等待所有成功完成(或某一个失败),做后续逻辑
图解:
语法:
注意:结果数组和合并时顺序
本周demo
生病了写不了demo