一、深浅拷贝
首先浅拷贝和深拷贝只针对想Object,Array这样的复杂对象,简单来说,浅拷贝只复制一层对象的属性,二深拷贝则复制了所有的层级。
对于字符串类型,浅复制是对值的复制,对于对象来说,浅复制是对对象地址的复制,并没 有开辟新的栈,也就是复制的结果是两个对象指向同一个地址,修改其中一个对象的属性,则另一个对象的属性也会 改变,而深复制则是开辟新的栈,两个对象对应两个不同的地址,修改一个对象的属性,不会改变另一个对象的属性。
浅拷贝

<script>
const obj = {
uname: '张三',
age: 18,
gender: '男',
gfs: ['凤姐', '芙蓉姐姐', '黄蓉'],
wife: {
w1: '蔡徐坤',
w2: 'ikun'
}
}
//第一种
const arr = { ...obj }
//使用展开运算符
arr.uname = '解决'
arr['wife']['w1'] = '开辟'
console.log(arr)
console.log(obj)
//第二种
const arr = {}
Object.assign(arr,obj)
arr.uname = '解决'
arr['wife']['w1'] = '开辟'
console.log(arr)
console.log(obj)
</script>
深拷贝

<script>
//递归:在函数里面 调用自己
//在内存里面开辟大量的空间,能少写就少写
//入栈:先进后出,后进先出
function f(n) {
console.log(n)
n = n - 2
if (n > 0) {
f(n)
}
console.log(n)
}
f(10)
</script>
<script>
//斐波拉契
//1,1,2,3,5,8,13.....
function f(n) {
if (n === 1 || n === 2) {
return 1
} else {
return f(n - 1) + f(n - 2)
}
}
const res = f(4)
console.log(res)
//阶层
//!5 = 5*4*3*2*1
function a(n) {
if (n === 1) {
return 1
} else {
return a(n - 1) * n
}
}
const to = a(4)
console.log(to)
//猴子吃桃子
function b(n) {
if (n === 0) {
return 1
} else {
return (b(n-1) + 1) * 2
}
}
const num = b(10)
console.log(num)
</script>
用递归将数组扁平化
<script>
const arr = [1, 2, 3, 4, [5, [7, 8, [9, 10]]]]
const newArr = []
function get(a) {
a.forEach(function (item) {
//判断是否是数组
if (item instanceof Array) {
get(item)
} else {
newArr.push(item)
}
})
}
get(arr)
console.log(newArr)
</script>
<script>
const obj = {
uname: '张三',
age: 18,
gender: '男',
gfs: ['凤姐', '芙蓉姐姐', '黄蓉'],
wife: {
w1: '蔡徐坤',
w2: 'ikun'
}
}
const arr = {}
//将就对象 里面的属性 copy到新对象里面
function deepCopy(newObj, oldObj) {
for (let k in oldObj) {
//获取旧对象 里面的属性值 赋值为item变量
const item = oldObj[k]
//判断item是不是数组
if (item instanceof Array) {
newObj[k] = []
deepCopy(newObj[k], item)
} else if (item instanceof Object) {
newObj[k] = {}
deepCopy(newObj[k], item)
} else {
newObj[k] = item
}
}
}
deepCopy(arr, obj)
console.log(arr)
</script>
2. js库lodash里面cloneDeep内部实现了深拷贝
<script src="./js/lodash.min.js"></script>
<script>
const obj = {
uname: '张三',
age: 18,
gender: '男',
gfs: ['凤姐', '芙蓉姐姐', '黄蓉'],
wife: {
w1: '蔡徐坤',
w2: 'ikun'
}
}
const arr = _.cloneDeep(obj)
arr['gfs'][1] = '订狄'
console.log(arr)
console.log(obj)
</script>
3. 通过JSON.stringify()实现
<script>
const obj = {
uname: '张三',
age: 18,
gender: '男',
gfs: ['凤姐', '芙蓉姐姐', '黄蓉'],
wife: {
w1: '蔡徐坤',
w2: 'ikun'
}
}
const str = JSON.stringify(obj)
const arr = JSON.parse(str)
arr['gfs'][1] = '订狄'
console.log(arr)
console.log(obj)
</script>
二、异常处理
throw 抛异常
异常处理是指预估代码执行过程中可能发生的错误,然后最大程度的避免错误的发生导致整个程序无法继续运行。
<script>
function f(x,y) {
if(!x || !y) {
throw new Error('没有传递参数')
//阻止代码往下执行
//Error 对象配合 throw 使用
}
return x + y
}
const ret = f()
console.log(ret)
</script>
总结:
throw
抛出异常信息,程序也会终止执行throw
后面跟的是错误提示信息Error
对象配合throw
使用,能够设置更详细的错误信息

