写在前面
本文为 b 站黑马程序员 pink 老师 JavaScript 教程的学习笔记。本着自己学习、分享他人的态度,分享学习笔记,希望能对大家有所帮助。推荐先按顺序阅读往期内容:\
1. JavaScript 学习笔记(Day1) \
2. JavaScript 学习笔记(Day2) \
3. JavaScript 学习笔记(Day3) \
4. JavaScript 学习笔记(Day4) \
5. JavaScript 学习笔记(Day5) \
6. JavaScript 学习笔记(WEB APIs Day1) \
7. JavaScript 学习笔记(WEB APIs Day2) \
8. JavaScript 学习笔记(WEB APIs Day3) \
9. JavaScript 学习笔记(WEB APIs Day4) \
10. JavaScript 学习笔记(WEB APIs Day5) \
11. JavaScript 学习笔记(WEB APIs Day6) \
12. JavaScript 学习笔记(JS进阶 Day1) \
13. JavaScript 学习笔记(JS进阶 Day2) \
14. JavaScript 学习笔记(JS进阶 Day3)
::: block-1
目录
- 1 深浅拷贝
- 2 异常处理
- 3 处理this
- 4 性能优化
- 5 综合案例
- 6 完结撒花
:::
1 深浅拷贝
1.1 浅拷贝
P188:https://www.bilibili.com/video/BV1Y84y1L7Nn?p=188
首先浅拷贝和深拷贝只针对引用类型
浅拷贝:拷贝的是地址
常见方法:
- 拷贝对象:Object.assgin() / 展开运算符 {...obj} 拷贝对象
- 拷贝数组:Array.prototype.concat() 或者 [...arr]
const obj = {
uname: 'pink'
}
const o = { ...obj }
console.log(o) // {uname: 'pink'}
o.uname = 'red'
console.log(o) // {uname : 'red'}
console.log(obj) // {uname: 'pink' }
如果是简单数据类型拷贝值,引用数据类型拷贝的是地址 (简单理解: 如果是单层对象,没问题,如果有多层就有问题)
// 一个pink对象
const pink = {
name: 'pink老师',
age: 18,
famliy: {
mother: 'pink妈妈'
}
}
const red = {}
object.assign(red, pink)
console.log(red) // {name: 'pink老师', age: 18}
red.name = 'red老师'
// 更改对象里面的 family 还是会有影响
red.famliy.mother = 'red妈妈'
consoie.log(red) // {name: 'red老师', age: 18}
// 不会影响pink对象
console.log(pink) // {name: 'pink老师', age: 18}
1.2 深拷贝
P189:https://www.bilibili.com/video/BV1Y84y1L7Nn?p=189
首先浅拷贝和深拷贝只针对引用类型
深拷贝:拷贝的是对象,不是地址
常见方法:
- 通过递归实现深拷贝
- lodash/cloneDeep
- 通过JSON.stringify()实现
1. 通过递归实现深拷贝
函数递归:
如果一个函数在内部可以调用其本身,那么这个函数就是递归函数
- 简单理解:函数内部自己调用自己, 这个函数就是递归函数
- 递归函数的作用和循环效果类似
- 由于递归很容易发生“栈溢出”错误(stack overflow),所以必须要加退出条件 return
let num = 1
// fn就是递归函数
function fn() {
console.log('我要打印6次')
if (num >= 6) {
return
}
num++
fn() // 函数内部调用函数自己
}
fn()
P190:https://www.bilibili.com/video/BV1Y84y1L7Nn?p=190
通过递归函数实现深拷贝:
const o = {}
function deepCopy(newObj, oldObj) {
for (let k in old0bj) {
if (oldObj[k] instanceof Array) {
new0bj[k] = []
deepCopy(newObj[k], oldObj[k])
} else if (oldObj[k] instanceof Object) {
newObj[k]= {}
deepCopy(newObj[k], oldObj[k])
}
else {
newObj[k] = old0bj[k]}
}
}
}
2. js库lodash里面cloneDeep内部实现了深拷贝
P191:https://www.bilibili.com/video/BV1Y84y1L7Nn?p=191
const obj = {
uname: 'pink',
age: 18,
hobby: ['篮球', '足球'],
family: {
baby: '小pink'
}
}
// 语法: _.cLoneDeep(要被克隆的对象)
const o = _.cloneDeep(obj)
console.log(o)
o.family.baby = '老pink'
console.log(obj)
3. 通过JSON.stringify()实现
const obj = {
uname: 'pink',
age: 18,
hobby:['篮球', '足球'],
family: {
baby: '小pink'
}
}
const o = JSON.parse(JSON.stringify(obj))
console.log(o)
o.family.baby = '老pink'
console.log(obj)
2 异常处理
2.1 throw 抛异常
P192:https://www.bilibili.com/video/BV1Y84y1L7Nn?p=192
异常处理是指预估代码执行过程中可能发生的错误,然后最大程度的避免错误的发生导致整个程序无法继续运行
- throw 抛出异常信息,程序也会终止执行
- throw 后面跟的是错误提示信息
- Error 对象配合 throw 使用,能够设置更详细的错误信息
function counter(x, y) {
if (!x || !y) {
throw new Error('参数不能为空!”
}
return X + y
}
counter()
2.2 try /catch 捕获异常
我们可以通过try / catch 捕获错误信息(浏览器提供的错误信息) try 试试 catch 拦住 finally 最后
- try...catch 用于捕获错误信息
- 将预估可能发生错误的代码写在 try 代码段中
- 如果 try 代码段中出现错误后,会执行 catch 代码段,并截获到错误信息
- finally 不管是否有错误,都会执行
<p>123</p>
<script>
function fn() {
try {
// 可能发送错误的代码要写到 try
const p = document.querySelector('.p')
p.style.color = 'red'
} catch (err) {
// 拦截错误,提示浏览器提供的错误信息,但是不中断程序的执行
console.log(err.message)
throw new Error('你看看,选择器错误了吧')
// 需要加return中断程序
// return
}
finally {
// 不管你程序对不对,一定会执行的代码
alert('弹出对话框')
}
console.log(11)
}
fn()
</script>
2.3 debugger
debugger 相当于打断点
3 处理this
3.1 this指向
P193:https://www.bilibili.com/video/BV1Y84y1L7Nn?p=193
普通函数的调用方式决定了 this 的值,即【谁调用 this 的值指向谁】
普通函数没有明确调用者时 this 值为 window,严格模式下没有调用者时 this 的值为 undefined
箭头函数中的 this 与普通函数完全不同,也不受调用方式的影响,事实上箭头函数中并不存在 this!
- 箭头函数会默认帮我们绑定外层 this 的值,所以在箭头函数中 this 的值和外层的 this 是一样的
- 箭头函数中的this引用的就是最近作用域中的this
- 向外层作用域中,一层一层查找this,直到有this的定义
注意情况1:
在开发中【使用箭头函数前需要考虑函数中 this 的值】,事件回调函数使用箭头函数时,this 为全局的 window
因此DOM事件回调函数如果里面需要DOM对象的this,则不推荐使用箭头函数
注意情况2:
同样由于箭头函数 this 的原因,基于原型的面向对象也不推荐采用箭头函数
3.2 改变this
P194:https://www.bilibili.com/video/BV1Y84y1L7Nn?p=194
1. call() –了解
使用 call 方法调用函数,同时指定被调用函数中 this 的值
语法:fun.call(thisArg, arg1, arg2, ...)
- thisArg:在 fun 函数运行时指定的 this 值
- arg1,arg2:传递的其他参数
- 返回值就是函数的返回值,因为它就是调用函数
const obj = {
uname: 'pink'
}
function fn(x, y) {
console.log(this) // windowconsole.log(x +y)
}
// 1.调用函数
// 2. 改变this指向
fn.call(obj, 1, 2)
2. apply()-理解
P195:https://www.bilibili.com/video/BV1Y84y1L7Nn?p=195
使用 apply 方法调用函数,同时指定被调用函数中 this 的值
语法:fun.apply(thisArg, [argsArray])
- thisArg:在fun函数运行时指定的 this 值
- argsArray:传递的值,必须包含在数组里面
- 返回值就是函数的返回值,因为它就是调用函数
- 因此 apply 主要跟数组有关系,比如使用 Math.max() 求数组的最大值
// 求和函数
function counter(x, y) {
return x + y
}
// 调用 counter 函数,并传入参数
let result = counter.apply(null, [5, 10])
console.log(result)
3. bind()-重点
P196:https://www.bilibili.com/video/BV1Y84y1L7Nn?p=196
bind() 方法不会调用函数。但是能改变函数内部this 指向
语法:fun.bind(thisArg, arg1, arg2, ...)
- thisArg:在 fun 函数运行时指定的 this 值
- arg1,arg2:传递的其他参数
- 返回由指定的 this 值和初始化参数改造的 原函数拷贝 (新函数)
- 因此当我们只是想改变 this 指向,并且不想调用这个函数的时候,可以使用 bind,比如改变定时器内部的this指向.
// 普通函数
function sayHi() {
console.log(this)
}
let user = {
name: '小明',
age: 18
}
// 调用 bind 指定this的值
let sayHello = sayHi.bind(user);
// 调用使用 bind 创建的新函数
sayHello()
相同点:
- 都可以改变函数内部的this指向.
区别点:
- call 和 apply 会调用函数, 并且改变函数内部this指向.
- call 和 apply 传递的参数不一样, call 传递参数 aru1, aru2..形式 apply 必须数组形式[arg]
- bind 不会调用函数, 可以改变函数内部this指向.
主要应用场景:
- call 调用函数并且可以传递参数
- apply 经常跟数组有关系. 比如借助于数学对象实现数组最大值最小值
- bind 不调用函数,但是还想改变this指向. 比如改变定时器内部的this指向.
4 性能优化
4.1 防抖
P197:https://www.bilibili.com/video/BV1Y84y1L7Nn?p=197
防抖: 单位时间内,频繁触发事件,只执行最后一次
使用场景:
- 搜索框搜索输入。只需用户最后一次输入完,再发送请求
- 手机号、邮箱验证输入检测
4.2 防抖
P198:https://www.bilibili.com/video/BV1Y84y1L7Nn?p=198
节流:单位时间内,频繁触发事件,只执行一次
使用场景:
高频事件:鼠标移动mousemove、页面尺寸缩放resize、滚动条滚动scroll等等
5 综合案例
P199:https://www.bilibili.com/video/BV1Y84y1L7Nn?p=199
6 完结撒花
P200:https://www.bilibili.com/video/BV1Y84y1L7Nn?p=200
::: block-2
感谢这段时间奋斗的自己,\
让我们这段时间变得有价值。
:::
<center>结束</center>
本文由mdnice多平台发布
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。