1.1 栈堆、基本数据类型、引用数据类型
栈堆:存放数据的地方
基本数据类型:number,string,boolean,null,undefined
引用数据类型:function,Array,object
基本数据类型保存在栈内存,引用类型保存在堆内存中。根本原因在于保存在栈内存的必须是大小固定的数据,引用类型的大小不固定,只能保存在堆内存中,但是可以把它的地址写在栈内存中以供访问。
1.2 赋值
将某一对象赋给某个变量的过程,称为赋值。
当将一个对象赋值给另外一个新的变量时,赋的其实是该对象在栈中的地址,而不是堆中的数据。也就是说,两个对象同时指向一个存储空间,是联动的,无论修改哪个对象都会影响另一个对象(针对引用数据类型)。
如下例:将a赋值给c,无论是修改a,还是修改c,两者都发生变化
var a=[1,2,3,4,5];
var c=a
console.log(a)//[1, 2, 3, 4, 5]
console.log(c)//[1, 2, 3, 4, 5]
c[0]=8
console.log(a)//[8, 2, 3, 4, 5]
console.log(c)//[8, 2, 3, 4, 5]
a[0]=10
console.log(a)//[10, 2, 3, 4, 5]
console.log(c)//[10, 2, 3, 4, 5]
1.3 浅拷贝
创建一个新对象,这个对象有着原始对象属性值的一份精准拷贝,如果属性是基本类型,拷贝的就是基本类型的值;如果属性是引用类型,拷贝的就是内存地址,如果其中一个对象改变了这个地址,就会影响到另一个对象(属性是引用类型的情况)。
浅拷贝会重新在堆中创建内存,拷贝前后对象的基本数据类型互不影响,但拷贝前后对象的引用类型因共享同一块内存,会相互影响。
浅拷贝与赋值的区别:浅拷贝创建了一个新的对象,指向一个新的房间,赋值是没有的。
//-------方法1-------
function shallowClone(source){
if(!source || typeof source !== 'object'){
return source;
}
let newObj = source.constructor=== Array ?[]:{};
//for in 循环遍历一个对象的可枚举属性,包括这个对象继承自原型链上的可枚举属性
for(var i in source){
if(source.hasOwnProperty(i)){
newObj[i]=source[i]
}
}
return newObj;
}
var person = {'name':'张三','age':[1,2,3,4,5]}
var person1 = shallowClone(person)
//name为基本数据类型
console.log(person.name)//张三
console.log(person1.name)//张三
person1.name="李四";
console.log(person.name)//张三
console.log(person1.name)//李四
person.name='王五';
console.log(person.name)//王五
console.log(person1.name)//李四
//age为引用数据类型,改变一个,影响另一个
console.log(person.age)//[1,2,3,4,5]
console.log(person1.age)//[1,2,3,4,5]
person1.age[0]=6;
console.log(person.age)//[6,2,3,4,5]
console.log(person1.age)//[6,2,3,4,5]
person.age[0]=7;
console.log(person.age)//[7,2,3,4,5]
console.log(person1.age)//[7,2,3,4,5]
浅拷贝实现方式:
JavaScript浅拷贝与深拷贝的几种方法。_喜陈的博客-CSDN博客_js深拷贝和浅拷贝的方法
let person = {'name':'张三','age':[1,2,3,4,5]}
let arr=['one','two',['three'],{name:'Bob'}]
// 1.Object.assign()
let newObj1 = Object.assign({}, person);
// 2.展开运算符...
let newObj2 = {...person};
// 3.array.prototype.concat()
let newObj3 = arr.concat();
// 4.array.prototype.slice()
let newObj4 = arr.slice();
//测试
arr[0] = '一'
arr[2][0] = '三'
arr[3].name = 'Jack'
console.log(arr);// ['一', 'two',['三'],{name: 'Jack'}]
console.log(newObj4);// ['one', 'two',['三'],{name: 'Jack'}]
问题:下例中也是对引用数据类型的修改,为什么不互相影响?
var person = ['张三',[1,2,3,4,5]]
var person1=person.concat()
person1[1]=[1,2,2];
console.log(person[1])//[1,2,3,4,5]
console.log(person1[1])//[1,2,2]
person[1]=[4,5,6];
console.log(person[1])//[4,5,6]
console.log(person1[1])//[1,2,2]
因为person1[1]存储的指针变了,而person[1]的指针没有变,这是直接替换了指针,并不是通过指针修改数据。引用数据类型的地址和值不存在一起,而是通过指针从地址指向值,person1[1]整体变化了,就是指针的指向变了。
1.4 深拷贝
深拷贝就是将一个对象从内存中完整的拷贝一份出来,从堆内存中开辟一个新的区域存放新对象,且修改新对象不会影响原有的对象。(意思就是a,b指向不同的堆)
深拷贝就不会像浅拷贝那样只拷贝一层,而是有多少层就拷贝多少层,真正的做到全部内容都放在自己新开辟的内存里,完成拷贝后不论如何修改都不会改变原有对象。可以利用递归思想实现深拷贝。
方法1:JSON.parse(JSON.stringify())
原理:用JSON.stringify将对象转成JSON字符串,再用JSON.parse()把字符串解析成对象,一去一来,新的对象产生了,而且对象会开辟新的栈,实现深拷贝。这种方法虽然可以实现数组或对象深拷贝,但不能处理函数这是因为JSON.stringify() 方法是将一个JavaScript值(对象或者数组)转换为一个 JSON字符串,不能接受函数。
function replace(key,value){
if(typeof value==='function'){
return value.toString()
}
if(typeof value==='undefined'){
return 'undefined'
}
return value
}
var arr1 = ['red','green'];
var arr2 = JSON.parse(JSON.stringify(arr1));//复制
var arr3 = [1,2,3,['1','2','3']];
var arr4 = JSON.parse(JSON.stringify(arr3));//复制
var arr5={'look':'handsome','eat':function (){console.log("吃胖")},"xxx":undefined}
var arr6 = JSON.parse(JSON.stringify(arr5,replace));//复制
console.log(arr2,"------",arr4,"------",arr5)//['red','green'];
arr1.push('black') ;//改变color1的值
arr3[3][0]=0;
console.log(arr2,"------",arr4,"------",arr6)//["red", "green"]
console.log(arr1,"------",arr3)//["red", "green", "black"]
方法2:递归实现深拷贝
function deepClone(obj){
if(!obj || typeof obj !== 'object'){
return obj;
}
//判断参数是不是一个对象
//两种方法
let objClone = obj.constructor=== Array ?[]:{};
// let objClone = Array.isArray(obj)?[]:{};
if(obj && typeof obj==="object"){
for(const key in obj){
if(obj.hasOwnProperty(key)){//检测是否有这个属性
//判断ojb子元素是否为对象,如果是,递归复制
if(obj[key]&&typeof obj[key] ==="object"){
objClone[key] = deepClone(obj[key]);
}else{
//如果不是,简单复制
objClone[key] = obj[key];
}
}
}
}
return objClone;
}
//深拷贝
var arr2 = {
name: '测试深拷贝',
age: 20,
address: ['沈阳', '合肥'],
friends: {
friend1: '张三',
friend2: '李四'
},
function(){
console.log("我是深拷贝的对象")
}
}
let b2=deepClone(arr2)
console.log("深拷贝b2:",b2)
b2.address[0]="深拷贝"
console.log("修改深拷贝b2:",b2)
console.log("原数组:",arr2)//原数组数据没变,不影响原数组
1.5 总结
深拷贝和浅拷贝是针对复杂数据类型来说的,浅拷贝只拷贝一层,而深拷贝是层层拷贝。