深拷貝與淺拷貝是針對(duì)對(duì)象屬性為對(duì)象的阳堕,因?yàn)榛緮?shù)據(jù)類型在進(jìn)行賦值操作時(shí)(也就是深拷貝(值拷貝))哩俭,是直接將值賦給了新的變量桦踊,也就是該變量是原變量的一個(gè)副本绞蹦,這時(shí)力奋,你修改兩個(gè)中的任意一個(gè)都不會(huì)影響另一個(gè);而對(duì)于對(duì)象或引用數(shù)據(jù)在進(jìn)行淺拷貝時(shí)坦辟,只是將對(duì)象的引用復(fù)制了一份刊侯,也就是內(nèi)存地址,即兩個(gè)不同的變量指向了同一個(gè)內(nèi)存地址锉走,那么在改變?nèi)我庖粋€(gè)變量的值都是改變內(nèi)存地址所存儲(chǔ)的值滨彻,因此兩個(gè)變量的值都會(huì)改變。
js中的深拷貝(值拷貝):
js 中的基本數(shù)據(jù)類型:String挪蹭,Number亭饵,Boolean,Null梁厉,Undefined辜羊,在賦值的過(guò)程中都是值拷貝。
例如词顾,let a = 10; b = a 八秃,修改其中一個(gè)變量的值,不會(huì)影響到另一個(gè)變量的值肉盹。js 中的淺拷貝(引用拷貝)
js 中的對(duì)象數(shù)據(jù)類型:Object昔驱,Array,F(xiàn)unction上忍,Map骤肛,Set,在賦值過(guò)程中都是引用拷貝窍蓝。
let obj = {
name:'小明',
age: 18
}
let obj2 = obj
obj2.name = '小紅';
console.log(obj) // {name:'小紅',age: 18}
console.log(obj2) // {name:'小紅',age: 18}
當(dāng)修改obj2的name屬性時(shí)腋颠,也會(huì)修改obj的name,因?yàn)樗麄冎赶蛲粔K內(nèi)存地址吓笙。
- 將淺拷貝轉(zhuǎn)換為深拷貝
在實(shí)際的項(xiàng)目開(kāi)發(fā)中淑玫,我們?cè)诙鄶?shù)情況下不希望將對(duì)象進(jìn)行淺拷貝,因?yàn)橹禃?huì)相互影響,容易出錯(cuò)混移。
3.1 Array的深拷貝
- 通過(guò)slice方法
slice() 操作數(shù)組時(shí)祠墅,不會(huì)對(duì)原數(shù)組有影響,會(huì)產(chǎn)出一個(gè)新的數(shù)組歌径。
let arr1 = [1,72,5,6]
let arr2 = arr1.slice()
arr2[0] = 100
console.log(arr1) // [1,72,5,6]
console.log(arr2) // [100,72,5,6]
數(shù)組arr2的改變未引起arr1的變化毁嗦。
- 通過(guò)concat方法
數(shù)組的concat() 方法,能夠連接兩個(gè)數(shù)組回铛,同樣不會(huì)改變?cè)瓉?lái)的數(shù)組狗准。
用一個(gè)空數(shù)組[ ] 連接另一個(gè)數(shù)組,即可實(shí)現(xiàn)深拷貝茵肃。
let arr3 = ['cat','dog','pig']
let arr4 = [].concat(arr3)
arr3[2] = 'big pig'
console.log(arr3) // ['cat','dog','big pig']
console.log(arr4) // ['cat','dog','pig']
- 通過(guò)ES6語(yǔ)法中的 …
ES6中的 … 腔长,我們經(jīng)常在數(shù)組的深拷貝中用到。
let arr5 = [0,0,1]
let arr6 = [...arr5]
arr5[0] = 10000
console.log(arr5) // [10000,0,1]
console.log(arr6) // [0,0,1]
- 通過(guò)Array.from 方法
Array.from() 方法能從一個(gè)類似數(shù)組或可迭代對(duì)象中返回一個(gè)新的數(shù)組實(shí)例验残。通過(guò)Array.from() 方法能獲取到一個(gè)數(shù)組的深拷貝捞附。
let arr7 = [1,2,3]
let arr8 = Array.from(arr7)
arr7[1] = 1000
console.log(arr7) // [1,1000,3]
console.log(arr8) // [1,2,3]
3.2 Object 的深拷貝
- 通過(guò)Object.assign() 方法
ES6的Object.assign(target,…sources) 用于對(duì)象的合并,將源對(duì)象的所有可枚舉屬性您没,復(fù)制到目標(biāo)對(duì)象中鸟召,并返回合并后的目標(biāo)對(duì)象。后來(lái)的源對(duì)象的屬性值氨鹏,將會(huì)覆蓋它之前的對(duì)象的屬性欧募。
let person = {
name: 'xia',
age: 25,
height: 160
}
let otherPerson = Object.assign({},person)
person.age = 30
console.log(person) // {name: "xia", age: 30, height: 160}
console.log(otherPerson) // {name: "xia", age: 25, height: 160}
- 萬(wàn)能轉(zhuǎn)換器(對(duì)Array和Object)
前面講了Array和Object的深拷貝方法,但是對(duì)于有更深層次的結(jié)構(gòu)關(guān)系(數(shù)組套數(shù)組仆抵,數(shù)組套對(duì)象跟继,對(duì)象套對(duì)象等),上面的方法就失靈了镣丑,可以看下面的例子舔糖。
let personArr = [{name: 'xia'}, {name: 'zhang'}]
let otherPersonArr2 = [...personArr]
personArr[0].name = 'xia xia'
console.log(personArr)
console.log(otherPersonArr2)
萬(wàn)能轉(zhuǎn)換器JSON.parse(JSON.stringify(obj)) 深拷貝已對(duì)象,它可以深拷貝多層級(jí)的莺匠,不同擔(dān)心嵌套問(wèn)題剩盒。
JSON.stringify() 將對(duì)象序列化成json對(duì)象
JSON.parse() 反序列化——將json對(duì)象反序列化成js對(duì)象
JSON.stringify(obj) 將js中的對(duì)象轉(zhuǎn)化為json字符串
let jack = {
name: 'jack'
}
console.log(jack)
console.log(JSON.stringify(jack))
它們?cè)诟袷缴嫌袇^(qū)別。下圖中的第一個(gè)是對(duì)象慨蛙,name沒(méi)有雙引號(hào)括起來(lái)。第二個(gè)是json字符串纪挎,其中期贫,name用雙引號(hào)括起來(lái)了。
JSON.parse() 將json字符串解析成對(duì)象
let obj = {
name: '小明'
}
console.log('obj: ', obj)
console.log('json string: ', JSON.stringify(obj))
let str = JSON.stringify(obj)
console.log('--------------')
console.log(str)
console.log('str to obj: ', JSON.parse(str))
- Vue中的淺拷貝和深拷貝
兩個(gè)button-counter 共用同一個(gè)jack對(duì)象异袄,用同一塊地址通砍,當(dāng)其中一個(gè)實(shí)例改變時(shí),會(huì)影響另一個(gè)實(shí)例的值。(淺拷貝)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>vue的data選項(xiàng)</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<button-counter></button-counter>
<button-counter></button-counter>
</div>
<script>
let jack = {
counter: 0
}
// 子組件
Vue.component('button-counter', {
data() {
// 函數(shù)類型
return jack
},
template: `<button @click="counter++">click {{counter}} times</button>`
})
let vm = new Vue({
el: '#app' // mount到DOM上
})
</script>
</body>
</html>
采用深拷貝封孙,重新創(chuàng)建一塊內(nèi)存迹冤。這樣,vue的button-counter組件中的counter值互不影響虎忌。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>vue的data選項(xiàng)</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<button-counter></button-counter>
<button-counter></button-counter>
</div>
<script>
let jack = {
counter: 0
}
// 子組件
Vue.component('button-counter', {
data() {
// 函數(shù)類型
return JSON.parse(JSON.stringify(jack))
},
template: `<button @click="counter++">click {{counter}} times</button>`
})
let vm = new Vue({
el: '#app' // mount到DOM上
})
</script>
</body>
</html>
``