目錄:
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 }

