堆和栈的区别
其实深拷贝和浅拷贝的主要区别就是其在内存中的存储类型不同。
堆和栈都是内存中划分出来用来存储的区域。
栈(stack)为自动分配的内存空间,它由系统自动释放; 而堆(heap)则是动态分配的内存,大小不定也不会自动释放。
//1
var a = 10;
b = a;
b += 20;
alert(a); //10
//2
var obj = {
name : "jack"
};
b = obj;
b.name = "Lily";
alert( obj.name ); //Lily
//3
var arr = [1,2,3];
brr = arr;
brr[0] = 10;
alert( arr[0] ); //10
//4
var a = 20;
function fun(a){
a += 30;
}
fun( a );
alert( a ); // 20
//5
var arr = [1,2,3];
function fun(brr){
brr[0] = 20;
}
fun(arr);
alert(arr[0]); // 20
function test(person) {
person = {
name: 'yyy',
age: 30
}
person.age = 26
return person
}
const p1 = {
name: 'yck',
age: 25
}
const p2 = test(p1)
console.log(p1) // -> ? // { name: 'yck',age: 26}
console.log(p2) // -> ? // person = { name: 'yyy',age: 30}
总结: JS中的基本类型按值传递,对象类型按共享传递的,共享传递和按引用传递的不同在于:在共享传递中对函数形参的赋值,不会影响实参的值
ECMAScript 的数据类型
重新回顾一下 ECMAScript 中的数据类型,主要分为
(1)基本数据类型
基本数据类型主要是:undefined,boolean,number,string,null,Symbol(es6新增)。
** 基本数据类型存放在栈中** 存放在栈内存中的简单数据段,数据大小确定,内存空间大小可以分配,是直接按值存放的,所以可以直接访问。
基本数据类型值不可变
基本数据类型的值是不可变的,动态修改了基本数据类型的值,它的原始值也是不会改变的,例如:
var str = "abc";
console.log(str[1]="f"); // f
console.log(str); // abc
在 js 中没有方法是可以改变布尔值和数字的。倒是有很多操作字符串的方法,但是这些方法都是返回一个新的字符串,并没有改变其原有的数据。
所以,记住这一点:基本数据类型值不可变。
基本类型的比较是值的比较
基本类型的比较是值的比较,只要它们的值相等就认为他们是相等的。
(2)引用类型
引用类型存放在堆中
引用类型(object)是存放在堆内存中的,变量实际上是一个存放在栈内存的指针,这个指针指向堆内存中的地址。每个空间大小不一样,要根据情况开进行特定的分配。
引用类型值可变
引用类型是可以直接改变其值的,例如:
var a = [1,2,3];
a[1] = 5;
console.log(a[1]); // 5
引用类型的比较是引用的比较
所以每次我们对 js 中的引用类型进行操作的时候,都是操作其对象的引用(保存在栈内存中的指针),所以比较两个引用类型,是看其的引用是否指向同一个对象。例如:
var a = [1,2,3];
var b = [1,2,3];
console.log(a === b); // false
虽然变量 a 和变量 b 都是表示一个内容为 1,2,3 的数组,但是其在内存中的位置不一样,也就是说变量 a 和变量 b 指向的不是同一个对象,所以他们是不相等的。
传值与传址
基本数据类型的赋值 是传值,所以基本类型的赋值的两个变量是两个独立相互不影响的变量。
引用类型的赋值是传址。只是改变指针的指向,例如,也就是说引用类型的赋值是对象保存在栈中的地址的赋值,这样的话两个变量就指向同一个对象,因此两者之间操作互相有影响。
赋值(=)、浅拷贝和深拷贝的区别

浅拷贝实现代码
(1)遍历复制
function shallowCopy(src) {
let dst= Array.isArray ? []: {};
for (var prop in src) {
//一般情况下,for in 循环只会遍历我们自定义的属性,原型上默认的属性不会遍历出来,但会将原型中新增的属性和方法遍历出来。
if (src.hasOwnProperty(prop)) { //Object的hasOwnProperty()方法返回一个布尔值,判断对象是否包含特定的自身(非继承)属性。
dst[prop] = src[prop];
}
}
return dst;
}
var x = {
a: 1,
b: { f: { g: 1 } },
c: [ 1, 2, 3 ]
};
var y = shallowCopy(x);
console.log(y.b.f === x.b.f); // true
(2)Object.assign()
Object.assign() 方法可以把任意多个的源对象自身的可枚举属性拷贝给目标对象,然后返回目标对象。
var x = {
a: 1,
b: { f: { g: 1 } },
c: [ 1, 2, 3 ]
};
var y = Object.assign({}, x);
console.log(y.b.f === x.b.f); // true
(3)通过展开运算符 ... 来实现浅拷贝
let a = {
age: 1
}
let b = { ...a }
a.age = 2
console.log(b.age) // 1
深拷贝
首先我们尝试使用递归去解决深拷贝:
function deepClone(obj) {
let objClone = Array.isArray(obj) ? [] : {};
if(obj && typeof obj === "object") {
for(key in obj) {
if(obj.hasOwnProperty(key)) {
// 判断 obj 子元素是否为对象,如果是,递归复制
if(obj[key] && typeof obj[key] === "object") {
objClone[key] = deepClone(obj[key]);
} else {
// 如果不是,简单复制
objClone[key] = obj[key];
}
}
}
}
return objClone;
}
let a = [1, 2, 3, 4];
let b = deepClone(a);
a[0] = 2;
console.log(a, b);
// Console
// a = [2, 2, 3, 4];
// b = [1, 2, 3, 4];
写法2:
function deepClone(obj) {
function isObject(o) {
return (typeof o === 'object' || typeof o === 'function') && o !== null
}
if (!isObject(obj)) {
throw new Error('非对象')
}
let isArray = Array.isArray(obj)
let newObj = isArray ? [...obj] : { ...obj }
Reflect.ownKeys(newObj).forEach(key => {
newObj[key] = isObject(obj[key]) ? deepClone(obj[key]) : obj[key]
})
return newObj
}
let obj = {
a: [1, 2, 3],
b: {
c: 2,
d: 3
}
}
let newObj = deepClone(obj)
newObj.b.c = 1
console.log(obj.b.c) // 2
使用 JSON 对象的 parse 和 stringify
注意:采用 JSON 进行的深拷贝,无法拷贝到 undefined、function、symbol 这类数据,它是有小 bug 的深拷贝。
function deepClone(obj) {
let _obj = JSON.stringify(obj);
let objClone = JSON.parse(_obj);
return objClone
}
let a = [0, 1, [2, 3], 4];
let b = deepClone(a);
a[0] = 1;
a[2][0] = 1;
console.log(a, b);
// Console
// a = [1, 1, [1, 3], 4];
// b = [0, 1, [2, 3], 4];
https://juejin.cn/post/6844903493925371917
参考:juejin.cn/post/684490… 来源:掘金 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。