js中的 赋值 浅拷贝 和 深拷贝 详细解读

js数据类型主要分基本数据类型和引用数据类型。前者包括Number,String等,后者主要是Object,因此以下会针对不同的数据类型来分析,需要的朋友可以参考一下

基本数据类型(Primary Data Types):

  • String(字符串)

  • Number(数字)

  • Boolean(布尔值)

  • Null(空值)

  • Undefined(未定义)

  • Symbol(符号,ES6 新增)

  • BigInt(大整数,ES2020 新增)

引用数据类型(Reference Data Types):

  • Object(对象)

  • Array(数组)

  • Function(函数)

  • Date(日期)

  • RegExp(正则表达式)

  • Map(映射)

  • Set(集合)

  • WeakMap(弱映射)

  • WeakSet(弱集合)

前言:在学习下面文章前我们简单了解一下的内存的知识,以下先简要提一下

1、js内存

js内存,或者说大部分语言的内存都分为栈和堆。基本数据类型的变量值分配在栈上,引用数据类型的变量值分配在堆上,栈中只是存储具体堆中对象的地址。

(1)基本类型:
5种基本数据类型Undefined、Null、Boolean、Number 和 String,变量是直接按值存放的,存放在栈内存中的简单数据段,可以直接访问。

(2)引用类型:
存放在堆内存中的对象,变量保存的是一个指针,这个指针指向另一个位置。当需要访问引用类型(如对象,数组等)的值时,首先从栈中获得该对象的地址指针,然后再从堆内存中取得所需的数据。

JavaScript存储对象都是存地址的,所以浅拷贝会导致 obj1 和obj2 指向同一块内存地址。改变了其中一方的内容,都是在原来的内存上做修改会导致拷贝对象和源对象都发生改变,而深拷贝是开辟一块新的内存地址,将原对象的各个属性逐个复制进去。对拷贝对象和源对象各自的操作互不影响。

2、赋值

对于基本数据类型,赋值操作是拷贝,即新旧变量不会相互影响。

  let a = 1
  let b = a
  b = 2
  console.log(a) // 1
  console.log(b) // 2

对于引用数据类型,赋值操作只是在栈中新增一个指向堆中对象的变量,即复制引用地址。新旧变量之间会互相影响,即在新变量上改变对象值,旧变量对应值也会改变。

  let a = {
    name: "mike"
  }
  let b = a
  b.name = "jack"
  console.log(a) // {name: "jack"}

3、浅拷贝

对于基本数据类型和不具有嵌套对象的数据,均是拷贝操作,新旧变量之间不会相互影响。

  let a = {
    name: "mike"
  }
  let b = {}
  b.name = a.name
  b.name = "jack"
  console.log(a) // {name: "mike"}

但是对于具有嵌套对象的数据,浅拷贝只拷贝第一层对象,深层次的值仍然是复制引用地址。

  let a = {
    name: "mike",
    language: {
      first: "english",
      second: "chinese"
    }
  }
  let b = {}
  b.name = a.name
  b.name = "jack"
  b.language = a.language
  b.language.first = "japanese"
  console.log(a) // {"name":"mike", language : {first: "japanese", second: "chinese"}}

js实现浅拷贝,思想:遍历target的每个属性,将起属性名和值赋值给新变量。

如果你明白了赋值的含义,那么在代码的第四行,当此时的target[key]的值是对象的时候,通过赋值赋予新变量,本质上是复制引用数据类型在堆中的地址,就不难理解为什么浅拷贝对于是否是嵌套对象的有不同结果了。

  function shallowCopy(target) {
    let result = {}
    for (const key in target) {
      result[key] = target[key]
    }
    return result
  }

4、深拷贝

深拷贝是完完全全的拷贝,新旧变量之间不会相互影响。

对于参数是否是对象有不同的处理方法,如果是对象,对于对象的每个属性和值赋值然后递归处理; 否则直接返回。

  function clone(target) {
    if (typeof target === "object") {
      //判断是否是数组
      let result = Array.isArray(target) ? [] : {}
      for (const key in target) {
        result[key] = clone(target[key])
      }
      return result
    }
    else {
      return target
    }
  }

到此这篇关于js中的赋值 浅拷贝和深拷贝详细的文章就介绍到这了

 **************************************************************************************************************

补充——浅拷贝和深拷贝的更加完善的函数

  // 浅拷贝
  const shallowClone = target => {
    // 基本数据类型直接返回
    if (typeof target === 'object' && target !== null) {
      // 获取target 的构造体
      const constructor = target.constructor
      // 如果构造体为以下几种类型直接返回
      if (/^(Function|RegExp|Date|Map|Set)$/i.test(constructor.name)) {
        return target
      }
      // 判断是否是一个数组
      const cloneTarget = Array.isArray(target) ? [] : {}
      for (prop in target) {
        // 只拷贝其自身的属性
        if (target.hasOwnProperty(prop)) {
          cloneTarget[prop] = target[prop]
        }
      }
      return cloneTarget
    } else {
      return target
    }
  }
  // 深拷贝
  const completeDeepClone = (target, map = new WeakMap()) => {
    // 基本数据类型,直接返回
    if (typeof target !== 'object' || target === null) {
      return target
    }
    // 函数 正则 日期 ES6新对象,执行构造题,返回新的对象
    const constructor = target.constructor
    if (/^(Function|RegExp|Date|Map|Set)$/i.test(constructor.name)) {
      return new constructor(target)
    }
    // map标记每一个出现过的属性,避免循环引用
    if (map.get(target)) {
      return map.get(target)
    }
    map.set(target, true)
    const cloneTarget = Array.isArray(target) ? [] : {}
    for (prop in target) {
      // 实现一个深拷贝函数通常需要递归地检查每个属性,如果属性值是对象,则递归调用自身进行拷贝;否则,直接复制该属性值。
      if (target.hasOwnProperty(prop)) {
        cloneTarget[prop] = completeDeepClone(target[prop], map)
      }
    }
    return cloneTarget
  }

补充——骚操作之JSON.parse(JSON.stringify(obj))

  let obj5 = {
    name: "mary",
    age: 17,
    grade: {
      maths: 60,
      arrGrade: [1, 2, 3],
    },
    arr: [1, 2, 3],
    fn: function add(str) {
      console.log(str)
    },
    undefinedData: undefined,
    RegExp: /^([0]{2}|0[1-9]|[1-9])\d*$/,
    date: new Date()
  }
  let obj6 = JSON.parse(JSON.stringify(obj5))
  obj6.age = 25
  obj6.grade.maths = 70
  console.log("obj5: ", obj5)
  console.log("obj6: ", obj6)

 

缺陷:

1、属性值为函数和undefined的属性会丢失

2、属性值为正则表达式的会变成{}

3、属性值为时间对象的会变成时间字符串

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值