JavaScript 深拷贝浅拷贝

深浅拷贝的含义

  • 浅拷贝:只复制指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存。
  • 深拷贝:会创造一个一模一样的对象,新对象和原对象不共享内存,修改新对象不会改到原对象。

浅拷贝

  1. 直接赋值

最简单的利用 = 赋值操作符实现了一个浅拷贝,可以很清楚的看到,随着 cloneArray 和 cloneObj 改变,originArray 和 originObj 也随着发生了变化。

const originArray = [1, 2, 3, 4, 5];
const originObj = {a: 'a', b: 'b', c: [1, 2, 3], d: {dd: 'dd'}};

const cloneArray = originArray;
const cloneObj = originObj;

console.log(cloneArray); // [1,2,3,4,5]
console.log(originObj); // {a:'a',b:'b',c:Array[3],d:{dd:'dd'}}

cloneArray.push(6);
cloneObj.a = {aa:'aa'};

console.log(cloneArray); // [1,2,3,4,5,6]
console.log(originArray); // [1,2,3,4,5,6]

console.log(cloneObj); // {a:{aa:'aa'},b:'b',c:Array[3],d:{dd:'dd'}}
console.log(originArray); // {a:{aa:'aa'},b:'b',c:Array[3],d:{dd:'dd'}}
  1. Object.assign() 方法
var user1 = {
    name: "椿生",
    age: 20,
    hobbies: ['music', 'pingpong']
};
var user2 = Object.assign({}, user1);
console.log(user1); //{name: "椿生", age: 20, hobbies: ['music', 'pingpong']}
console.log(user2); //{name: '椿生', age: 20, hobbies: ['music', 'pingpong']}


// Object 单层对象深拷贝
user1.name = "Sakura";
console.log(user1); //{name: 'Sakura', age: 20, hobbies: ['music', 'pingpong']}
console.log(user2); //{name: '椿生', age: 20, hobbies: ['music', 'pingpong']}


// Object 多层对象浅拷贝
user1.hobbies[0] = "League of Legends";
console.log(user1); //{name: 'Sakura', age: 20, hobbies: ['League of Legends', 'pingpong']}
console.log(user2); //{name: '椿生', age: 20, hobbies: ['League of Legends', 'pingpong']}
  1. ES6 扩展运算符
var user1 = {
    name: "椿生",
    like: {
        singer: "Eason Chan",
        sport: "pingpong",
        },
   };
             
var user2 = {...user1};
console.log(user1); // {name: '椿生', like: {singer: 'Eason Chan', sport: 'pingpong'}}
console.log(user2); // {name: '椿生', like: {singer: 'Eason Chan', sport: 'pingpong'}}

user1.name = "Sakura";
console.log(user1); // {name: 'Sakura', like: {singer: 'Eason Chan', sport: 'pingpong'}}
console.log(user2); // {name: '椿生', like: {singer: 'Eason Chan', sport: 'pingpong'}}

user1.like.singer = "Anita Mui";
console.log(user1); // {name: 'Sakura', like: {singer: 'Anita Mui', sport: 'pingpong'}}
console.log(user2); // {name: '椿生', like: {singer: 'Anita Mui', sport: 'pingpong'}}

// 结论:... 实现的是对象第一层的深拷贝。更深层拷贝的是其引用值,为浅拷贝。
  1. 数组的 slice 和 concat 方法
const originArray = [1, [1, 2, 3], {a: 1}];
const cloneArray = originArray.concat();
console.log(cloneArray === originArray); // false
cloneArray[1].push(4);
cloneArray[2].a = 2; 
console.log(originArray); // [1,[1,2,3,4],{a:2}]
// originArray 中含有数组 [1,2,3] 和对象 {a:1},如果我们直接修改数组和对象,不会影响 originArray,
// 但是我们修改数组 [1,2,3] 或对象 {a:1} 时,发现 originArray 也发生了变化。 
// 结论:concat 只是对数组的第一层进行深拷贝。

const originArray = [1,[1,2,3],{a:1}];
const cloneArray = originArray.slice();
console.log(cloneArray === originArray); // false
cloneArray[1].push(4);
cloneArray[2].a = 2; 
console.log(originArray); // [1,[1,2,3,4],{a:2}]
// 结论:slice 只是对数组的第一层进行深拷贝

  1. JQuery 中的 $.extend