<script>
function foo() {
try {
// 查找 DOM 节点
const p = document.querySelector('.p')
p.style.color = 'red'
} catch (error) {
// try 代码段中执行有错误时,会执行 catch 代码段
// 查看错误信息
console.log(error.message)
// 终止代码继续执行
return
}
finally {
alert('执行')
}
console.log('如果出现错误,我的语句不会执行')
}
foo()
</script>
总结:
try...catch
用于捕获错误信息将预估可能发生错误的代码写在
try
代码段中如果
try
代码段中出现错误后,会执行catch
代码段,并截获到错误信息

<script>
const obj = {
uname: '张三',
age: 18,
gender: '男',
gfs: ['凤姐', '芙蓉姐姐', '黄蓉'],
wife: {
w1: '蔡徐坤',
w2: 'ikun'
}
}
const arr = {}
function deepCopy(newObj, oldObj) {
debugger
//直接在这里进行断点调试
for (let k in oldObj) {
const item = oldObj[k]
if (item instanceof Array) {
newObj[k] = []
deepCopy(newObj[k], item)
} else if (item instanceof Object) {
newObj[k] = {}
deepCopy(newObj[k], item)
} else {
newObj[k] = item
}
}
}
deepCopy(arr, obj)
console.log(arr)
</script>
this
普通函数
普通函数的调用方式决定了 this
的值,即【谁调用 this
的值指向谁】
注: 普通函数没有明确调用者时 this
值为 window
,严格模式下没有调用者时 this
的值为 undefined
。
<script>
// 普通函数
function sayHi() {
console.log(this)
}
// 函数表达式
const sayHello = function () {
console.log(this)
}
// 函数的调用方式决定了 this 的值
sayHi() // window
window.sayHi()
// 普通对象
const user = {
name: '小明',
walk: function () {
console.log(this)
}
}
// 动态为 user 添加方法
user.sayHi = sayHi
uesr.sayHello = sayHello
// 函数调用方式,决定了 this 的值
user.sayHi()
user.sayHello()
</script>
箭头函数
箭头函数中的 this
与普通函数完全不同,也不受调用方式的影响,事实上箭头函数中并不存在 this
!箭头函数中访问的 this
不过是箭头函数所在作用域的 this
变量。
改变this
call
使用 call
方法调用函数,同时指定函数中 this
的值
<script>
// 普通函数
function sayHi() {
console.log(this);
}
let user = {
name: '小明',
age: 18
}
let student = {
name: '小红',
age: 16
}
// 调用函数并指定 this 的值
sayHi.call(user); // this 值为 user
sayHi.call(student); // this 值为 student
// 求和函数
function counter(x, y) {
return x + y;
}
// 调用 counter 函数,并传入参数
let result = counter.call(null, 5, 10);
console.log(result);
</script>
总结:
call
方法能够在调用函数的同时指定this
的值使用
call
方法调用函数时,第1个参数为this
指定的值call
方法的其余参数会依次自动传入函数做为函数的参数
apply
使用 apply 方法调用函数,同时指定函数中 this
的值
<script>
function f(x,y) {
console.log(this,x,y)
}
const obj = {
name: '订狄'
}
//函数名.apply(改变函数里面的对象)
//相同点:call()和apply() 都可以调用函数,也可以改变函数里面的this指向
//区别:传递的参数个数不一样,call() 可以传递多个参数, apply()只有两个参数, 第一个参数是,需要改变函数里面的对像,第二个参数必须是数组
//f.apply(obj)
f.apply(obj,[1,2,3])
const a = [1,2,3,4,5,6,7]
console.log(...a)
</script>
最大值案例
<script>
const arr = [1,2,3,45,5]
const res = Math.max.apply(Math,arr)
//const res = Math.max(...arr)
console.log(res)
</script>
总结:
apply
方法能够在调用函数的同时指定this
的值使用
apply
方法调用函数时,第1个参数为this
指定的值apply
方法第2个参数为数组,数组的单元值依次自动传入函数做为函数的参数
bind
方法并 不会调用函数,而是创建一个指定了 this
值的新函数

<script>
//语法: 函数名.bind(this指向,参数1,参数2,....)
//不能调用函数 可以改变函数里面this的指向 可以返回一个新的函数
function f(x, y, z) {
console.log(this, x, y, z)
}
const obj = {
name: 'hh',
age: 12
}
const ff = f.bind(obj, 10, 20, 30)
ff()
//bind 、call 、apply
//相同点:都可以改变函数里面 this 指向
//区别:
//1. bind不能调用函数 可以返回一个新的函数 call 、apply可以调用函数
//2. bind 第一个参数是用于改变函数里面的 this 指向 ,其他的参数 可以是 参数列表
//3. call 第一个参数是用于改变函数里面的 this 指向 ,其他的参数 可以是 参数列表
//4. apply 只有2个参数 第一个参数是用于改变函数里面的 this 指向 第二个参数是数组
</script>
注:bind
方法创建新的函数,与原函数的唯一的变化是改变了 this
的值。
