前端面试:浅拷贝与深拷贝详细解释

内存

内存分为四个区域:栈区(堆栈),堆区,全局静态区,只读区(常量区和代码区)

在这里插入图片描述

  1. 栈区

    1. 数据:局部变量,形参,被调用的函数的地址等
    2. 特点:
      • 读取速度快。存储和释放的思路是按照栈来进行的,存储是压栈,释放弹栈。
      • 空间小:基本类型的数据占用空间的大小不会随着值的改变而改变。
  2. 堆区

    1. 数据:new出来的数据
    2. 特点:
      • 读取速度慢
      • 空间大:引用类型的数据大小是动态的,会随着数据的增加而改变大小。
  3. 全局静态区

    1. 数据:全局变量和静态变量
    2. 特点:程序运行过程中,数据会一直在内存中。
  4. 只读区

    1. 数据:常量区存放常量,代码区存放程序代码。
    2. 特点:在程序运行过程中数据不能改变。

基本类型和引用类型在内存上存储的区别

function test(){
	var age = 250;	// 基本类型
	var arr = new Array(12, 23, 34);	// 引用类型
}

在这里插入图片描述
test()在调用时:

  1. 定义局部变量 age。由于age是局部变量,所以在栈中申请内存空间,起名为age,又由于给age赋的值 250 250 250是基本类型,所以,值直接存储在栈中。
  2. 定义局部变量arr。由于arr是局部变量,所以在栈中申请空间,但是arr的内存中存储的是引用类型(new出来的),所以,先在堆中申请空间存放数据 12 , 23 , 34 , 12,23,34, 12,23,34,。再把堆区的地址赋给arr

数据类型

基本类型和引用类型

  1. 基本类型:值类型。直接存储在栈(stack)中的数据,即在变量所对于的内存区域存储的是值,而不是地址。
  2. 引用类型:地址类型,存储的是该对象在栈中引用,真实的数据存放在堆内存里。
    引用数据类型在栈中存储了指针,该指针指向堆中该实体的起始地址。当解释器寻找引用值时,会首先检索其在栈中的地址,取得地址后从堆中获得实体。

赋值时的内存变化

  1. 基本类型
    在这里插入图片描述

  2. 引用类型
    在这里插入图片描述
    在这里插入图片描述

    如果给arr[0]赋值的话,arr1[0]的值也会发生变化,因为,arrarr1保存着相同的地址,它门两个引用的数据是共享的。

深拷贝和浅拷贝

深拷贝浅拷贝只针对ObjectArray这种引用类型。

  • 浅拷贝只复制指向某个对象的指针,而不复制对象本身,新旧对象共享一块内存。
  • 深拷贝会创建一个一模一样的对象,新对象和原对象不共享内存,修改新对象不会影响到原对象。

赋值与浅拷贝区别

把一个对象赋值给一个新的变量时,赋的其实是该对象在栈中的地址,而不是堆中的数据。两个对象指向同一个存储空间,无论哪个对象发生改变,其实都是改变存储空间中的内容。两个对象是联动的

浅拷贝是按位拷贝,会创建一个新的对象,这个对象有着原始对象属性值的一份精准拷贝。如果属性是基本类型,拷贝的就是基本类型的;如果是引用类型,则拷贝的是内存地址。如果其中一个对象改变了这个地址,就会影响到另一个对象。

默认拷贝构造函数这是对对象进行浅拷贝,只复制对象空间,不复制资源。

示例

// 赋值
var obj1 = {
  name: 'a',
  age: '18',
  type: [1, [2, 3], [4, 5]],
};

var obj2 = obj1;
obj2.name = 'b';
obj2.type = ['一', '二'];

console.log('obj1:', obj1);
console.log('obj2:', obj2);

在这里插入图片描述

// 浅拷贝
var obj1 = {
  name: 'a',
  age: '18',
  type: [1, [2, 3], [4, 5]],
};

var obj2 = shallowCopy(obj1);
obj2.name = 'b';
obj2.type[1] = ['一', '二'];

function shallowCopy(obj) {
  var newObj = {};
  for (var prop in obj) {
    if (obj.hasOwnProperty(prop)) {
      newObj[prop] = obj[prop];
    }
  }
  return newObj;
}

console.log('obj1:', obj1);
console.log('obj2:', obj2);

在这里插入图片描述

是否和原数据指向同一对象第一层数据为基本数据类型原数据中包含子对象
赋值改变——原数据改变改变——原数据改变
浅拷贝改变——原数据不变改变——原数据改变
深拷贝改变——原数据不变改变——原数据不变

