JS-进阶-拷贝异常节流抖动

JS学习笔记16

深浅拷贝(针对引用类型)

浅拷贝

拷贝的是地址

常见方法:

  1. 拷贝对象:Object.assgin() / 展开运算符 {…obj} 拷贝对象
  2. 拷贝数组:Array.prototype.concat() 或者 […arr]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
const obj = {
uname: 'jason',
age: 18,
family: {
baby: '1'
}
}
const o = {}
Object.assign(o, obj)
o.age = 20
o.family.baby = '2'
console.log(o)
console.log(obj)
//这里对o.family进行修改,会导致obj里面也修改,因为对象里面的引用类型浅拷贝拷贝了地址

如果是简单数据类型拷贝值,引用数据类型拷贝的是地址 (简单理解: 如果是单层对象,没问题,如果有多层就有问题)

深拷贝

拷贝的是对象,不是地址

==通过递归实现深拷贝==

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
const obj={
uname: 'jason',
age: 18,
hobby: ['1','2']
}//拷贝对象里有数组对象
const o={}
function deepcopy(newobj,oldobj){
for(let k in oldobj){ //一定要先写数组,因为数组也是对象会执行第二块内容
if(oldobj[k] instanceof Array){
newobj[k]=[]
deepcopy(newobj[k],oldobj[k])
}//处理里面数组对象的拷贝
else if(oldobj[k] instanceof Object){
newobj[k]={}
deepcopy(newobj[k],oldobj[k])
}//处理里面对象的拷贝
else{
newobj[k]=oldobj[k]
}
}
}


Lodash

js库

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<body>
<!-- 先引用 -->
<script src="./lodash.min.js"></script>
<script>
const obj = {
uname: 'pink',
age: 18,
hobby: ['乒乓球', '足球'],
family: {
baby: '小pink'
}
}
const o = _.cloneDeep(obj)
console.log(o)
o.family.baby = '老pink'
console.log(obj)
</script>
</body>

利用JSON实现深拷贝

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<body>
<script>
const obj = {
uname: 'pink',
age: 18,
hobby: ['乒乓球', '足球'],
family: {

baby: '小pink'
}
}
// 把对象转换为 JSON 字符串
// console.log(JSON.stringify(obj))
const o = JSON.parse(JSON.stringify(obj))
console.log(o)
o.family.baby = '123'
console.log(obj)
</script>
</body>

先转换成字符串,再转成对象。

异常处理

throw抛异常

1
2
3
4
5
6
7
8
9
10
11
<script>
function fn(x, y) {
if (!x || !y) { //没有传值就是undefined
// throw '没有参数传递进来'
throw new Error('没有参数传递过来')
}

return x + y
}
console.log(fn())
</script>
  • Error对象配合throw使用
  • 会终止程序

捕获异常 try catch finally

通过try/catch捕捉错误信息(浏览器提供的错误信息)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<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>

dubugger

相当于断点,直接在代码里面插入

this

this指向

普通函数

谁调用指向谁

普通函数没有明确调用者时this值为window,严格模式下没有调用者为undefined

箭头函数

  1. 箭头函数会默认帮我们绑定外层this的值 ,箭头函数中this的值和外层的this是一样的
  2. 箭头函数中this引用的就是最近作用域中的this
  3. 向外层作用域中,一层层查找this,直到有this的定义

箭头函数中访问的 this 不过是箭头函数所在作用域的 this 变量。

原型,构造函数,dom事件函数等等不方便使用箭头函数

适用需要使用上层this的地方

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<script>

console.log(this) // 此处为 window
// 箭头函数
const sayHi = function() {
console.log(this) // 该箭头函数中的 this 为函数声明环境中 this 一致
}
// 普通对象
const user = {
name: '小明',
// 该箭头函数中的 this 为函数声明环境中 this 一致
walk: () => {
console.log(this)
},

sleep: function () {
let str = 'hello'
console.log(this)
let fn = () => {
console.log(str)
console.log(this) // 该箭头函数中的 this 与 sleep 中的 this 一致
}
// 调用箭头函数
fn();
}
}

// 动态添加方法
user.sayHi = sayHi

// 函数调用
user.sayHi()
user.sleep()
user.walk()
</script>

改变this

call()

使用call方法调用函数,同时指定被调用函数中this的值

1
fun.call(thisArg,arg1,arg2,...)
  • thisArg 在fun运行时指定this的值
  • arg1,arg2函数正常的形参

apply()

使用apply方法调用函数,同时指定被调用函数中this的值

1
fun.apply(thisArg,[argsArray])

argsArray:传递的值,必须包含在数组里面

1
2
3
4
5
6
7
const obj = {
age: 18
}
function fn(x, y) {
console.log(this) // {age: 18}
console.log(x + y)
}

返回值就是函数的返回值

==bind()==

不会调用函数,能改变函数内部this指向,返回值是一个新的函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<script>
// 普通函数
function sayHi() {
console.log(this)
}
let user = {
name: '小明',
age: 18
}
// 调用 bind 指定 this 的值
let sayHello = sayHi.bind(user);
// 调用使用 bind 创建的新函数
sayHello()
</script>

防抖

  • 单位时间内,频繁触发事件,只执行最后一次

场景:搜索框搜索输入。只需用户最后依次输入完成

​ 手机号,邮箱验证输入检测

实现:

  1. lodash提供的防抖处理
1
_.debounce(func,[wait=0],[options=])

2.手写一个防抖函数

(防抖的核心就是利用setTimeout实现)

  1. 声明定时器变量
  2. 每次鼠标移动时要先判断是否有定时器,有的话清除以前的定时器
  3. 如果没有定时器,则开启
  4. 定时器里面写函数调用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
function mouseMove() {
box.innerHTML = ++i
// 如果里面存在大量操作 dom 的情况,可能会卡顿
}
function debounce(fn,t){
let timeId
return function(){ //这里返回匿名函数很关键,相当于形成了一个闭包
if(timeId) clearTimeout(timeId)
timeId =setTimeout(function(){
fn();
},t)
}
}
box.addEventListener('mousemove',debounce(mouseMove,500))

为什么return一个匿名函数?不用行不行?
确保事件触发的是匿名函数,不会重新将timer置为null。同时,匿名函数是一个闭包,timer变量不会被回收。这样,就能使用clearTimeout清空上次的定时器了。默默摸摸你

节流 throttle

单位时间内,频繁触发事件,只执行一次

  1. lodash提供的节流函数
1
box.addEventListener('mousemove', _.throttle(mouseMove, 500))

2.手写节流函数

1
2
3
4
5
6
7
8
9
10
11
function throttle(fn,t){
let timer=null
return function(){
if(!timer){
timer=setTimeout(function(){
fn()
timer=null //这里清空定时器必须用这个方法,因为定时器函数里面无法使用cleartimeout
},t)
}
}
}

JS-进阶-拷贝异常节流抖动
https://wjcbolg.cn/2023/04/29/JS学习笔记16/
作者
JasonWang
发布于
2023年4月29日
许可协议
BY-JW