JavaScript-深浅拷贝、异常处理、this、防抖节流
深浅拷贝
在开发中经常需要复制,就会出现这种情况

两个对象都指向同一个地址,所以修改一个的制,另一个也会变化
浅拷贝
首先浅拷贝和深拷贝只针对引用类型
浅拷贝:拷贝的是地址
常见方法:
- 拷贝对象:
Object.assgin()/ 展开运算符{...obj}拷贝对象 - 拷贝数组:
Array.prototype.concat()或者[...arr]
如果是简单数据类型拷贝值,引用数据类型拷贝的是地址 (简单理解: 如果是单层对象,没问题,如果有多层就有问题)
深拷贝
首先浅拷贝和深拷贝只针对引用类型
深拷贝:拷贝的是对象,不是地址
常见方法:
- 通过递归实现深拷贝
- lodash/cloneDeep
- 通过
JSON.stringify()实现
通过递归实现深拷贝:
1 | const obj = { |

js库lodash里面cloneDeep内部实现了深拷贝:
1 | <script src="js/lodash.min.js"></script> |

通过JSON.stringify()实现:
JSON.stringify(obj):将对象obj序列化成一个JSON字符串。JSON.parse(...):将上一步得到的JSON字符串反序列化成一个新的JavaScript对象。
1 | const o = JSON.parse(JSON.stringify(obj)) |

异常处理
throw抛异常
异常处理是指预估代码执行过程中可能发生的错误,然后最大程度的避免错误的发生导致整个程序无法继续运行
1 | function counter(x, y) { |

总结:
throw抛出异常信息,程序也会终止执行throw后面跟的是错误提示信息Error对象配合throw使用,能够设置更详细的错误信息
try/catch捕获错误信息
我们可以通过try/catch捕获错误信息(浏览器提供的错误信息)
1 | function foo() { |

总结:
try...catch用于捕获错误信息- 将预估可能发生错误的代码写在
try代码段中 - 如果
try代码段中出现错误后,会执行catch代码段,并截获到错误信息 finally不管是否有错误,都会执行
debugger
直接在代码中加入debugger,可以直接设置断点

处理this
this指向
普通函数
普通函数的调用方式决定this的值,就是谁调用的this就指向谁
1 | function sayHi() { |
普通函数没有明确调用者时,this值为window,严格模式下没有调用者时this值为undefined
箭头函数
箭头函数的this与普通函数完全不一样,不受调用方式的影响,事实上箭头函数中并不存在this
- 箭头函数会默认帮我们绑定外层
this的值,所以在箭头函数中this的值和外层的this是一样的 - 箭头函数中的
this引用的就是最近作用域中的this - 向外层作用域中,一层一层查找
this,直到有this的定义
1 | const sayHi = () => { |
ChatGPT的解释:
user.sayHi(): 在这里,sayHi是一个箭头函数,它在全局上下文中被定义。因此,当user.sayHi()被调用时,this指向箭头函数定义时的上下文,即全局对象(在浏览器中是window,在Node.js中可能是global或globalThis)。注意,即使sayHi被作为user对象的方法调用,由于它是箭头函数,这不会影响this的值。user.hobby():hobby是使用function关键字定义的传统函数,并作为user对象的方法被调用。因此,在hobby函数内部,this指向调用它的对象,即user对象。同理,内部的箭头函数fn捕获了hobby函数中的this,所以在fn中this也指向user对象。user.word():word是一个箭头函数,在user对象的定义中捕获了它定义时的this。由于user对象字面量不形成单独的作用域,word函数捕获的this是在包含user定义的外部作用域中的this,也就是全局对象。
特殊情况
在开发中使用箭头函数前需要考虑函数中 this 的值,事件回调函数使用箭头函数时,this 为全局的 window,因此DOM事件回调函数不推荐使用箭头函数
1 | // DOM 节点 |
同样由于箭头函数 this 的原因,基于原型的面向对象也不推荐采用箭头函数
1 | function Person() { |
总结:
- 函数内不存在this,沿用上一级的
- 不适用于构造函数、原型函数、DOM事件函数等
- 适用于需要使用上层this的地方
改变this
JavaScript中还允许指定函数中this的指向,有3个方法可以动态指定普通函数中this的指向
call()apply()bind()
call()
使用 call() 方法调用函数,同时指定函数中 this 的值
语法:fn.call(thisArg, arg1, arg2, ...)
thisArg:在fn函数运行时指定的this值arg1、arg2:传递的其他参数- 返回值就是函数的返回值,因为他就是调用函数
1 | // 普通函数 |
总结:
call()方法能够在调用函数的同时指定this的值- 使用
call()方法调用函数时,第1个参数为this指定的值 call()方法的其余参数会依次自动传入函数做为函数的参数
apply()
使用 apply() 方法调用函数,同时指定函数中 this 的值
语法:fn.apply(thisArg, [argsArray])
thisArg:在fn函数运行时指定的this值argsArray:传递的值,必须包含在数组里面- 返回值就是函数的返回值,因为它就是调用函数
- 因此
apply()主要跟数组有关系,比如使用Math.max()求数组的最大值
1 | function sayHi() { |
总结:
apply()方法与call()方法几乎一致,只是第二个参数是否为数组的区别
bind()
bind 方法并不会调用函数,而是创建一个指定了 this 值的新函数
语法:fn.bind(thisArg, arg1, arg2, ...)
thisArg:在 fn 函数运行时指定的this值arg1,arg2:传递的其他参数- 返回由指定的
this值和初始化参数改造的原函数拷贝(新函数) - 因此当我们只是想改变
this指向,并且不想调用这个函数的时候,可以使用bind(),比如改变定时器内部的this指向
1 | function saythis() { |

三种方法的区别
相同点:
- 都可以改变函数内部的
this指向
区别点:
call和apply会调用函数, 并且改变函数内部this指向call和apply传递的参数不一样,call传递参数arg1,arg2..形式,而apply必须数组形式[arg]bind不会调用函数,可以改变函数内部this指向
主要应用场景:
call调用函数并且可以传递参数apply经常跟数组有关系,比如借助于数学对象实现数组最大值最小值bind不调用函数,但是还想改变this指向,比如改变定时器内部的this指向
性能优化
防抖
防抖(debounce),就是指触发事件后在 n 秒内函数只能执行一次,如果在 n 秒内又触发了事件,则会重新计算函数执行时间
例如之前的商品放大镜案例,鼠标放入盒子就会在旁边出现放大效果,离开200ms后消失,但是如果在200ms之内鼠标再次返回盒子,则不会出现放大效果,所以需要设定重新计算执行时间
1 | //监听鼠标经过和离开 |
节流
节流(throttle),就是指连续触发事件但是在 n 秒中只执行一次函数
假如一张轮播图完成切换需要300ms, 不加节流效果,快速点击,则嗖嗖嗖的切换,加上节流效果,不管快速点击多少次,300ms时间内,只能切换一张图片。
