目录
1.深浅拷贝
1.1 浅拷贝
1.1.1 浅拷贝的认识
1.1.2 浅拷贝的小结
1.2 深拷贝
1.2.1 递归实现深拷贝
1.2.2 js类库lodash/cloneDeep实现深拷贝
1.2.3 JSON.stringify()实现深拷贝
2.异常处理
2.1 throw 抛异常
2.2 try /catch 捕获异常
2.3 debugger
3.处理this
3.1 this指向
3.1.1 普通函数
3.1.2 箭头函数
3.2 改变this
3.2.1 call()
3.2.2 apply()
3.2.3 bind()--重点
4.性能优化
4.1 防抖
4.2 节流
4.3 总结
5.节流的综合案例
1.深浅拷贝
因为对象赋值是地址,因此修改其中一个,另外一个也会受到影响。
1.1 浅拷贝
1.1.1 浅拷贝的认识
- 1. 拷贝对象:Object.assgin() / 展开运算符 {...obj} 拷贝对象
- 2.拷贝数组:Array.prototype.concat() 或者 [...arr]
1.1.2 浅拷贝的小结
- 直接赋值的方法,只要是对象,都会相互影响,因为是直接拷贝对象栈里面的地址
- 浅拷贝如果是一层对象,不相互影响,如果出现多层对象拷贝还会相互影响
- 拷贝对象之后,里面的属性值是简单数据类型直接拷贝值
- 如果属性值是引用数据类型则拷贝的是地址
1.2 深拷贝
- 1. 通过递归实现深拷贝
- 2. lodash/cloneDeep
- 3. 通过JSON.stringify()实现
1.2.1 递归实现深拷贝
- 如果一个函数在内部可以调用其本身,那么这个函数就是递归函数
- 简单理解:函数内部自己调用自己, 这个函数就是递归函数
- 递归函数的作用和循环效果类似
- 由于递归很容易发生“栈溢出”错误(stack overflow),所以必须要加退出条件 return
练习:
- 通过递归函数来实现
- 如果是基本数据类型的键值对,就正常赋值
- 如果是引用类型就需要递归函数,先处理数组后处理对象
1.2.2 js类库lodash/cloneDeep实现深拷贝
访问:https://www.lodashjs.com/
然后下载并引入js文件,才可以使用封装好的cloneDeep()
1.2.3 JSON.stringify()实现深拷贝
怎么做到的?
刚开始obj是一个对象,然后转换为JSON字符串,后面有parse重新转为对象,又因为前面的对象和后面的对象的地址不一样,因此可以实现深拷贝。
2.异常处理
2.1 throw 抛异常
异常处理是指预估代码执行过程中可能发生的错误,然后最大程度的避免错误的发生导致整个程序无法继续运行。
- 1. throw 抛出异常信息,程序也会终止执行
- 2. throw 后面跟的是错误提示信息
- 3. Error 对象配合 throw 使用,能够设置更详细的错误信息(会指出第几行代码出错)
2.2 try /catch 捕获异常
- 1. try...catch 用于捕获错误信息
- 2. 将预估可能发生错误的代码写在 try 代码段中
- 3. 如果 try 代码段中出现错误后,会执行 catch 代码段,并截获到错误信息
- 4. finally 不管是否有错误,都会执行
- 5. catch捕获到错误信息,程序不会停止
2.3 debugger
当代码多的时候,不方便找到对应的片段,就可以加debugger,浏览器控制台就会定位到
该debugger,省去了找代码的时间。
3.处理this
3.1 this指向
- 1. 普通函数this指向
- 2. 箭头函数this指向
3.1.1 普通函数
3.1.2 箭头函数
- 1. 箭头函数会默认帮我们绑定外层 this 的值,所以在箭头函数中 this 的值和外层的 this 是一样的
- 2.箭头函数中的this引用的就是最近作用域中的this
- 3.向外层作用域中,一层一层查找this,直到有this的定义
总结:
- 构造函数
- 原型函数
- dom事件函数
3.2 改变this
3.2.1 call()
作用:使用 call 方法调用函数,同时传入需要被指定的this对象,来实现改变函数的this对象
参数说明:
- thisArg:在 fun 函数运行时指定的 this 值
- arg1,arg2:传递的其他参数
- 返回值就是函数的返回值,因为它就是调用函数
3.2.2 apply()
作用:使用 apply 方法调用函数,同时指定被调用函数中 this 的值
参数说明:
- thisArg:在fun函数运行时指定的 this 值
- argsArray:传递的值,必须包含在数组里面
- 返回值就是函数的返回值,因为它就是调用函数
3.2.3 bind()--重点
参数说明:
- thisArg:在 fun 函数运行时指定的 this 值
- arg1,arg2:传递的其他参数
- 返回由指定的 this 值和初始化参数改造的 原函数拷贝 (拷贝原函数,并且改变原函数的this指向)
4.性能优化
4.1 防抖
- 1.lodash提供的防抖函数来处理
- 2.手写一个防抖函数来处理
①: 写一个demo函数 ,来控制这个操作函数(mouseMove), 500ms之后才去执行这个函数
②: 节流函数传递2个参数, 第一个参数 mouseMove函数,第二个参数 指定时间500ms
③: 鼠标移动事件,里面写的是demo函数
④:定时器有就先清除,后再添加新的定时器
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style>
.box {
width: 500px;
height: 500px;
background-color: #ccc;
color: #fff;
text-align: center;
font-size: 100px;
}
</style>
</head>
<body>
<div class="box"></div>
<script>
/* 手写防抖函数 */
// 获取对象
const box = document.querySelector('.box')
let i = 1
// 写入html文档
function mouseMove() {
box.innerHTML = i++
}
// 鼠标移动事件,因为 demo(mouseMove, 500)返回的是一个回调函数,因此没问题
box.addEventListener('mousemove', demo(mouseMove, 500))
// 核心思路:
// ①: 写一个demo函数 ,来控制这个操作函数(mouseMove), 500ms之后才去执行这个函数
// ②: 节流函数传递2个参数, 第一个参数 mouseMove函数,第二个参数 指定时间500ms
// ③: 鼠标移动事件,里面写的是demo函数
function demo(fun, time) {
let timeOut
return function () {
// 定时器有就先删除,后再添加新的定时器
if (timeOut) {
clearTimeout(timeOut)
}
timeOut = setTimeout(fun, time)
}
}
</script>
</body>
</html>
4.2 节流
- 1.lodash提供的节流函数来处理
- 2.手写一个节流函数来处理
①: 写一个throttle函数 ,来控制这个操作函数(mouseMove), 500ms之后才去执行这个函数
②: 节流函数传递2个参数, 第一个参数 mouseMove函数,第二个参数 指定时间500ms
③: 鼠标移动事件,里面写的是throttle函数
④:如果没有定时器就开启,如果有定时器则不开启新的定时器
// 1.1 定时器里面调用函数
// 1.2 定时器里面清除该定时器
代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style>
.box {
width: 500px;
height: 500px;
background-color: #ccc;
color: #fff;
text-align: center;
font-size: 100px;
}
</style>
</head>
<body>
<div class="box"></div>
<script>
/* 手写节流函数 */
// 获取对象
const box = document.querySelector('.box')
let i = 1
// 写入html文档
function mouseMove() {
box.innerHTML = i++
}
// 鼠标移动事件
box.addEventListener('mousemove', throttle(mouseMove, 500))
// 核心思路:
// ①: 写一个throttle函数 ,来控制这个操作函数(mouseMove), 500ms之后才去执行这个函数
// ②: 节流函数传递2个参数, 第一个参数 mouseMove函数,第二个参数 指定时间500ms
// ③: 鼠标移动事件,里面写的是throttle函数
function throttle(fun, time) {
let timeOut = null
return function () {
// 1.如果没有定时器就开启,如果有定时器则不开启新的定时器
// 1.1 定时器里面调用函数
// 1.2 清除定时器
if (!timeOut) {
timeOut = setTimeout(function () {
// 调用要执行的函数
fun()
// 开启定时器里面清理定时器
// 设置null的原因是因为开启定时器里面直接clearTimeout会出现异常情况,因此直接把变量赋值为null
// 异常情况就是开启定时器是timeOut = 1,在里面调用了clearTimeout的timeOut还是为1
timeOut = null
}, time)
}
}
}
</script>
</body>
</html>
注意:setTimeout中无法删除定时器,因为定时器还在运作,因此用timeOut = null,而不是
clearTimeout(timeOut)
4.3 总结
- 节流: 就是指连续触发事件但是在 n 秒中只执行一次函数,比如 可以利用节流实现 1s之内 只能触发一次鼠标移动事件
- 防抖:如果在 n 秒内又触发了事件,则会重新计算函数执行时间
- 节流: 鼠标移动,页面尺寸发生变化,滚动条滚动等开销比较 大的情况下
- 防抖: 搜索框输入,设定每次输入完毕n秒后发送请求,如果期 间还有输入,则从新计算时间
5.节流的综合案例
页面打开,可以记录上一次的视频播放位置
- ①:ontimeupdate 事件在视频/音频(audio/video)当前的播放位置发送改变时触发
- ②:onloadeddata 事件,每次打开页面,加载当前帧的数据
- ③:利用本地存储视频播放时间,下次打开页面就可以重本地存储获取该时间来实现
- 1. 在ontimeupdate事件触发的时候,每隔1秒钟,就记录当前时间到本地存储
- 2. 下次打开页面, onloadeddata 事件触发,就可以从本地存储取出时间,让视频从取出的时间播放,如果没有就默认为0s
- 3. 获得当前时间 video.currentTime
<body>
<div class="container">
<div class="header">
<a href="http://pip.itcast.cn">
<img src="https://pip.itcast.cn/img/logo_v3.29b9ba72.png" alt="" />
</a>
</div>
<div class="video">
<video src="https://v.itheima.net/LapADhV6.mp4" controls></video>
</div>
<div class="elevator">
<a href="javascript:;" data-ref="video">视频介绍</a>
<a href="javascript:;" data-ref="intro">课程简介</a>
<a href="javascript:;" data-ref="outline">评论列表</a>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.min.js"></script>
<script>
// 获取对象
const video = document.querySelector('video')
// 播放视频就会触发(利用节流让它一秒只触发一次)
video.ontimeupdate = _.throttle(function () {
// console.log(11)
localStorage.setItem('currentTime', video.currentTime)
}, 1000)
// 下次打开页面,视频的进度停留在 video.currentTime
video.onloadeddata = function () {
video.currentTime = localStorage.getItem('currentTime') || 0
}
</script>
</body>