浅拷贝和深拷贝
首先,大家需要区分,赋值不属于拷贝。
基础类型赋值是值复制,复制后会有独立的内存空间;
引用类型赋值是引用复制,复制后共享同一个内存空间。
let a = 1
let b = a
b = 2
console.log(a) // 1
let arr = [1, [2, 3]]
let newArr = arr
console.log(arr[0] === newArr[0]) // true
console.log(arr[1] === newArr[1]) // true
newArr[0] = 11
console.log(arr[0]) // 11
newArr[1] = 2
console.log(arr[1]) // 2
浅拷贝和深拷贝都是针对对象的,不是针对基础类型的。
浅拷贝
-
如果原始对象的属性是基本类型,拷贝的就是基本类型的值,会有独立的内存空间
-
如果原始对象的属性是引用类型(对象、数组等),拷贝的就是内存地址,也就是说原始对象和新对象的这个属性指向同一个内存地址,那么该属性被修改就会影响另一个对象
-
浅拷贝的方法有
-
扩展运算符
-
Object.assign()
-
// 例一:
let arr = [1, [2, 3]]
let newArr = [...arr] // [1, [2, 3]] 浅拷贝
console.log(arr === newArr) // false
console.log(arr[0] === newArr[0]) // true 因为基础类型是值比较
console.log(arr[1] === newArr[1]) // true 因为引用类型公用一个内存地址
// 修改基础类型
newArr[0] = 11
console.log(newArr) // [11, [2, 3]]
console.log(arr) // [1, [2, 3]] 原对象没有跟着变
// 修改引用类型
newArr[1][0] = 22
console.log(newArr) // [11, [22, 3]]
console.log(arr) // [1, [22, 3]] 原对象跟着变了
// -----分割线-----
// 例二:
let arr = [1, [2, 3]]
let newArr = [...arr]
newArr[1] = [22, 33]
console.log(newArr) // [1, [22, 33]]
console.log(arr) // [1, [2, 3]]
/*
这里的 arr 为什么没有跟着变化呢?
因为在执行 newArr[1] = [22, 33] 的时候,
实际上是修改的 newArr[1] 对象的引用,即它指向了一个新的引用对象,
这与例一不同,
例一修改的不是对象的引用,修改的是引用对象的内部的数据
*/
深拷贝
深拷贝不仅复制对象本身,还递归复制对象的引用类型的属性所指向的对象,直到那些对象都是基本类型为止。这意味着深拷贝后的对象和原始对象是完全独立的,修改其中一个对象不会影响到另一个对象。
对于一些简单的对象可以使用 JSON.parse(JSON.stringify(obj)) 方法来实现深拷贝,但是如果对象中包含 undefined、Symbol、Date、Map、Set 等类型的数据时就会出现问题。这时候就需要自己封装方法来实现深拷贝。
这里给出一个深拷贝封装的函数:
export function deepClone(source) {
if (!source && typeof source !== 'object') {
throw new Error('error arguments', 'deepClone')
}
const targetObj = source.constructor === Array ? [] : {}
Object.keys(source).forEach(keys => {
if (source[keys] && typeof source[keys] === 'object') {
targetObj[keys] = deepClone(source[keys])
} else {
targetObj[keys] = source[keys]
}
})
return targetObj
}
扩展运算符
ES6 新特性,主要用于数组扩展和对象扩展
数组扩展
computed: {
test() {
{ label: 'a', value: '1'},
this.add
{ label: 'b', value: '2'},
}
},
data() {
return {
add: []
}
},
created() {
this.add = [
{ label: 'c', value: '3'},
{ label: 'd', value: '4'},
]
console.log(this.test) // 打印出的数组并不是连续的对象数组,而是中间有一段是Array
}
正确的写法是运用扩展运算符
computed: {
test() {
{ label: 'a', value: '1'},
...this.add
{ label: 'b', value: '2'},
}
},
// ...add 扩展运算符
对象扩展
let t1 = {
a: '1',
b: '2'
}
let t2 = {
c: '3',
d: '4',
e: [1,2],
f: {f1: '1', f2: '2'}
}
let t3 = {...t1, ...t2}
console.log("t3:", t3);
注意:扩展运算符是浅拷贝。
其他
获取数组的最大最小值
var arr = [22, 13, 6, 55, 30];
console.log(Math.max(...arr)); // 55
console.log(Math.min(...arr)); // 6
console.log(Math.min(arr)); // NaN
解构赋值
解构赋值 ES6 新增,允许你从数组或对象中提取数据,并将其赋值给声明的变量。这种语法提供了一种非常方便的方式来处理数组或对象的属性/元素,使得代码更加简洁和易读。
// 数组解构
const arr = [1, 2, 3, 4]
const [first, second] = arr // 取的数组的第一个和第二个,数组解构要按顺序
console.log(first) // 1
// 对象解构
const usr = {
name: '小李',
age: 20,
num: 1
}
const {age, name} = usr // 对象解构不按顺序按属性
console.log(name) // 小李
// 剩余参数(...)
let [a, ...rest] = [1, 2, 3, 4, 5];
console.log(a); // 1
console.log(rest); // [2, 3, 4, 5]
// 取别名
let { foo: baz } = { foo: 'hello' };
console.log(baz); // 输出: hello
// 函数参数解构
function person({ name, age }) {
console.log(name); // 张三
console.log(age); // 10
}
person({ name: '张三', age: 10 });
注意:解构赋值得到的对象变量实际上是原对象的一个引用,而不是对象的一个拷贝,这与浅拷贝类似。
一个综合示例:
// bad
const oirginal = { a: 1, b: 2 }
const copy = Object.assign(original, { c: 3 })
delete copy.a
// good
const oirginal = { a: 1, b: 2 }
const copy = { ...original, c: 3 } // copy => { a: 1, b: 2, c: 3}
const { a, ...noA } = copy // noA => { b: 2, c: 3}