博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
前端学习(三):浅拷贝和深拷贝
阅读量:6150 次
发布时间:2019-06-21

本文共 4282 字,大约阅读时间需要 14 分钟。

堆和栈的区别

其实深拷贝和浅拷贝的主要区别就是其在内存中的存储类型不同。

堆和栈都是内存中划分出来用来存储的区域。

栈(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 = 2console.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 = 1console.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.im/post/59ac1c4ef265da248e75892b  复制代码

参考: 来源:掘金 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

你可能感兴趣的文章
jq漂亮实用的select,select选中后,显示对应内容
查看>>
C 函数sscanf()的用法
查看>>
python模块之hashlib: md5和sha算法
查看>>
售前工程师的成长---一个老员工的经验之谈
查看>>
Get到的优秀博客网址
查看>>
老男孩教育每日一题-第107天-简述你对***的理解,常见的有哪几种?
查看>>
Python学习--time
查看>>
在OSCHINA上的第一篇博文,以后好好学习吧
查看>>
Spring常用注解
查看>>
我的友情链接
查看>>
PCS子层有什么用?
查看>>
查看端口,关闭端口
查看>>
linux:yum和apt-get的区别
查看>>
Sentinel 1.5.0 正式发布,引入 Reactive 支持
查看>>
数据库之MySQL
查看>>
2019/1/15 批量删除数据库相关数据
查看>>
数据类型的一些方法
查看>>
Mindjet MindManager 2019使用教程:
查看>>
详解 CSS 绝对定位
查看>>
AOP
查看>>