前端学习(三):浅拷贝和深拷贝

212 阅读5分钟

堆和栈的区别

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

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

栈(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];

参考:juejin.cn/post/684490…

https://juejin.cn/post/6844903493925371917  

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