「写在前面」
本文为 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)
目录
-
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
感谢这段时间奋斗的自己,
让我们这段时间变得有价值。
本文由 mdnice 多平台发布