浅拷贝和深拷贝的含义及实现

含义

当你复制一个值给另一个值的时候,假如复制后的值跟之前的值不会再有任何关系,就叫深拷贝,否则就叫浅拷贝。
那么什么时候才会有关系,什么时候没有呢?例子:

let str = 'string'
let strA = str
strA = 'changed'
console.log(str, strA) //  string, changed 深拷贝

let obj = {name: 'sifan'}
let objA = obj
objA.age = 22
console.log(obj, objA) // {name: 'sifan', age: 22}, {name: 'sifan', age: 22} 浅拷贝

str为什么不变呢?obj又为什么变呢?这个时候需要引入基本类型的坑了。
JavaScript基本类型有number,string,boolean,null,undefined,symbel(ES6新增),Bigint(ES10新增),还有引用类型object => Array,Function,Date, Regexp,对于不同的类型存储方式也不同。
基本类型使用栈内存,它们的数据在一个栈当中,这种也叫做值类型:

引用类型使用堆内存,它们的名在栈当中,但是值在堆里面,名字指向的是位置:

现在懂了吧,基本类型两个存的值都是分开的,所以很明显八竿子打不着;引用类型的堆地址相同,指向的就是同一个堆,所以改变值会让里面的东西都发生改变。

那么怎样才能让它不指向同一个堆地址呢,很简单,让objA指向新的地址就可以了,也就是在堆里面再开辟一个空间,这样他们就不会有关联了。

实现方法

JSON

缺点:会忽略undefined,symbol,不能序列化函数,不能解决循环引用的对象

    let obj = {
      name: 'sifan',
      age: 21,
      hobby: ['play game','book'],
      cb: function() {
        console.log('wa ou')
      },
      reg: /"/g,
      data: new Date()
    }
    let objA = JSON.parse(JSON.stringify(obj))
    objA.sex = 'male'
    console.log(obj, objA);

递归实现

    let obj = {
      name: 'sifan',
      age: 21,
      hobby: ['play game','book'],
      cb: function() {
        console.log('wa ou')
      },
      reg: /"/g,
      data: new Date()
    }

    function deepClone(paramter) {
      let result = Array.isArray(paramter) ? [] : {}
      if(paramter && typeof paramter === "object") {
          if(Object.prototype.toString.call(paramter) === '[object RegExp]') {
            result = new RegExp(paramter)
          } else if(Object.prototype.toString.call(paramter) === '[object Date]') {
            result = new Date(paramter)
          } else {
            for(let i in paramter) {
              if(paramter[i] && typeof paramter[i] === 'object') {
                result[i] = deepClone(paramter[i])
              } else {
                result[i] = paramter[i]
              }            
            }
          }
      }
      return result
    }

    let objA = deepClone(obj)
    console.log(obj, objA);

嘿嘿嘿,看,简简单单你就把他写出来了。所以做什么细心就欧克了。冲冲冲!!!

lodash

npm install lodash // 安装依赖
import _ from 'lodash';
const objA = _.cloneDeep(obj);

还有哦

对于简单数组的深拷贝可以非常简单:
slice

let arr = [1,2,3,4,5,6]
let brr = arr.slice()
arr.push(100)
brr.shift()
console.log(arr, brr)

...运算符

let arr = [1,2,3,4,5,6]
let brr = [...arr]
arr.push(100)
brr.shift()
console.log(arr, brr)

分割线

浅拷贝实现
对象

Object.assign()

assign详解

...(es6)

注意,需要用{}去接。 // {...obj}
数组

Array.prototype.slice(start, end)

基于当前数组,从开始下标(包含),到结束下标(不包含),返回一个新数组,不影响原数组

Array.prototype.concat(多个参数)

合并参数为一个新的数组

Array.from(参数)

将一个类数组转化为数组

...(es6扩展运算符)

posted @ 2021-09-26 13:38  卿六  阅读(179)  评论(1编辑  收藏  举报