几个概念:栈堆,基本数据类型与引用数据类型
基本数据类型存放在栈中,引用类型存放在堆中。
- 基本数据类型: 字符串(String)、数字(Number)、布尔(Boolean)、对空(Null)、未定义(Undefined)、Symbol。
- 引用数据类型:对象(Object)、数组(Array)、函数(Function)。
什么是堆栈
堆栈都是一种数据项按序排列的数据结构,只能在一端(称为栈顶(top))对数据项进行插入和删除。
- 栈(stack)为自动分配的内存空间,它由系统自动释放;
- 堆(heap)则是动态分配的内存,大小不定也不会自动释放。
堆和栈的区别:
1,堆,队列优先,先进先出(FIFO—first in first out)
2,栈,先进后出(FILO—First-In/Last-Out)
什么是深拷贝、浅拷贝
1,是指拷贝一个对象时,不仅仅把对象的引用进行复制,还把该对象引用的值也一起拷贝
2,源对象与拷贝对象互相独立,其中任何一个对象的改动都不会对另外一个对象造成影响
深拷贝和浅拷贝简单解释
- 浅拷贝和深拷贝都只针对于引用数据类型,浅拷贝只复制指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存;但深拷贝会另外创造一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改到原对象;
- 区别:浅拷贝只复制对象的第一层属性、深拷贝可以对对象的属性进行递归复制
深拷贝常用方法
- 用 JSON.stringify 把对象转成字符串,再用 JSON.parse 把字符串转成新的对象(使用JSON)。
var obj1 = { body: { a: 10 } };
var obj2 = JSON.parse(JSON.stringify(obj1));
obj2.body.a = 20;
console.log(obj1);
// { body: { a: 10 } } <-- 沒被改到
console.log(obj2);
// { body: { a: 20 } }
console.log(obj1 === obj2);
// false
console.log(obj1.body === obj2.body);
// false
坏处:它会抛弃对象的constructor。也就是深拷贝之后,不管这个对象原来的构造函数是什么,在深拷贝之后都会变成Object。所以只适合 Number, String, Boolean, Array 的扁平对象
- 递归拷贝
function deepClone(obj) {
let objClone = Array.isArray(obj) ? [] : {};
if (obj && typeof obj === "object") {
for (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 obj1 = {
a: 1,
b: 2,
c: {
d: 3
}
}
var obj2 = deepClone(obj1);
obj2.a = 3;
obj2.c.d = 4;
alert(obj1.a); // 1
alert(obj2.a); // 3
alert(obj1.c.d); // 3
alert(obj2.c.d); // 4
- 使用ES6中的 Object.create()方法
function deepClone(initalObj, finalObj) {
var obj = finalObj || {};
for (var i in initalObj) {
var prop = initalObj[i]; // 避免相互引用对象导致死循环,如initalObj.a = initalObj的情况
if(prop === obj) {
continue;
}
if (typeof prop === 'object') {
obj[i] = (prop.constructor === Array) ? [] : Object.create(prop);
} else {
obj[i] = prop;
}
}
return obj;
}
- 热门的函数库lodash,也有提供_.cloneDeep用来做深拷贝。
var _ = require('lodash');
var obj1 = {
a: 1,
b: { f: { g: 1 } },
c: [1, 2, 3]
};
var obj2 = _.cloneDeep(obj1);
console.log(obj1.b.f === obj2.b.f);
// false
- JQ的extend方法
//$.extend( [deep ], target, object1 [, objectN ] )
//deep表示是否深拷贝,为true为深拷贝,为false,则为浅拷贝
//target Object类型 目标对象,其他对象的成员属性将被附加到该对象上。
//object1 objectN可选。 Object类型 第一个以及第N个被合并的对象。
let a=[0,1,[2,3],4],
b=$.extend(true,[],a);
a[0]=1;
a[2][0]=1;
console.log(a,b);
深拷贝应用场景:不对原数据对象造成影响
1、从服务器fetch到数据之后我将其存放在store中,通过props传递给界面,然后我需要对这堆数据进行修改,那涉及到修改就一定有保存和取消,所以我们需要将这堆数据拷贝到其他地方(网友的经历)
2、当你想使用某个对象的值,在修改时不想修改原对象,那么可以用深拷贝来弄一个新的内存对象。
浅拷贝常用方法
- 直接赋值(=)
let a = {
b:1,
m:'p'
}
const acl = a
- ES6的Object.assign()
let o ={name: {asd: '123'}}
let p = Object.assign({}, o)
p.name = '123456789'
console.log(p, o);//{name: "123456789"},{name: {asd: "123"}}
1、Object.assign()用于将所有可枚举属性的值从一个或多个源对象复制到目标对象。
2、Object.assign进行的拷贝是浅拷贝,及只有第一层是深拷贝。若拷贝的属性的值是对象的复合属性,就只是拷贝过来一个引用。
- ES6扩展运算符( ... )
let a={title:{info:"二级属性"}};
let b={...a,content:"一个教程"};
b.title.info = '二级属性都被修改了'
console.log(a.title);
console.log(b.title);
注意点
- slice()和concat()都并非深拷贝
// 对只有一级属性值的数组对象使用slice
var a = [1,2,3,4];
var b = a.slice();
b[0] = 2;
alert(a); // 1,2,3,4
alert(b); // 2,2,3,4
// 对有多层属性的数组对象使用slice
var a = [1,[1,2],3,4];
var b = a.slice();
b[1][0] = 2;
alert(a); // 1,2,2,3,4
alert(b); // 1,2,2,3,4
从上面两个例子可以看出,由于数组内部属性值为引用对象,因此使用slice和concat对对象数组的拷贝,整个拷贝还是浅拷贝,拷贝之后数组各个值的指针还是指向相同的存储地址。因此,slice和concat这两个方法,仅适用于对不包含引用对象的一维数组的深拷贝