一、深浅拷贝

1.浅拷贝
首先浅拷贝和深拷贝只针对引用类型
浅拷贝:拷贝的是地址
常见方法:
-
拷贝对象:Object.assgin() / 展开运算符 {...obj} 拷贝对象
-
拷贝数组:Array.prototype.concat() 或者 [...arr]
>如果是简单数据类型拷贝值,引用数据类型拷贝的是地址 (简单理解: 如果是单层对象,没问题,如果有多层就有问题)



html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<script>
const obj = {
uname: "pink",
age: 18,
family: {
baby: "小pink",
},
};
// 浅拷贝
// const o = { ...obj };
// console.log(o); //Object { uname: "pink", age: 18, family: {...} }
// o.age = 20;
// console.log(o); //Object { uname: "pink", age: 20, family: {...} }
// console.log(obj); //Object { uname: "pink", age: 18, family: {...} }
const o = {};
Object.assign(o, obj);
o.age = 20;
o.family.baby = "老pink";
console.log(o); //Object { uname: "pink", age: 20, family: {...} }
console.log(obj); //Object { uname: "pink", age: 18, family: {...} }
</script>
</body>
</html>
2.深拷贝
首先浅拷贝和深拷贝只针对引用类型
深拷贝:拷贝的是对象,不是地址
常见方法:
- 通过递归实现深拷贝
- lodash/cloneDeep
- 通过JSON.stringify()实现
(1)函数递归
函数递归:
如果一个函数在内部可以调用其本身,那么这个函数就是递归函数
·简单理解:函数内部自己调用自己, 这个函数就是递归函数
·递归函数的作用和循环效果类似
· 由于递归很容易发生"栈溢出"错误(stack overflow),所以 必须要加退出条件 return
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
const obj = {
uname: 'pink',
age: 18,
hobby: ['乒乓球', '足球'],
family: {
baby: '小pink'
}
}
const o = {}
// 拷贝函数
function deepCopy(newObj, oldObj) {
debugger
for (let k in oldObj) {
// 处理数组的问题 一定先写数组 在写 对象 不能颠倒
if (oldObj[k] instanceof Array) {
newObj[k] = []
// newObj[k] 接收 [] hobby
// oldObj[k] ['乒乓球', '足球']
deepCopy(newObj[k], oldObj[k])
} else if (oldObj[k] instanceof Object) {
newObj[k] = {}
deepCopy(newObj[k], oldObj[k])
}
else {
// k 属性名 uname age oldObj[k] 属性值 18
// newObj[k] === o.uname 给新对象添加属性
newObj[k] = oldObj[k]
}
}
}
deepCopy(o, obj) // 函数调用 两个参数 o 新对象 obj 旧对象
console.log(o)
o.age = 20
o.hobby[0] = '篮球'
o.family.baby = '老pink'
console.log(obj)
console.log([1, 23] instanceof Object)
// 复习
// const obj = {
// uname: 'pink',
// age: 18,
// hobby: ['乒乓球', '足球']
// }
// function deepCopy({ }, oldObj) {
// // k 属性名 oldObj[k] 属性值
// for (let k in oldObj) {
// // 处理数组的问题 k 变量
// newObj[k] = oldObj[k]
// // o.uname = 'pink'
// // newObj.k = 'pink'
// }
// }
</script>
</body>
</html>
(2)lodash
js库lodash里面cloneDeep内部实现了深拷贝

html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<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); //Object { uname: "pink", age: 18, hobby: (2) [...], family: Object { baby: "老pink" } }
o.family.baby = "老pink";
console.log(obj); //Object { uname: "pink", age: 18, hobby: (2) [...], family: Object { baby: "小pink" } }
</script>
</body>
</html>
(3)JSON.stringify()
javascript
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<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); //Object { uname: "pink", age: 18, hobby: Array [ "乒乓球", "足球" ], family: {Object { baby: "123" }} }
o.family.baby = "123";
console.log(obj); //Object { uname: "pink", age: 18, hobby: Array [ "乒乓球", "足球" ], family: Object { baby: "小pink" } }
</script>
</body>
</html>

二、异常处理
1.throw抛异常

2.try/catch捕获异常


3.debugger
相当于断点调试

三、处理this
1.this指向
(1)普通函数

普通函数的调用方式决定了 this 的值,即【谁调用 this 的值指向谁】

