JavaScript对象的浅拷贝与深拷贝

1、JS包含两种数据类型: 值类型(基本类型)和引用类型(对象类型)

  值类型都是简单的数据段(Number、String 、Boolean、Null、Undefined),引用类型是由多个值构成对象(Object 、Array 、Function 、Data等)。

  我们进行变量赋值时,解析器首先要确认的就是这个值是基本类型还是引用类型。

  值类型数据存储在栈(stack)中,栈是内存中一种特殊的数据结构,也称为线性表。变量的位置和变量值的位置是重叠的,也就是说值类型的数据被存储在变量被访问的位置。

  引用类型数据存储在堆(heap)中,堆是内存中的动态区域,相当于自留空间,在程序运行期间会动态分配给代码。堆中存储的一般都是对象,然后通过一个编号传递给栈内变量,这个编号就是所谓的引用指针(point),这样变量和变量值之间是分离的,它们通过指针相联系。当读写数据时,计算机通过变量的指针找到堆中的数据块,并进行操作。

2、对象的浅拷贝

  浅拷贝就是增加一个引用指针(point)指向已存在的内存,浅拷贝只是拷贝了内存的地址,原来的变量跟拷贝出来的变量指向同一内存地址,改变其中某个变量,另一个也会变。

let arr1 = [1,2,3];
let arr2 = arr1;
arr1.push(4);
console.log(arr1); // [1,2,3,4]
console.log(arr2); // [1,2,3,4]

3、深拷贝

  深拷贝就是增加一个引用指针(point),并申请一个新的内存,并且让这个新增加的“指针”指向这个新的内存地址,当我们需要复制原对象而又不能修改原对象的时候,深拷贝是唯一的选择。

3、深拷贝的方法

  黑科技手段。会忽略值为function以及undefied的字段,而且对date类型的支持也不太友好,只能克隆原始对象自身的值,不能克隆它继承的值。

JSON.parse(JSON.stringify(obj))

  递归

function clone(value, isDeep) {
  if (value === null) return null
  if (typeof value !== 'object') return value
  if (Array.isArray(value)) {
    if (isDeep) {
      return value.map(item => clone(item, true))
    }
    return [].concat(value)
  } else {
    if (isDeep) {
      var obj = {}
      Object.keys(value).forEach(item => {
        obj[item] = clone(value[item], true)
      })
      return obj
    }
    return { ...value }
  }
}
var objects = { c: { 'a': 1, e: [1, {f: 2}] }, d: { 'b': 2 } }
var shallow = clone(objects, true)
console.log(shallow.c.e[1]) // { f: 2 }
console.log(shallow.c === objects.c) // false
console.log(shallow.d === objects.d) // false
console.log(shallow === objects) // false

  递归改造(保持原形链的继承)

var clone = function (obj) { 
    if(obj === null) return null 
    if(typeof obj !== 'object') return obj;
    if(obj.constructor===Date) return new Date(obj); 
    if(obj.constructor === RegExp) return new RegExp(obj);
    var newObj = new obj.constructor ();  //保持继承链
    for (var key in obj) {
        if (obj.hasOwnProperty(key)) {   //不遍历其原型链上的属性
            var val = obj[key];
            newObj[key] = typeof val === 'object' ? arguments.callee(val) : val; // 使用arguments.callee解除与函数名的耦合
        }
    }  
    return newObj;  
}; 

 

posted @ 2018-11-01 16:25  樱桃小丸籽  阅读(238)  评论(0编辑  收藏  举报