一 首先了解JavaScript中的基本数据类型

     基本数据类型:String,Number,Boolean,Null,Undefined

     引用数据类型:Araay,Date,RegExp,Function

二 基本数据类型和引用数据类型的区别?

     (1)它们保存的位置不同:基本数据保存在栈中,引用数据类型保存在堆内存中。JS对引用数据的操作其实操作对象的引用而不是实际的对象,也就是指向实际对象的内存地址,如果obj1拷贝了obj2,那么这两个对象指向了同一堆内存对象,具体就是吧obj1栈内存中的引用地址复制了一份给obj2,所以他们指向了同一个堆内存对象。

        那为什么基本数据类型要存在栈内存中,而引用数据要存在堆内存中?

         (1)堆比栈大,栈的查找速度比堆块。

         (2)基本数据类型比较的稳定,相对的话占用的内存比较小

         (3)引用数据类型一般都是动态的,而且可能是无限大,引用的值也经常改变,所以不能放在栈中,这样会降低查找的速度,因此放在变量栈中的值应该是指向该对象再堆内存中的地址,地址的大小的固定的,所以吧他存在栈中对变量的性能没有影响。

      JS一般在访问存在堆内存的对象的时候是不能直接访问的,所以在访问对象的时候,要先获取改对象再堆内存中的地址,在根据改地址去访问该对象中的值。

    (2)基本数据类型可以使用typeof可以返回基本数据类型,但是Null会返回object,所以Null表示一个空对象指针;引用数据类型使用typeof会返回object,所以引用数据类型要用instanceof来检测引用数据的类型。

    (3)定义引用数据类型需要使用new操作符,后面在跟一个构造函数来创建,或者使用对象字面量表示法创建对象。

        使用new操作符创建对象

var obj1 = new Object();obj1.a = 1;

        使用对象字面量表示法创建对象

var obj1 = {  a: 1,  b: 2}

基本数据类型 name和value值都是存储在栈中

数组 拷贝 java 数组拷贝js_数组深拷贝

当b=a的时候

数组 拷贝 java 数组拷贝js_数组_02

栈内存开辟了一个新内存出来,所以在修改a的值的时候不会影响到b的值

引用数据类型-name是存在栈中,value存在堆内存中,但是栈内存会提供一个引用地址指向该对象在堆内存中的值

数组 拷贝 java 数组拷贝js_数组_03

当b=a拷贝时,其实复制的是a的引用地址,并不是堆内存中的值

数组 拷贝 java 数组拷贝js_引用数据类型_04

当你修改a里面的值的时候,由于a与b指向的是同一个地址所以b也就受到了影响,这就是浅拷贝。

数组 拷贝 java 数组拷贝js_引用数据类型_05

如果在堆内存中开辟了一个新的内存地址专门存在b的值的话,那就达到了深拷贝的效果了。

数组 拷贝 java 数组拷贝js_数组 拷贝 java_06

三 什么是深拷贝和浅拷贝

      首先深拷贝和浅拷贝都只针对于引用类型的数据;浅拷贝只复制指向某个对象的指针,并不复制对象的本身,新原对象还是共享同一块内存,但是深拷贝会创造一个一模一样的新对象出来,新对象跟原对象不再共享同一块内存地址,修改新对象也不会影响到原对象。

       区别:浅拷贝只复制对象的第一层属性,深拷贝可以对对象的属性进行递归复制。

四 实现深拷贝

   1、json对象的parse和stringfy

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  console.log(a) //[1,1,2,3,4]console.log(b) //[0,1,2,3,4]

   2、递归复制所有层级属性

