JavaScript中的浅拷贝与深拷贝

上一篇
JavaScript中的继续

媒介

文章最先之前,让我们先思索一下这几个题目:

  • 为何会有浅拷贝与深拷贝
  • 什么是浅拷贝与深拷贝
  • 怎样完成浅拷贝与深拷贝

好了,题目出来了,那末下面就让我们带着这几个题目去探讨一下吧!

假如文章中有涌现马虎、毛病的地方,还请看到的小伙伴多多指教,先行谢过

以下↓

数据范例

在最先相识 浅拷贝深拷贝 之前,让我们先来回忆一下 JavaScript 的数据范例(能够参考这里 JavaScript中的数据范例

JavaScript 中,我们将数据分为 基础数据范例(原始值)援用范例

  • 基础数据范例的值是按值接见的,基础范例的值是不可变的
  • 援用范例的值是按援用接见的,援用范例的值是动态可变的

由于数据范例的接见体式格局差别,它们的比较体式格局也是不一样的

var a = 100;
var b = 100;

a === b // true

var c = {a: 1, b: 2};
var d = {a: 1, b: 2};

c == d // false 两个差别的对象
  • 基础数据范例的比较是值得比较
  • 援用范例的比较是援用地点的比较

鉴于以上数据范例的特性,我们能够开端想到:所谓 浅拷贝深拷贝 能够就是关于值的拷贝和援用的拷贝(简朴数据范例都是对值的拷贝,不举行辨别)

一般来说,我们所触及的拷贝对象,也都是针对援用范例的。由于援用范例属性层级能够也会有多层,如许也就引出了我们所要去相识的 浅拷贝深拷贝

浅拷贝

望文生义,所谓浅拷贝就是对对象举行浅条理的复制,只复制一层对象的属性,并不包括对象内里的援用范例数据

设想一下,假如让你本身去完成这个功用,又会有怎样的思绪呢

起首,我们须要晓得被拷贝对象有哪些属性吧,然后还须要晓得这些属性都对应了那些值或许地点的援用吧。那末,答案已呼之欲出了,是的,轮回

var person = {
    name: 'tt',
    age: 18,
    friends: ['oo', 'cc', 'yy']
}

function shallowCopy(source) {
    if (!source || typeof source !== 'object') {
        throw new Error('error');
    }
    var targetObj = source.constructor === Array ? [] : {};
    for (var keys in source) {
        if (source.hasOwnProperty(keys)) {
            targetObj[keys] = source[keys];
        }
    }
    return targetObj;
}

var p1 = shallowCopy(person);

console.log(p1)

在上面的代码中,我们建立了一个 shallowCopy 函数,它吸收一个参数也就是被拷贝的对象。

  • 起首建立了一个对象
  • 然后 for...in 轮回传进去的对象,为了防止轮回到原型上面会被遍历到的属性,运用 hasOwnProperty 限定轮回只在对象本身,将被拷贝对象的每个属性和值添加到建立的对象当中
  • 末了返回这个对象

经由过程测试,我们拿到了和 person 对象险些一致的对象 p1。看到这里,你是不是是会想那这个效果和 var p1 = person 如许的赋值操纵又有什么区别呢?

我们再来测试一波

var p2 = person;

// 这个时刻我们修正person对象的数据
person.name = 'tadpole';
person.age = 19; 
person.friends.push('tt')

p2.name // tadpole
p2.age // 19
p2.friends // ["oo", "cc", "yy", "tt"]

p1.name // tt
p1.age // 18
p1.friends // ["oo", "cc", "yy", "tt"]

上面我们建立了一个新的变量 p2 ,将 person 赋值给 p2 ,然后比较两个变量

和原数据是不是指向统一对象第一层数据为基础数据范例原数据中包括子对象
赋值转变会使原数据一同转变转变会使原数据一同转变
浅拷贝转变不会使原数据一同转变转变会使原数据一同转变

深拷贝

相识完浅拷贝,置信小伙伴们关于深拷贝也应当了然于胸了

浅拷贝由于只是复制一层对象的属性,当碰到有子对象的状况时,子对象就会相互影响。所以,深拷贝是对对象以及对象的一切子对象举行拷贝

完成体式格局就是递归挪用浅拷贝

function deepCopy(source){
   if(!source || typeof source !== 'object'){
     throw new Error('error');
   }
   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] = deepCopy(source[keys]);
         }else{
           targetObj[keys] = source[keys];
         }
      } 
   }
   return targetObj;
}
var obj1 = {
    arr: [1, 2, 3],
    key: {
        id: 22
    },
    func: function() {
        console.log(123)
    }
}

var obj2 = deepCopy(obj1);

obj1.arr.push(4);

obj1.arr // [1, 2, 3, 4]
obj2.arr // [1, 2, 3]
obj1.key === obj2.key // false
obj1.func === obj2.func // true

关于深拷贝的对象,转变源对象不会对获得的对象有影响。只是在拷贝的过程当中源对象的要领丧失了,这是由于在序列化 JavaScript 对象时,一切函数和原型成员会被故意疏忽

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

// 应用JSON序列化完成一个深拷贝
function deepCopy(source){
  return JSON.parse(JSON.stringify(source));
}
var o1 = {
  arr: [1, 2, 3],
  obj: {
    key: 'value'
  },
  func: function(){
    return 1;
  }
};
var o2 = deepCopy(o1);
console.log(o2); // => {arr: [1,2,3], obj: {key: 'value'}}

完成拷贝的其他体式格局

浅拷贝

  • Array.prototype.slice()
  • Array.prototype.concat()
  • Object.assign
  • 拓展操纵符...

深拷贝

许多框架或许库都供应了深拷贝的体式格局,比方 jQuerylodash 函数库等等,基础完成体式格局也就和我们前面引见的迥然差别

跋文

依据需求的差别,比方有时刻我们须要一个全新的对象,在修正它的时刻不去影响到源对象,那末这个时刻我们就可以够须要深拷贝;反之,浅拷贝就可以完成我们的需求

只是,我们须要注意到一点,那就是由于完成深拷贝运用递归的体式格局,就增加了机能的斲丧

置信在不停运用的过程当中,你肯定会对它愈来愈熟习

末了,引荐一波前端进修进程,不定期分享一些前端题目和故意思的东西迎接 star 关注 传送门

    原文作者:游荡de蝌蚪
    原文地址: https://segmentfault.com/a/1190000018999209
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