$.extend(deep, target, object1, object2, ....) 通过这个方法就可以实现深浅拷贝。各个参数的说明如下:

  • deep:true 表示深拷贝,false 表示浅拷贝
  • target:要拷贝的目标对象
  • object1:待拷贝的对象
var user = {
  name: "法医",
  age: 18,
  like: {
    eat: "面条",
    sport: "篮球",
  },
};

var target = {};

$.extend(target, user);
target.name = "前端猎手";
console.log(user); //{name: '前端猎手', like: {eat: '面条', sport: '篮球'}}
console.log(target); //{name: '前端猎手', like: {eat: '面条', sport: '篮球'}}

深拷贝

实现思想:通过递归思维,遍历所需进行深拷贝的对象,直到里边的都是基本数据类型,然后再进行赋值,就是深度拷贝。

  1. JSON.parse(JSON.stringify(待拷贝对象))

能实现普通对象(function, Symbol(), undefined 这三种除外)的深拷贝;而实现function, Symbol(), undefined 这三种时,会造成数据丢失。

let test = {
  fun: function(){},
  abc: undefined,
  sym: Symbol(),
  def: 'aaa',
  fn: function(){},
};

console.log(JSON.parse(JSON.stringify(test)));   // {def: 'aaa'}
  1. JQuery 中的 $.extend
var user = {
  name: "法医",
  age: 18,
  like: {
    eat: "面条",
    sport: "篮球",
  },
};

var target = {};
$.extend(true,target, user);
target.like.eat = "米饭";
console.log(user); //{name: '法医', like: {eat: '面条', sport: '篮球'}}
console.log(target); //{name: '法医', like: {eat: '米饭', sport: '篮球'}}

  1. 手写递归的方式来实现深拷贝
//定义检测数据类型的功能函数   
function checkedType(target) {
    return Object.prototype.toString.call(target).slice(8, -1)
}

//实现深度克隆---对象/数组 
function deepClone(target) {
    //判断拷贝的数据类型   
    //初始化变量result 成为最终克隆的数据     
    let result, targetType = checkedType(target)  //这里的意思是相当于let result; let targetType = checkedType(target);
    if (targetType === 'Object') {
        result = {}
    } else if (targetType === 'Array') {
        result = []
    } else {
        return target
    }

    //遍历目标数据
    for (let i in target) {
        //获取遍历数据结构的每一项值。
        let value = target[i]
        //判断目标结构里的每一值是否存在对象/数组
        if (checkedType(value) === 'Object' || checkedType(value) === 'Array') { //对象/数组里嵌套了对象/数组
            //继续遍历获取到value值
            result[i] = deepClone(value)
        } else { //获取到value值是基本的数据类型或者是函数。
            result[i] = value;
        }
    }

    return result
}
// 深拷贝:对对象内部进行深拷贝,支持 Array、Date、RegExp、DOM
    function deepCopy(params) {
      // 如果不是对象则退出(可停止递归)
      if (typeof params !== 'object') return;
      // 深拷贝初始值:对象/数组
      let newObj = (params instanceof Array) ? [] : {};
      // 使用 for-in 循环对象属性(包括原型链上的属性)
      for (let i in params) {
        // 只访问对象自身属性
        if (params.hasOwnProperty(i)) {
          // 当前属性还未存在于新对象中时
          if (!(i in newObj)) {
            if (params[i] instanceof Date) {
              // 判断日期类型
              newObj[i] = new Date(params[i].getTime());
            } else if (params[i] instanceof RegExp) {
              // 判断正则类型
              newObj[i] = new RegExp(params[i]);
            } else if ((typeof params[i] === 'object') && params[i].nodeType === 1) {
              // 判断 DOM 元素节点
              let domEle = document.getElementsByTagName(params[i].nodeName)[0];
              newObj[i] = domEle.cloneNode(true);
            } else {
              // 当元素属于对象(排除 Date、RegExp、DOM)类型时递归拷贝
              newObj[i] = (typeof params[i] === 'object') ? deepCopy(params[i]) : params[i];
            }
          }
        }
      }

      return newObj;
    }

你可能感兴趣的:(JavaScript,&,TypeScript,javascript)