JS专题之深浅拷贝

前言

在开发过程当中,偶尔会遇到这种场景,拿到一个数据后,你打算对它进行处理,可是你又但愿拷贝一份副本出来,方便数据对比和之后恢复数据。javascript

那么这就涉及到了 JS 中对数据的深浅拷贝问题,所谓深浅拷贝,浅拷贝的意思就是,你只是复制了对象数据的引用,并无把内存里的值另外复制一份,那么深拷贝就是把值完整地复制一份新的值。java

在以前的文章《JS专题之数据类型和类型检测》中我有讲过,JS 中的数据类型分为两种,基本数据类型和引用数据类型,基本数据类型是保存在栈的数据结构中的,是按值访问,因此不存在深浅拷贝问题。jquery

而好比对象,数组,函数,正则,时间对象这些都是引用数据类型,是保存在堆中的。因此,引用数据类型的复制,是内存地址的传递,并无拷贝出一份新的数据。数组

那么深拷贝,浅拷贝的区别是什么呢?先给结论:数据结构

操做拷贝以后的数据不会影响到原数据的值拷贝,就是深拷贝,反正,有影响则为浅拷贝。闭包

1、应用场景

平常开发中,JS 拷贝大多会在 数据保存,数据比对,数据同步 时出现,因此,当你在这些场景的时候,要记得里面隐藏有一个数据深浅拷贝的问题。app

2、浅拷贝

咱们来看一下浅拷贝:函数

function clone(origin) {
    var result = {};
        for (var prop in origin) {
            if (origin.hasOwnProperty(prop)) {
                result[prop] = origin[prop];
            }
        }
        return result;
}

var jay = {
    name: "jayChou",
    age: 40,
    family: {
        wife: "Quinlivan"
    }
}

var otherJay = clone(jay);

otherJay.age = 18;
otherJay.family.wife = "otherGirl";

console.log(jay); 
// 
// {
// name: "jayChou",
// age: 40, // 没被改变
// family: {
// wife: "otherGirl" // 同时被改变,说明是同一个引用
// }
// }

console.log(otherJay);

// 
// {
// name: "jayChou",
// age: 18,
// family: {
// wife: "otherGirl" // 被改变了
// }
// }
复制代码

咱们发现,首先,浅拷贝不是直接赋值,浅拷贝新建了一个对象,而后将源对象的属性都一一复制过来,复制的是值,而不是引用。工具

咱们知道,对象都是按地址引用进行访问的,浅拷贝的复制只复制了第一层的属性,并无递归将全部的值复制过来,因此,操做拷贝数据,对原数据产生了影响,故而为浅拷贝。post

进而,那些能够直接返回原数组的方法就能够简单实现数组和对象浅拷贝。

// 一、 数组浅拷贝 - slice
function shallowCopy1(origin) {
    return origin.slice();
}

// 二、 数组浅拷贝 - concat
function shallowCopy2(origin){
    return origin.concat();
}

// 三、 数组浅拷贝 - 遍历
function shallowCopy3(origin){
  var result = [];
  for(var i = 0; i < origin.length; i++) {
    result.push(origin[i]);
  }
  return result;
}


// 四、 对象浅拷贝 - Object.assign
function shallowCopy4(origin) {
    return Object.assign({},origin)
}

// 五、 对象浅拷贝 - 扩展运算符
// 扩展运算符(...)用于取出参数对象的全部可遍历属性,拷贝到当前对象之中
function shallowCopy5(origin) {
    return {
        ...origin
    }
}

复制代码

Object.assign 的拷贝,假如源对象的属性值是一个指向对象的引用,它也只拷贝那个引用值。MDN 有相应的实例和解释。

2、深拷贝

深拷贝就完整复制数据的值(而非引用),目的在于避免拷贝后数据对原数据产生影响。

目前深拷贝的实现方法主要有递归复制,JSON 正反序列化:

// 1. 深拷贝 - JSON 正反序列化
// 缺点就是没法拷贝 undefined、function、symbol 这类特殊的属性值。
function deepClone1(origin) {
    return JSON.parse(JSON.stringify(arr));
}

// 2. 深拷贝 - 递归;
function deepClone2(origin) {
  const result = origin.constructor === Array ? [] : {};
  for (let keys in origin) {
    // 不遍历原型链上的属性
    if (origin.hasOwnProperty(keys)) {
      if (origin[keys] && typeof origin[keys] === "object") {
        // 若是值是对象,就递归一下, 区分是通常对象仍是数组对象
        result[keys] = origin[keys].constructor === Array ? [] : {};
        // 若是是引用数据类型,会递归调用
        result[keys] = deepClone(origin[keys]);
      } else {
        // 若是不是,就直接赋值
        result[keys] = origin[keys];
      }
    }
  }
  return result;
}

复制代码

JS 的深拷贝的应用,须要根据你的使用场景进行使用,首先是有无必要深拷贝,其次是数据类型,是否直接使用 JSON 的 API 其实就能够。

JS 深浅拷贝在平常开发中使用频率仍是较高的,其中考察的知识点,主要在于:

一、是否遇到过深浅拷贝的问题,里面有什么坑
二、是否了解 JS 的数据类型,数据在计算机中的存储机制
三、是否了解数组、对象的一些经常使用的 API
四、jquery、lodash、underscore 的相关工具函数使用

总结

深浅拷贝主要考察了开发者对 JS 数据类型的了解,数组,对象经常使用方法的特色和应用,递归函数的封装。

春节快乐!
写于大年三十,不写文章浑身不舒服~ 欢迎关注个人我的公众号“谢南波”,专一分享原创文章。

掘金专栏 JavaScript 系列文章

  1. JavaScript之变量及做用域
  2. JavaScript之声明提高
  3. JavaScript之执行上下文
  4. JavaScript之变量对象
  5. JavaScript之原型与原型链
  6. JavaScript之做用域链
  7. JavaScript之闭包
  8. JavaScript之this
  9. JavaScript之arguments
  10. JavaScript之按值传递
  11. JavaScript之例题中完全理解this
  12. JavaScript专题之模拟实现call和apply
  13. JavaScript专题之模拟实现bind
  14. JavaScript专题之模拟实现new
  15. JS专题之事件模型
  16. JS专题之事件循环
  17. JS专题之去抖函数
  18. JS专题之节流函数
  19. JS专题之函数柯里化
  20. JS专题之数组去重
相关文章
相关标签/搜索