Number、Boolean、String、undefined、Null。
变量是直接按值存放的,存放在栈内存中的简单数据段,可以直接访问。
Function,Array,Object------技术对象系列,typeof()这个三种类型得到的都是object
存放在堆内存中的对象,变量保存的是一个指针,这个指针指向另一个位置。当需要访问引用类型的值时,首先从栈中获得该对象的地址指针,然后再从堆内存中取得所需的数据。
基本类型都放在栈(图左)(stack)里
引用类型都放在堆(图右)(heap)里
通常我们拷贝一个变量的值给另一个变量时,会直接使用"="赋值。
但是,当我们拷贝一个引用类型时,经常会出现如下情况:
let arr1 = [1,2,3];
let arr2 = arr1;
console.log(arr1); // [1,2,3]
console.log(arr2); // [1,2,3]
arr1[2] = 4;
console.log(arr1); // [1,2,4]
console.log(arr2); // [1,2,4]
arr2[2] = 5;
console.log(arr1); // [1,2,5]
console.log(arr2); // [1,2,5]
我们发现,只要改变一个变量的值,另一个变量的值也会改变。这是因为两个变量的值指向同一个堆内存的数据,使用‘=’赋值之只是将一个变量对象的引用(指针)赋值给另一个变量。
基本类型变量的值都存储在栈(stack)中,所以浅拷贝与深拷贝只针对引用类型有所区分。
浅拷贝:只是复制了对象的引用地址,没有两个对象指向同一个内存地址,所以修改其中任意的值,另一个值都会随之变化。
也就是拷贝对象里面的数据,但是没有完全将子对象的数据全部拷贝。
1.引用复制(遍历拷贝-拷贝一层)
//对象遍历拷贝
function clone(copyObj) {
let obj = {};
for ( let i in copyObj) {
obj[i] = copyObj[i]; //使用‘=’直接赋值,没有深度拷贝,子对象可能是object
}
return obj;
}
let x = {
a: 1,
b: { d: 1 },
c: [ 1, 2, 3 ]
};
let y = clone(x);
x.a = 2;
x.b.d = 2;
x.c[0] = 2;
console.log(x.a); // 2
console.log(y.a); // 1
console.log(x.b); // {d: 2}
console.log(y.b); // {d: 2}
console.log(x.c); // [ 2, 2, 3 ]
console.log(y.c); // [ 2, 2, 3 ]
//数组遍历拷贝
function copy (array) {
let newArray = []
for(let item of array) {
newArray.push(item);
}
return newArray;
}
let arr1 = [1,{a:1},[ 1, 2, 3 ]];
let arr2 = copy(arr1);
arr1[0]= 2;
arr1[1].a= 2;
arr1[2][1]= 2;
console.log(arr1[0]); // 2
console.log(arr2[0]); // 1
console.log(arr1[1]); // {a: 2}
console.log(arr2[1]); // {a: 2}
console.log(arr1[2]); // [ 2, 2, 3 ]
console.log(arr2[2]); // [ 2, 2, 3 ]
2.Object.assign() (对象浅拷贝-拷贝一层)
Object.assign():用于对象的合并,将源对象的所有可枚举属性,复制到目标对象,并返回合并后的target
用法: Object.assign(target, source1, source2);
let x = {
a: 1,
b: {d: 1},
c: [ 1, 2, 3 ]
};
let y = Object.assign({}, x);
console.log(x.a); // 1
console.log(y.a); // 1
console.log(x.b); // {d: 1}
console.log(y.b); // {d: 1}
console.log(x.c); // [ 1, 2, 3 ]
console.log(y.c); // [ 1, 2, 3 ]
x.a = 2;
x.b.d = 2;
x.c[0] = 2;
console.log(x.a); // 2
console.log(y.a); // 1
console.log(x.b); // {d: 2}
console.log(y.b); // {d: 2}
console.log(x.c); // [ 2, 2, 3 ]
console.log(y.c); // [ 2, 2, 3 ]
3.slice() 和 concat() (数组浅拷贝-拷贝一层)
(1) slice() 方法返回一个从已有的数组中截取一部分元素片段组成的新数组(不改变原来的数组!)
用法:array.slice(start,end) start表示是起始元素的下标,end表示的是终止元素的下标
当slice()不带任何参数的时候,默认返回一个长度和原数组相同的新数组
let arr1 = [1,{a:'a'},[ 1, 2, 3 ]];
let arr2 = arr1.slice();
console.log(x.a); // 1
console.log(y.a); // 1
console.log(x.b); // {d: 1}
console.log(y.b); // {d: 1}
console.log(x.c); // [ 1, 2, 3 ]
console.log(y.c); // [ 1, 2, 3 ]
x.a = 2;
x.b.d = 2;
x.c[0] = 2;
console.log(x.a); // 2
console.log(y.a); // 1
console.log(x.b); // {d: 2}
console.log(y.b); // {d: 2}
console.log(x.c); // [ 2, 2, 3 ]
console.log(y.c); // [ 2, 2, 3 ]
(2)concat() 方法用于连接两个或多个数组。( 该方法不会改变现有的数组,而仅仅会返回被连接数组的一个副本。)
用法:array.concat(array1,array2,......,arrayN)
当concat()不带任何参数的时候,默认参数为空数组
let arr1 = [1,{a:'a'},[ 1, 2, 3 ]];
let arr2 = arr1.concat();
console.log(x.a); // 1
console.log(y.a); // 1
console.log(x.b); // {d: 1}
console.log(y.b); // {d: 1}
console.log(x.c); // [ 1, 2, 3 ]
console.log(y.c); // [ 1, 2, 3 ]
x.a = 2;
x.b.d = 2;
x.c[0] = 2;
console.log(x.a); // 2
console.log(y.a); // 1
console.log(x.b); // {d: 2}
console.log(y.b); // {d: 2}
console.log(x.c); // [ 2, 2, 3 ]
console.log(y.c); // [ 2, 2, 3 ]
4.扩展运算符(...)(扩展运算符浅拷贝-拷贝一层)
// 对象使用(...)扩展运算符
let x = {
a: 1,
b: {d: 1},
c: [ 1, 2, 3 ]
};
let y = {...x};
console.log(x.a); // 1
console.log(y.a); // 1
console.log(x.b); // {d: 1}
console.log(y.b); // {d: 1}
console.log(x.c); // [ 1, 2, 3 ]
console.log(y.c); // [ 1, 2, 3 ]
x.a = 2;
x.b.d = 2;
x.c[0] = 2;
console.log(x.a); // 2
console.log(y.a); // 1
console.log(x.b); // {d: 2}
console.log(y.b); // {d: 2}
console.log(x.c); // [ 2, 2, 3 ]
console.log(y.c); // [ 2, 2, 3 ]
// 数组使用(...)扩展运算符
let arr1 = [1,{a:'a'},[ 1, 2, 3 ]];
let arr2 = [...arr1];
console.log(x.a); // 1
console.log(y.a); // 1
console.log(x.b); // {d: 1}
console.log(y.b); // {d: 1}
console.log(x.c); // [ 1, 2, 3 ]
console.log(y.c); // [ 1, 2, 3 ]
x.a = 2;
x.b.d = 2;
x.c[0] = 2;
console.log(x.a); // 2
console.log(y.a); // 1
console.log(x.b); // {d: 2}
console.log(y.b); // {d: 2}
console.log(x.c); // [ 2, 2, 3 ]
console.log(y.c); // [ 2, 2, 3 ]
深拷贝:在堆内存中开辟了一块内存地址用于存放复制的对象,并且将原对象的所有层级的数据都拷贝给另一个对象,形成两个独立的对象。也就是2个不同的内存地址,所以修改其中任意的值,另一个值都不会变化。
1.JSON对象的parse和stringify
JSON.stringify()是目前前端开发过程中最常用的深拷贝方式,原理是把一个对象序列化成为一个JSON字符串,将对象的内容转换成字符串的形式再保存在磁盘上,再用JSON.parse()反序列化将JSON字符串变成一个新的对象。
JSON对象是ES5中引入的新的类型(支持的浏览器为IE8+)
通过JSON.stringify实现深拷贝有几点要注意
function Obj() {
this.func = function () {
alert(1)
};
this.obj = {a:1};
this.arr = [1,2,3];
this.und = undefined;
this.reg = /123/;
this.date = new Date(0);
this.NaN = NaN
this.infinity = Infinity
this.sym = Symbol(1)
}
var obj1 = new Obj();
Object.defineProperty(obj1,'innumerable',{
enumerable:false,
value:'innumerable'
})
console.log('obj1',obj1);
var str = JSON.stringify(obj1);
var obj2 = JSON.parse(str);
console.log('obj2',obj2);
拷贝信息如下: