目錄:
1、深拷貝與淺拷貝
2、淺拷貝實現
3、深拷貝實現
傳值與傳址
相關筆記:https://www.cnblogs.com/xiaoxuStudy/p/12185177.html
原始數據類型的拷貝是傳值,應用數據類型的拷貝是傳址。
深拷貝與淺拷貝
因為原始類型的拷貝是傳值,所以對於原始數據類型並沒有深淺拷貝的區別。深淺拷貝都是對於引用數據類型而言的。
深拷貝與淺拷貝的使用場景:1、都是復雜對象,即對象的屬性還是對象
如果要賦值對象的所有屬性都不是引用類型的時候,可以使用淺拷貝,遍歷並復制。
淺拷貝只復制一層對象,當對象的屬性是引用類型時,實質復制的是其引用,當引用值指向發生改變時,也會跟着改變。
使用 for-in
下面實現一個淺拷貝:
//實現淺拷貝 let shallowCopy = obj => { let rst = {}; //遍歷對象 for(let key in obj){ //只復制本身擁有的(非繼承過來的)枚舉屬性 if(obj.hasOwnProperty(key)){ rst[key] = obj[key]; } } return rst; } let star = { name:'虞書欣', age : 20, //又是一個對象 friend : { name : '孔雪兒' } } let otherStar = shallowCopy(star); otherStar.name = '劉雨昕'; otherStar.age = '22'; otherStar.friend.name = '金子涵'; console.log( star );
輸出:
{ name: '虞書欣', age: 20, friend: { name: '孔雪兒' } }
上面創建一個 shallowCopy 函數,傳入一個對象作為參數,該函數遍歷該對象將該對象的屬性復制到一個空對象 rst 中,最后返回 rst 對象。創建一個star 對象, 注意這里 star 對象有一個屬性 friend, friend 是引用類型,將 star 作為參數傳給 shallowCopy 函數,shallowCopy 函數返回一個 rst 對象,otherStar 指向 rst 對象。這里看一下修改 otherStar 會不會影響到 star 。修改 otherStar 的 name 屬性、age 屬性跟 friend 屬性的對象的 name 屬性。輸出 star 發現 friend 屬性改變了,name屬性跟age屬性都沒有變。
所以,要記得:淺拷貝只復制一層對象,當對象的屬性是引用類型時,實質復制的是其引用,當引用值指向發生改變時,也會跟着改變。
用 Object.assign() 拷貝也是一樣。
使用 Object.assign( )
let xiaoxu = { name:'小許', info:{ gender:'女', hobby:'sleep' } } let a = Object.assign( {}, xiaoxu ); a.name = "nana"; console.log( xiaoxu.name ); //沒變 //輸出:小許 a.info.gender = '男'; console.log( xiaoxu.info.gender ); //變了 //輸出:男
使用對象的擴展運算符
擴展運算符的 value 是原始數據類型的時候,是深拷貝。當 value 是引用類型的時候,是淺拷貝。
let xiaoxu = { name:'小許', info:{ gender:'女', hobby:'sleep' } } let a = { ...xiaoxu }; a.name = "nana"; console.log( xiaoxu.name ); //沒變 //輸出:小許 a.info.gender = '男'; console.log( xiaoxu.info.gender ); //變了 //輸出:男
深復制遞歸復制了所有層級。
使用 JSON.stringify()
注意:如果需要拷貝的是純的JSON數據,不需要循環引用,可以使用 JSON.stringify 實現。
let obj = { name : '小明', songs : ['想見你想見你想見你', '暮陽少年'] } let obj1 = JSON.parse(JSON.stringify(obj)); obj1.name = '小華'; obj1.songs[0] = '飄'; let girl = [{ name : '小許', colors : ['black','pink'], fn : function(){}, age : undefined }] let boy = JSON.parse(JSON.stringify(girl)); console.log(boy); //輸出:[ { name: '小許', colors: [ 'black', 'pink' ] } ]
使用遞歸
let deepClone = obj => { let newObj = Array.isArray(obj) ? [] : {}; if( obj && typeof obj === 'object' ){ for( let key in obj ){ if( obj.hasOwnProperty(key) ){ //如果對象的屬性是引用類型 if( obj[key] && typeof obj[key] ){ newObj[key] = deepClone(obj[key]); }else{ //如果對象的屬性不是引用類型,直接拷貝 newObj[key] = obj[key]; } } } } return newObj; } let xiaoxu = { name :'xiaoxu', idols : ['虞書欣','劉雨昕'], fn : function(){}, age : undefined } let girl = deepClone(xiaoxu); girl.name = 'nana'; girl.idols = ['林宥嘉','JonyJ']; girl.fn = '親一口=3='; girl.age = 20; console.log(xiaoxu); console.log(girl);
輸出:
混合模式 Mixin
不通過繼承去擴展方法
let mixin = {
say(){
console.log(`${this.name}在說話`);
},
sing(){
console.log(`${this.name}在唱歌`);
},
run(){
console.log(`${this.name}在跑步`);
}
}
class Student{
constructor(name){
this.name = name;
}
}
Object.assign( Student.prototype, mixin );
let student = new Student('小許');
student.sing(); //輸出:小許在唱歌
為什么要把 mixin 復制到 prototype 上?因為這樣子維護性好且減少內存占用。
Vue 的混入(Mixin)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="vue.js"></script>
</head>
<body>
<div id="app">
<h1>{{name}}..{{age}}</h1>
</div>
<script>
Vue.mixin({
data(){
return{
name:'小許'
}
},
methods:{
say(){
console.log('hello');
}
}
})
new Vue({
el:'#app',
data(){
return{
age:22
}
},
mounted(){
this.say();
}
})
</script>
</body>
</html>
使用 pick
先使用命令 yarn add -D underscore 安裝 underscore 依賴
const _ = require(`underscore`); //引進underscore let obj = { name : '小許', age : 22 } //返回一個 obj 的副本 let age = _.pick(obj, 'age'); console.log( age ); //輸出:{ age: 22 } console.log( obj ); //輸出:{ name: '小許', age: 22 }