目录
一、深浅拷贝
(一)浅拷贝
1.浅拷贝的简单使用
拷贝对象:
拷贝数组:
使用两种浅拷贝方法
2.浅拷贝的问题
(二)深拷贝
1.通过递归实现深拷贝
递归函数:
递归实现过程:
其他问题:
2.利用 lodash 实现深拷贝
3.利用 JSON 实现深拷贝
二、异常处理
(一)抛出异常 throw
(二)捕获异常 try catch
(三)debugger
三、处理 this
(一)this 指向
1.普通函数 this 指向
2.箭头函数 this 指向
(二)改变 this
1.call() 了解
2.apply()
3. bind() 重点
四、防抖 debounce
(一)介绍
(二)利用 lodash 函数
(三)手写函数防抖
五、节流 throttle
(一)介绍
(二)利用 lodash 函数
(三)手写函数节流
六、记录视频播放时间
(一)ontimeupdata
(二)ondoadeddata
练习:利用递归函数实现 setTimeout 模拟 setInterval 效果
练习:改变 this 指向 按钮 2s 后再次可用
综合案例:记录视频播放时间 节流
结构相同对象,复制对象重新赋值
不能用直接赋值来拷贝对象:o = obj
这样他俩指向同一个地址一个变了,就都变了
深浅拷贝只针对引用数据类型
适用于单层的对象和数组,不适用于多层嵌套的数组对象拷贝
最外层拷贝数值,内层拷贝地址
Object.assign() / 展开运算符 (...obj) 拷贝对象
Array.prototype.concat() / [...arr]
解决了直接赋值的问题,这样就变成拷贝值(最外层)而不是地址
第一种:...展开运算符
第二种:Object.assign(被赋值的,赋值的)
只拷贝最外层的值,如果有多层的话就变成拷贝地址了
如下只改变 拷贝出来的 o 对象的 family 的 num 属性,结果原本的 obj 中的 num 的值也变了,是因为有复杂多层对象时,里层拷贝的是地址,地址相同变一个都变了
直接拷贝对象,解决浅拷贝的问题,有三种方式实现深拷贝
函数内部调用自己就是递归函数
作用类似循环
容易发生栈溢出错误 必须添加退出条件
调用函数:直接看函数 deepCopy() 里卖两个实参 obj 和 o
函数内部:
for(k in oldobj) 循环递归 原本对象中的元素,因为是对象 k 代表的是对象中的属性名 而且 k 不是具体的属性名 不能用 oldobj.k 的形式 而应该用 [ ]
注意 oldobj[k] 和 newobj[k] 的含义不同 因为一个对象里面有东西,一个没东西 oldobj[k] 是指对象中具体的属性值 而 newobj[k] 指的是属性名
相当于 o.uname = obj.uname ('pink')
在外层时 直接进行赋值就行 newobj[k] = oldobj[k]
在内层时就得进行判断了,因为是数组也得进行递归赋值,所以直接用我们这个现成的函数递归实现就行
判断如果当前 oldobj[k] 属性值 是数组时 就再次调用函数,先初始化新数组 就是类似 上面的 o 得有个空的数组
当再次调用时oldobj[k] 变成了数组 ['足球', ‘篮球’] 此时 for(k in oldobj[k]) 递归中的 k 的意义变了,在数组中递归 k 是数组下标 然后分别遍历 足球 篮球 都不是数组 到 else 中进行赋值
就是newobj[0] = oldobj[0] ('足球')
newobj[1] = oldobj[1] ('篮球')
最后组成数组
递归实现完成
如果有内层还有对象的话
同理上面再加一个 else if 分支即可
但是注意!!!
Object 判断必须在 Array 下面因为数组也算对象 就会把 hobby 误认为是对象
它是一个 js 库 里面有递归实现深拷贝的方法 可以直接下载进行调用
先引入再调用
_cloneDeep
先把对象转换成字符串,然后再把字符串放到新的对象中转换回对象
预估代码中的可能发生的错误,最大程度避免错误导致程序无法继续进行
throw 会使程序中断,然后抛出异常
body>
我们可以通过 try / catch 来捕获浏览器提供的错误信息
try 尝试 把觉得可能发生错误的代码写进去
catch 拦住 是捕获 try 中的错误信息 不是自动中断可以使用 return 中断 catch 里面有参数 用message 就能得到浏览器的错误信息了
finally 最后 不管程序对不对 一定会执行的代码 catch 使用 return 中断了其它的代码 但是不拦截 finally
123
给程序员调试用的,在代码的一行加上 debugger 在浏览器中打开,然后 f12 一刷新 就直接能跳到debugger 的地方
123
谁调用指向谁 如果是严格模式 指向 undefined
下面 this 分别指向 window window window button obj
箭头函数其实不存在 this 箭头函数默认会去上层作用域找 this,一层一层向外找
对象没有 this
事件回调函数使用箭头函数时 this 是全局的 window
使用普通函数就是那个调用者
dom 事件中的回调函数不建议用 this
原型对象 构造函数 也不建议用 箭头函数
原型对象 和 构造函数中的 this 都指向 实例对象 用箭头函数就变成指向 window 就不能再添加属性了
调用函数 ,然后改变this 指向
函数.call(改变后的this指向,实参1,实参2)
参数是数字
和 call 类似 ,但第二个参数必须是数组
函数.apply(改变后的this指向,[必须是数组])
没有 ... 展开运算符之前的求最值写法
函数里面把数组变成数值了,这样就可以利用这个特点求最大最小值了
Math.max 是一种方法可以用 apply
指向为 null 也行
bind() 不会调用函数,但是能改变 this 指向
bind 拷贝了原来的函数 但是 this 向变了
返回值是一个函数
单位时间内,频繁触发事件,只执行最后一次
拿倒计时举例子,点击按钮倒计时十秒 但是还没倒计时结束的时候,又点击一次倒计时,则刚才的倒计时就不再进行了,而是执行最后一次新的倒计时
搜索框输入时,用户最后一次输入完再发送请求,不用输入一个字请求一遍,减少服务器请求压力,减少请求次数
手机号 邮箱验证同理
语法:_.debounce(函数名,间隔时间)
防抖的核心是利用定时器 setTimeout 来实现
1.声明定时器变量
2.鼠标滑动时 先判断是否有定时器,有就先清除之前的定时器
3.没有就开启定时器,存到变量里面
4.定时器里调用要执行的函数
单位时间内,频繁触发事件,只执行一次
还拿倒计时的例子举例 如果点击按钮触发了进行倒计时 10s 的事件 ,然后再反复点击这个按钮,和防抖不一样,节流不会执行新点击的,而是把最开始的倒计时事件执行结束,不执行后面的事件
长使用于高频事件中
鼠标移动 mousemove,界面尺寸缩放 resize,滚动条滚动 scroll 等
语法:_.throttle(函数名,单位时间)
节流的核心和防抖类似,也是利用定时器 setTimeout 来实现
1.声明定时器变量
2.鼠标滑动时 先判断是否有定时器,有就不开启新的定时器
3.没有定时器就开启新的定时器,存到变量里面
4.定时器里调用要执行的函数 定时器里要把定时器清空
在定时器里面是无法用 clearTimeout(timer) 清除定时器的 写法不科学 得用 timer = null 才行
具体请看综合案例
视频音频当前的播放位置发生改变时触发
触发频率很高,需要进行节流操作
当前帧的数据加载 完成且还没有足够多的数据播放视频音频的下一帧触发
就是页面一打开触发
可以利用这个加载时间 把我们存储的视频上一次看到的时间,读取出来并且把视频的当前时间赋值,跳到我们上次看的地方
界面展示:
代码部分:
Document