浅拷贝实现方式

1. Object.assign()

Object.assign() 方法可以把任意多个的源对象自身的可枚举属性拷贝给目标对象,然后返回目标对象。但是 Object.assign()进行的是浅拷贝,拷贝的是对象的属性的引用,而不是对象本身。注意:当object只有一层的时候,是深拷贝

var obj1 = { a: { a: 'aaa', b: 10 } };
var obj2 = Object.assign({}, obj1);
obj2.a.a = 'bbb';
console.log(obj1.a.a);

var obj3 = { username: 'zsy' };
var obj4 = Object.assign({}, obj3);
obj4.username = 'ZhShy';
console.log(obj3);

在这里插入图片描述

2. Array.prototype.concat()

let arr = [
  1,
  3,
  {
    username: 'zsy',
  },
];
let arr2 = arr.concat();
arr2[2].username = 'ZhShy';
console.log(arr);

在这里插入图片描述

3. Array.prototype.slice()

let arr = [
  1,
  3,
  {
    username: 'zsy',
  },
];
let arr2 = arr.slice();
arr2[2].username = 'ZhShy';
console.log(arr);

在这里插入图片描述

深拷贝的实现方法

1. JSON.parse(JSON.stringify())

JSON.stringify将对象转成 JSON 字符串,再用JSON.parse()把字符串解析成对象,一去一来,新的对象产生了,而且对象会开辟新的栈,实现深拷贝。

let arr = [
  1,
  3,
  {
    username: 'zsy',
  },
];
let arr2 = JSON.parse(JSON.stringify(arr));
arr2[2].username = 'ZhShy';
console.log(arr, arr2);

在这里插入图片描述
这种方法虽然可以实现数组或对象深拷贝,但不能处理函数

2. 手写递归方法

递归方法实现深度克隆原理:遍历对象、数组直到里边都是基本数据类型,然后再去复制,就是深度拷贝

function checkedType(target) {
  return Object.prototype.toString.call(target).slice(8, -1);
}

function clone(target) {
  let result,
    targetType = checkedType(target);
  if (targetType === 'object') {
    result = {};
  } else if (targetType === 'Array') {
    result = [];
  } else {
    return target;
  }
  for (let i in target) {
    let value = target[i];
    if (checkedType(value) === 'object' || checkedType(value) === 'Array') {
      result[i] = clone(value);
    } else {
      result[i] = value;
    }
  }
  return result;
}

3. lodash

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
  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
浅拷贝是创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值,如果属性是引用类型,拷贝的就是内存地址,所以如果其中一个对象改变了这个地址,就会影响到另一个对象。\[2\] 深拷贝是将一个对象从内存中完整的拷贝一份出来,从堆内存中开辟一个新的区域存放新对象,且修改新对象不会影响原对象。深拷贝会递归地复制对象中的子对象,确保所有层级的对象都被复制。\[2\] 浅拷贝的实现方式有多种,比如使用Array.concat()、Array.slice()、Object.assign()和展开运算符等。这些方法都是创建一个新的对象,但是对于引用类型的属性,仍然是共享同一块内存,所以会相互影响。\[2\] 深拷贝的实现方式也有多种,比如使用JSON.parse(JSON.stringify())和jQuery.extend()等。这些方法会递归地复制对象中的子对象,确保所有层级的对象都被复制,从而实现完全独立的拷贝。\[2\] 浅拷贝适用于对简单的对象或数组进行拷贝,当想要修改其中一个对象的值,但又要保留原来对象的值不被修改时,可以使用浅拷贝。而深拷贝适用于对复杂的对象或数组进行拷贝,当想要创建一个新的对象或数组,并且对新对象或数组的修改不会影响原对象或数组时,可以使用深拷贝。\[4\] 总结起来,浅拷贝只复制一层对象的属性,而深拷贝则递归复制了所有层级。\[2\] #### 引用[.reference_title] - *1* [有关深拷贝浅拷贝(经典前端面试题)](https://blog.csdn.net/weixin_49602426/article/details/127289749)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down28v1,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [[前端面试题]:深拷贝浅拷贝的区别](https://blog.csdn.net/WWEIZAI/article/details/126519334)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down28v1,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [前端面试浅拷贝深拷贝的区别?](https://blog.csdn.net/weixin_39570751/article/details/123363926)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down28v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ZhShy23

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值