深浅拷贝:
1.问题:赋值操作时,obja=objb会把b的地址赋值给a,这样使得修改a时候,b的内容也会改变。
2.浅拷贝:
深浅拷贝都 针对的是引用类型,而浅拷贝拷贝的是地址。
拷贝对象方法: object.assgin(新,旧) 和 展开运算符:{...旧}
拷贝数组方法:array.prototype.concat() 和[...数组名]
浅拷贝的问题: 普通对象类型直接拷贝值,如果原来的对象里面又嵌套一个对象,则嵌套的对象拷贝的又会是地址了,后面再修改会使得新旧都改变。
3.为了解决浅拷贝问题则用深拷贝:
深拷贝的三种方式:
①通过递归实现
递归函数:函数自己内部调用自己。
例1:settimeout()到一定事件执行某个函数。 tolocalestring()获得当前的日期。实现计时器
function getTime() {
document.querySelector('div').innerhtml = new Date().tolocalestring()
settimeout(getTime,1000)
}
例子2:
const obj = {
uname: 'pig',
age: 18,
hobby: ['足球', '篮球']
}
//新对象为空
const o = { }
//构造函数deepcopy把obj的内容赋值到o里面
function deepcopy(newobj, oldobj) {
//k是属性名,oldobj[k]才是真正的值。
for (let k in oldobj) {
//对于拷贝来说,对象是简单对象直接赋值了。但是如果有数组,浅拷贝依然不能解决改变原来数组里面值的问题。要先判断oldobj[k],是否是数组类型
if(oldobj [k] instanceof Array){
newobj[k] = [ ] //如果oldobj[k]是数组类型,则new[hobby]先设为空数组形式。再次进行对象里面小数组的deepcopy
deepcopy(newobj[k], oldobj[k]) //相当于把old[hobby]赋值给new[hobby]
}else{ //如果不是数组类型直接赋值
newobj[k] = oldobj[k]
}
}
}
一定先判断对象,判断数组。再因为数组也是对象。
//调用一下才执行
deepcopy(o, obj)
//console.log(o)
o.hobby[0] = '羽毛球'
console.log(obj)
console.log(o)
一定先判断对象,判断数组。再因为数组也是对象。
深拷贝:1.新的对象修改不会影响旧的值,且深拷贝需要用递归。
2.普通拷贝直接赋值,若是数组和对象则再次利用递归,且是先判断Array再判断object。
②lodash/cloneDeep
js库里面lodash的loaddeep方法,首先下载js库,再在script标签中单独进行引入
一定是单独在一对<script></script>中引入!!!!错过两次了
<script src="./lodash.min.js"></script>
<script>
//引入js库,必须在script标签里面引用!!!
const obj = {
uname: 'pig',
age: 18,
hobby: ['足球', '篮球'],
family: {
baby: 'pig1'
}
}
const o = _.cloneDeep(obj)
o.family.baby = 'pig2'
console.log(obj)
console.log(o)
</script>
③通过JSON.stringify()实现
简称 涮一下就是阳澄湖大闸蟹了。 转来转去两者的堆地址就不同了,所以两者之间修改是不互相影响的。
//把对象转为json字符串用json.stringify, 再把字符串转为对象用parse.
const obj2 = JSON.parse(JSON.stringify(obj1))
异常处理
1.throw异常.
throw new Error('错误信息') //类似一个错误函数
throw抛出会终止程序。
2.try/catch捕获异常
try 是试一试下面代码是否会出错,catch是如果try代码出错则会报错,而且是需要传参数error的,这样才能打印出error.message,在catch里面加上return 才会终止程序,否则会继续执行下面操作。finally是无论代码是否出错都会执行的内容。
3.debugger
用来调试时候立马会出现在debugger处。
处理this
1.普通函数this指向:
谁调用就指向谁
function fn() {
conlose.log(this) //这个this就是指向window,因为是window.fn() fn是window的方法。
}
const obj = {
speak: function fn() { conlose.log(this)}//这里this指向obj。因为是obj.speak() .
}
函数里面才有this,对象是没有this指向的
2.箭头函数
箭头函数无this,this指向的是上一级的函数。
3.改变this
①call() 很少用此方法. 传的参数是普通数
②apply() 传的是一个数组!!!
const obj ={
age: 18
}
function fn(x, y) {
console.log(this) //this指向的是window
console.log(x +y)
}
fn.apply(obj, [1, 2]) //this指向的是obj
应用于:求数组的最大值!
之前求数组里面最大值,先用运算符...展开数组变成数值,再用数学方法 Math.max(数值)。
现在求最大值,Math.max.apply(Math, arr) //this指的是math,也可以写null谁也不指向。 这里重点在于我可以传数组到max去比较!!!
③bind() 不会调用函数。只会改变this。
区别:前两个都是返回的是调用的函数结果。 而这个返回的是一摸一样的函数内容,只是this指向改变了而已。
三者的
相同点:
都可以改变函数内部的 this 指向.
区别点:
call 和 apply 会调用函数,并且改变函数内部 this 指向.
call 和 apply 传递的参数不一样, call 传递参数aru1,aru2..形式 apply 必须数组形式[ arg ]
bind 不会调用函数,可以改变函数内部 this 指向.
主要应用场景:
call 调用函数并且可以传递参数
apply 经常跟数组有关系.比如借助于数学对象实现数组最大值最小值
bind 不调用函数,但是还想改变 this 指向.比如改变定时器内部的 this 指向.
防抖
单位时间内,频繁触发事件,只执行最后一次。
例子:划过盒子则盒子内数字+1,如果在盒子划一圈则会一直+1,防抖就是划一圈只会执行最后一次划过才+1.
处理方法:
_.debounce(执行的函数, 等待时间)
手写防抖函数:
核心利用settimeou定时器来实现的
1. 声明定时器的变量
2.每次鼠标移动的时候就要先判断是否有定时器,如果有定时器则先清除定时器。
3.如果没有定时器,就开启定时器,存到定时器变量里面。
4.定时器里面写函数调用
function debounce (fn,t) {
let timer
return function () {
if (timer) clearTimeout(timer)
timer = setTimeout(function () {
fn()},t)
}
}
box.addEventListener('mousemove', debounce(mousemove), 500)