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