html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<button>点击</button>
<script>
// 普通函数: 谁调用我,this就指向谁
console.log(this) // window
function fn() {
console.log(this) // window
}
window.fn()
window.setTimeout(function () {
console.log(this) // window
}, 1000)
document.querySelector('button').addEventListener('click', function () {
console.log(this) // 指向 button
})
const obj = {
sayHi: function () {
console.log(this) // 指向 obj
}
}
obj.sayHi()
</script>
</body>
</html>
(2)箭头函数
html
<script>
console.log(this); // 此处为 window
// 普通函数
const sayHi = function () {
console.log(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();
// this指的是user
// Object { name: "小明", walk: walk(), sleep: sleep(), sayHi: sayHi() }
// user.sleep();
// this指的是user(普通函数)
// Object { name: "小明", walk: walk(), sleep: sleep(), sayHi: sayHi() }
// hello
// Object { name: "小明", walk: walk(), sleep: sleep(), sayHi: sayHi() }
user.walk();
// this指的是window(箭头函数)
// Window
// Window
</script>

html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<button class="btn">点击</button>
<script></script>
</body>
<script>
// DOM 节点
const btn = document.querySelector(".btn");
// 箭头函数 此时 this 指向了 window
btn.addEventListener("click", () => {
console.log(this); //Window
});
// 普通函数 此时 this 指向了 DOM 对象
btn.addEventListener("click", function () {
console.log(this);//<button class="btn">
});
</script>
</html>


2.改变this
(1)call()- this指向传入的参数


(2)apply()-传入必须是数组,this指向数组



(3)bind()-不会调用函数,但改变this

(4)总结- important

四、性能优化 (重要)
1.防抖
所谓防抖,就是指触发事件后在 n 秒内函数只能执行一次,如果在 n 秒内又触发了事件,则会重新计算函数执行时间

实现方式
(1)lodash提供的防抖来处理
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style>
.box {
width: 500px;
height: 500px;
background-color: #ccc;
color: #fff;
text-align: center;
font-size: 100px;
}
</style>
</head>
<body>
<div class="box"></div>
<script src="./lodash.min.js"></script>
<script>
const box = document.querySelector('.box')
let i = 1 // 让这个变量++
// 鼠标移动函数
function mouseMove() {
box.innerHTML = ++i
// 如果里面存在大量操作 dom 的情况,可能会卡顿
}
// 添加事件
// box.addEventListener('mousemove', mouseMove)
// lodash 防抖的写法 - 500ms后加一
// 语法:_.debounce(fun,时间)
box.addEventListener('mousemove', _.debounce(mouseMove, 500))
</script>
</body>
</html>
(2)手写一个防抖函数来处理
手写防抖函数
核心是利用setTimeout()定时器来实现
1.声明定时器变量
2.每次鼠标移动(时间触发)的时候都要先判断是否有定时器,如果有先清除以前的定时器
3.如果没有定时器,则开启定时器,存入到定时器变量里面。
4.定时器里面写函数调用
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style>
.box {
width: 500px;
height: 500px;
background-color: #ccc;
color: #fff;
text-align: center;
font-size: 100px;
}
</style>
</head>
<body>
<div class="box"></div>
<script>
const box = document.querySelector('.box')
let i = 1 // 让这个变量++
// 鼠标移动函数
function mouseMove() {
box.innerHTML = ++i
// 如果里面存在大量操作 dom 的情况,可能会卡顿
}
// 防抖函数
function debounce(fn, t) {
let timeId
return function () {
// 如果有定时器就清除
if (timeId) clearTimeout(timeId)
// 开启定时器 200
timeId = setTimeout(function () {
fn()
}, t)
}
}
// box.addEventListener('mousemove', mouseMove)
box.addEventListener('mousemove', debounce(mouseMove, 200))
</script>
</body>
</html>
2.节流
所谓节流,就是指连续触发事件但是在 n 秒中只执行一次函数。

(1) lodash函数
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style>
.box {
width: 500px;
height: 500px;
background-color: #ccc;
color: #fff;
text-align: center;
font-size: 100px;
}
</style>
</head>
<body>
<div class="box"></div>
<script src="./lodash.min.js"></script>
<script>
const box = document.querySelector(".box");
let i = 1; // 让这个变量++
// 鼠标移动函数
function mouseMove() {
box.innerHTML = ++i;
// 如果里面存在大量操作 dom 的情况,可能会卡顿
}
// box.addEventListener('mousemove', mouseMove)
// lodash 节流写法
box.addEventListener("mousemove", _.throttle(mouseMove, 500));
</script>
</body>
</html>
(2)手写一个节流函数
手写一个节流函数- 每隔 500ms + 1
节流的核心就是利用定时器(setTimeout)来实现
1.声明一个定时器变量
2.当鼠标每次滑动都先判断是否有定时器了,如果有定时器则不开启新定时器
3.如果没有定时器则开启定时器,记得存到变量里面
3.1 定时器里面调用执行的函数
3.2定时器里面要把定时器清空

html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style>
.box {
width: 500px;
height: 500px;
background-color: #ccc;
color: #fff;
text-align: center;
font-size: 100px;
}
</style>
</head>
<body>
<div class="box"></div>
<script>
const box = document.querySelector(".box");
let i = 1; // 让这个变量++
// 鼠标移动函数
function mouseMove() {
box.innerHTML = ++i;
// 如果里面存在大量操作 dom 的情况,可能会卡顿
}
// 手写一个节流函数- 每隔 500ms + 1
// 节流的核心就是利用定时器(setTimeout)来实现
// 1.声明一个定时器变量
// 2.当鼠标每次滑动都先判断是否有定时器了,如果有定时器则不开启新定时器
// 3.如果没有定时器则开启定时器,记得存到变量里面
// 3.1 定时器里面调用执行的函数
// 3.2定时器里面要把定时器清空
// 节流函数
function throttle(fn, t) {
let timeId = null;
return function () {
if (!timeId) {
timeId = setTimeout(function () {
fn();
// 清空定时器
timeId = null;
// 不用clearTimeout()
// 原因:在setTimeout中是无法删除定时器多,因为定时器还在运作,所以使用timer=null 而不是clearTimeout(timer)
}, t);
}
};
}
// box.addEventListener('mousemove', mouseMove)
box.addEventListener("mousemove", throttle(mouseMove, 200));
</script>
</body>
</html>
3.总结

