开发过程中 如果我们直接采用赋值的方法的时候会有下面的问题
修改一个对象的赋值的时候,另一个对象的赋值也会发生改变
因为他们的存储规则
所以为了避免这些问题,我们产生了浅拷贝。
1.浅拷贝和深拷贝只是针对引用类型
2.浅拷贝:拷贝的是地址,如果是简单数据类型拷贝值,引用数据类型拷贝的是地址 (简单理解: 如果是单层对象,没问题,如果有多层就有问题)
常见的拷贝方法:
1.拷贝对象 : Object.assgin() 或者 /展开运算符{...obj}拷贝对象
2.拷贝数组:Array.prototype.concat() 或者[...arr] 展开运算符
当拷贝复杂类型数据的时候会出现 一下问题?
总结:
1.直接赋值和浅拷贝有什么区别?
①直接赋值的方法,只要是对象,都会相互影响,因为直接拷贝是对象栈里面的地址
②浅拷贝如果是一层对象,不相互影响,如果出现多层对象拷贝还是会相互影响。
浅拷贝怎么理解?
浅拷贝对象之后,里面的属性值是简单数据类型的直接拷贝
如果属性值是引用数据类型则拷贝的是地址。
深浅拷贝区别:浅拷贝和深拷贝只针对引用类型
深拷贝:拷贝的是对象而不是地址
常见方法:
①通过递归实现深拷贝(for(k in))
②lodash/cloneDeep
③通过JSON.stringify()实现
函数递归:
如果一个函数在内部可以调用其本身,那么这个函数就是递归函数。
递归函数的作用和循环效果类似
由于递归很容易发生‘栈溢出’的错误(也就是死循环)(stack overflow),所以必须加退出条件 return
函数递归的方法?
let num = 1
//fn是递归函数
function fn(){
console.log('我要打印6次')
if(num >= 6 ){ //如果不教判定条件就会陷入栈溢出 也就是死循环
return
}
num++
fn()//函数内部调用函数自己
}
fn()
利用函数递归实现 setTimeout模拟 setInterval效果
function fn(){
const time = new Date().toLocaleString()
console.log(time)
setTimeout(fn,1000)//定时器调用当前函数 fn调用的是fn函数
//这里的fn = fn(); setTimeout(function(){},1000) fn代替了function(){}
}
fn()
1.2.1 通过递归调用 实现浅拷贝
const oldObj = {
uname:'red',
age:18,
hobby:['xx','xxx'],
family:{
father:1
}
}
//创建一个新对象 来存储数据
const newObj={}
function deepCopy(newObj,oldObj){
for(let k in oldObj){
//判断老的数据类型里面 是否存在 数组类型的数据 通过instanceof
if(oldObj[k] instanceof Array){
//给新对象创建 一个新的属性 并且赋值给一个数组来方便存储数据
newObj[k] = []
//调用深拷贝函数 把刚创建的数组 和 老的数组 以实参的形式传入到函数 来赋值
deepCopy(newObj[k],oldObj[k])
//判断老的数据里面 是否存在对象类型的数据 通过instanceof
}else if(oldObj[k] instanceof Object){
//创建一个新的对象 属性 并且给一个对象
newObj[k] = []
//调用深拷贝函数 把创建的新的对象和 老数据中存储的 以实参的形式传入到函数 来拷贝
deepCopy(newObj[k],oldObj[k])
// 如果不满足以上两项条件 就是引用赋值类型 数据 可以直接赋值。
}else{
newObj[k] = oldObj[k];
}
}
}
1.2.2 通过JS库lodash 里面的cloneDeep 内部实现了深拷贝
const obj = {
uname:'pink',
age:18,
hobby:['篮球','足球'],
family:{
baby:'小pink'
}
}
//语法 _.cloneDeep(要被克隆的对象)
const o = _.cloneDeep(obj)
o.family.baby='老pink'
1.2.3通过JSON.stringify() 方法实现
const obj = {
uname:'pink',
age:18,
hobby:['篮球','足球'],
family:{
baby:'小pink'
}
}
// 先用json 将对象转义为字符串类型 然后再转移成 对象 存储到O 里面即可
const o = JSON.parse(JSON.stringify(obj))
console.log(o)
o.family.baby='老pink'
console.log(obj)
2.1throw 抛出异常
异常处理是指预估代码执行过程中可能发生的错误,然后最大成端的避免错误的发生,避免导致整个程序无法继续运行
function counter(x,y){
if(!x || !y){
//throw '参数不能为空'
throw new Error('参数不能为空')
}
return x+y
}
counter()
总结 :
①throw 抛出异常信息,程序也会终止执行
②throw 后面跟的是错误提示信息
③Error 对象配合throw 使用能够设置更详细的错误信息
2.2try/catch 捕获异常
我们可以通过try / catch 捕获错误信息(浏览器提供的错误信息)try 试试catch 拦住 finally 最后
function foo() {
try{
//查找 DOM 节点
const p = doucument.querySelector('.p')
p.style.color = 'red'
// error 就像时间对象中的 e 一样 不可以省略
} catch(error){
//try 代码断中执行有错误时,会执行 catch代码段
//查看错误信息
console.log(error.message)
///终止代码运行
return
}
finally{
alert('执行')
}
console.log('如果出现错误,我的语句不会执行')
}
foo()
总结:
1.try...catch用于捕获错误信息
2.将预估可能发生错误的代码写在try代码段中
3.如果try代码段中出现错误后,会执行catch代码段,并截获到错误信息
4.finally 不管是否有错误 都会执行
2.3debugger
开启 debugger ,调试模式下代码会在 debugger 处停止执行。
类似于断点调试哦
3.1this 指向
3.1.1普通函数this指向
普通函数的调用方式决定了 this的值,即谁调用this 的值指向谁
普通函数没有明确调用者时this值为window 严格模式(use strict 图三)下没有调用者时this的值为undefined
3.1.2箭头函数this指向
箭头函数中的 this 与普通函数完全不同,也不受调用方式的影响,事实上箭头函数中并不存在 this !
注意情况1
注意情况2
js 中还允许指定函数中this 的指向,有三个方法可以动态指定普通函数中this的指向
使用call方法调用函数,同时指定被调用函数中this的值
call作用:调用函数,并可以改变刁颖函数里面的this指向
call 里面第一个参数是指定this,其余是可以传递才参数
appley方法调用函数,同时指定函数调用中this值 这个主要来传递数组
第一个参数可以设置null
call和apple的区别是什么?
都是调用函数,都能改变this的指向
参数不一样,apply传递必须是数组
bind()方法不会调用函数,但是能改变函数内部this指向的问题
总结:
相同点:都可以改变函数内部的this指向
区别点:
call 和 apply 会调用函数 并且改变函数内部this指向
call 和 apply 传递的参数不一样,call 传递参数 aru1,aru2..形式,必须是数组【arg】
bind 不会调用函数,可以改变函数内部this指向
主要应用场景
call调用函数并且可以传递参数
apply 经常跟数组有关系,比如借助数学对象Math.来求大小值
bind 不用调用函数,但是还想改变this指向,比如改变定时器内部的this指向
4.1防抖
就是指触发事件后的N秒内,如果再次被触发事件就会触发当前计算函数的执行时间。
4.2节流