JavaScript中的浅拷贝和深拷贝

在JavaScript中,关于ObjectArray这类援用范例值,当从一个变量向另一个变量复制援用范例值时,这个值的副本实际上是一个指针,两个变量指向同一个堆对象,转变个中一个变量,另一个也会受到影响。

这类拷贝分为两种状况:拷贝援用和拷贝实例,也就是我们说的浅拷贝和深拷贝

浅拷贝(shallow copy)

拷贝原对象的援用,这是最简朴的浅拷贝。

// 对象
var o1 = {a: 1};
var o2 = o1;

console.log(o1 === o2);  // =>true
o2.a = 2; 
console.log(o1.a); // => 2

// 数组
var o1 = [1,2,3];
var o2 = o1;

console.log(o1 === o2); // => true
o2.push(4);
console.log(o1); // => [1,2,3,4]

拷贝原对象的实例,然则对其内部的援用范例值,拷贝的是其援用,经常使用的就是如jquey中的$.extend({}, obj); Array.prototype.slice()Array.prototype.concat()都邑返回一个数组或许对象的浅拷贝,举个例子:

var o1 = ['darko', {age: 22}];
var o2 = o1.slice(); // 依据Array.prototype.slice()的特征,这里会返回一个o1的浅拷贝对象

console.log(o1 === o2); // => false,申明o2拷贝的是o1的一个实例

o2[0] = 'lee';
console.log(o1[0]); // => "darko" o1和o2内部包括的基础范例值,复制的是其实例,不会相互影响

o2[1].age = 23;
console.log(o1[1].age); // =>23 o1和o2内部包括的援用范例值,复制的是其援用,会相互影响

可以经由过程Array.prototype.slice()jQuery中的$.extend({}, obj)完成对一个数组或许对象的浅拷贝,我们也可以本身写一个简朴浅拷贝函数来加深对浅拷贝的明白、

// 浅拷贝完成,仅供参考
function shallowClone(source) {
    if (!source || typeof source !== 'object') {
        throw new Error('error arguments');
    }
    var targetObj = source.constructor === Array ? [] : {};
    for (var keys in source) {
        if (source.hasOwnProperty(keys)) {
            targetObj[keys] = source[keys];
        }
    }
    return targetObj;
}

深拷贝(deep copy)

深拷贝也就是拷贝出一个新的实例,新的实例和之前的实例互不影响,深拷贝的完成有几种要领,起首我们可以借助jQuery,lodash等第三方库完成一个深拷贝实例。在jQuery中可以经由过程增加一个参数来完成递归extend,挪用$.extend(true, {}, ...)就可以完成一个深拷贝。

我们也可以本身完成一个深拷贝的函数,平常有两种体式格局,一种就是用递归的体式格局来做,另有一种是运用JSON.stringifyJSON.parse来做,这两种体式格局各有好坏,先来看看递归的要领怎么做。

jQuery中的extend要领基础的就是根据这个思绪完成的,然则没有办法处置惩罚源对象内部轮回援用的题目,同时对Date,Funcion等范例值也没有完成真正的深度复制,然则这些范例的值在从新定义的时刻平常都是直接掩盖,所以也不会对源对象产生影响,从肯定水平上来讲也算是完成了一个深拷贝。

// 递归完成一个深拷贝
function deepClone(source){
   if(!source || typeof source !== 'object'){
     throw new Error('error arguments', 'shallowClone');
   }
   var targetObj = source.constructor === Array ? [] : {};
   for(var keys in source){
      if(source.hasOwnProperty(keys)){
         if(source[keys] && typeof source[keys] === 'object'){
           targetObj[keys] = source[keys].constructor === Array ? [] : {};
           targetObj[keys] = deepClone(source[keys]);
         }else{
           targetObj[keys] = source[keys];
         }
      } 
   }
   return targetObj;
}
// test example
var o1 = {
  arr: [1, 2, 3],
  obj: {
    key: 'value'
  },
  func: function(){
    return 1;
  }
};
var o3 = deepClone(o1);
console.log(o3 === o1); // => false
console.log(o3.obj === o1.obj); // => false
console.log(o2.func === o1.func); // => true
  

另有一种完成深拷贝的体式格局是运用JSON对象中的parsestringify,JOSN对象中的stringify可以把一个js对象序列化为一个JSON字符串,parse可以把JSON字符串反序列化为一个js对象,经由过程这两个要领,也可以完成对象的深复制。

我们从下面的例子就可以看到,源对象的要领在拷贝的过程当中丧失了,这是由于在序列化JavaScript对象时,一切函数和原型成员会被故意疏忽,这个完成可以满足一些比较简朴的状况,可以处置惩罚JSON花样所能示意的一切数据范例,同时如果在对象中存在轮回运用的状况也没法正确处置惩罚。

// 运用JSON序列化完成一个深拷贝
function deepClone(source){
  return JSON.parse(JSON.stringify(source));
}
var o1 = {
  arr: [1, 2, 3],
  obj: {
    key: 'value'
  },
  func: function(){
    return 1;
  }
};
var o2 = deepClone(o1);
console.log(o2); // => {arr: [1,2,3], obj: {key: 'value'}}
    原文作者:Darko
    原文地址: https://segmentfault.com/a/1190000008637489
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