function deepCopy(obj1) {      var obj2 = Array.isArray(obj1) ? [] : {};      if (obj1 && typeof obj1 === "object") {        for (var i in obj1) {          if (obj1.hasOwnProperty(i)) {            // 如果子属性为引用数据类型,递归复制            if (obj1[i] && typeof obj1[i] === "object") {              obj2[i] = deepCopy(obj1[i]);            } else {              // 如果是基本数据类型,只是简单的复制              obj2[i] = obj1[i];            }          }        }      }      return obj2;    }    var obj1 = {      a: 1,      b: 2,      c: {        d: 3      }    }    var obj2 = deepCopy(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

缺陷:当遇到两个互相引用的对象,会出现死循环的情况,为了避免相互引用的对象导致死循环的情况,则应该在遍历的时候判断是否相互引用对象,如果是则退出循环;

function deepCopy(obj1) {      var obj2 = Array.isArray(obj1) ? [] : {};      if (obj1 && typeof obj1 === "object") {        for (var i in obj1) {          var prop = obj1[i]; // 避免相互引用造成死循环,如obj1.a=obj          if (prop == obj1) {            continue;          }          if (obj1.hasOwnProperty(i)) {            // 如果子属性为引用数据类型,递归复制            if (prop && typeof prop === "object") {              obj2[i] = (prop.constructor === Array) ? [] : {};              arguments.callee(prop, obj2[i]); // 递归调用            } else {              // 如果是基本数据类型,只是简单的复制              obj2[i] = prop;            }          }        }      }      return obj2;    }    var obj1 = {      a: 1,      b: 2,      c: {        d: 3      }    }    var obj2 = deepCopy(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
// Object.create实现深拷贝1,但也只能拷贝一层function deepCopy(obj1) {      var obj2 = Array.isArray(obj1) ? [] : {};      if (obj1 && typeof obj1 === "object") {        for (var i in obj1) {          var prop = obj1[i]; // 避免相互引用造成死循环,如obj1.a=obj          if (prop == obj1) {            continue;          }          if (obj1.hasOwnProperty(i)) {            // 如果子属性为引用数据类型,递归复制            if (prop && typeof prop === "object") {              obj2[i] = (prop.constructor === Array) ? [] : Object.create(prop);            } else {              // 如果是基本数据类型,只是简单的复制              obj2[i] = prop;            }          }        }      }      return obj2;    }    var obj1 = {      a: 1,      b: 2,      c: {        d: 3      }    }    var obj2 = deepCopy(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
// Object实现拷贝2,浅拷贝var obj1 = {      a: 1,      b: 2,      c: {        d: 3      }    }    var obj2 = Object.create(obj1);    obj2.a = 3;    obj2.c.d = 4;    alert(obj1.a); // 1    alert(obj2.a); // 3    alert(obj1.c.d); // 4    alert(obj2.c.d); // 4

3、jquery的extends方法

$.extend([deep ], target, object1 [, objectN ])

deep表示是否深拷贝,为true为深拷贝;为false,为浅拷贝。

target Object类型 目标对象,其他对象的成员属性将被附加到该对象上。

object1  objectN可选。Object类型 第一个以及第N个被合并的对象。 

let a = [0,1,[2,3],4]let b = $.extend(true, [], a)a[0] = 1a[2][0] = 1  // [1,1,[1,3],4]b  // [0,1,[2,3],4]

4、lodash函数库实现深拷贝

let result = _.cloneDeep(test)

5、Reflect法

// 代理法function deepClone(obj) {    if (!isObject(obj)) {        throw new Error('obj 不是一个对象!')    }    let isArray = Array.isArray(obj)    let cloneObj = isArray ? [...obj] : { ...obj }    Reflect.ownKeys(cloneObj).forEach(key => {        cloneObj[key] = isObject(obj[key]) ? deepClone(obj[key]) : obj[key]    })    return cloneObj}

6、用slice实现对数组的深拷贝

// 当数组里面的值是基本数据类型,比如String,Number,Boolean时,属于深拷贝// 当数组里面的值是引用数据类型,比如Object,Array时,属于浅拷贝var arr1 = ["1","2","3"]; var arr2 = arr1.slice(0);arr2[1] = "9";console.log("数组的原始值:" + arr1 );console.log("数组的新值:" + arr2 );

7.用concat实现对数组的深拷贝

// 当数组里面的值是基本数据类型,比如String,Number,Boolean时,属于深拷贝var arr1 = ["1","2","3"];var arr2 = arr1.concat();arr2[1] = "9";console.log("数组的原始值:" + arr1 );console.log("数组的新值:" + arr2 );// 当数组里面的值是引用数据类型,比如Object,Array时,属于浅拷贝var arr1 = [{a:1},{b:2},{c:3}];var arr2 = arr1.concat();arr2[0].a = "9";console.log("数组的原始值:" + arr1[0].a ); // 数组的原始值:9console.log("数组的新值:" + arr2[0].a ); // 数组的新值:9

五 实现浅拷贝

for···in只循环第一层

// 只复制第一层的浅拷贝function simpleCopy(obj1) {   var obj2 = Array.isArray(obj1) ? [] : {};   for (let i in obj1) {   obj2[i] = obj1[i];  }   return obj2;}var obj1 = {   a: 1,   b: 2,   c: {         d: 3      }}var obj2 = simpleCopy(obj1);obj2.a = 3;obj2.c.d = 4;alert(obj1.a); // 1alert(obj2.a); // 3alert(obj1.c.d); // 4alert(obj2.c.d); // 4

Object.assign方法

var obj = {    a: 1,    b: 2}var obj1 = Object.assign(obj);obj1.a = 3;console.log(obj.a) // 3

直接用=赋值

let a=[0,1,2,3,4],    b=a;console.log(a===b);a[0]=1;console.log(a,b);