了解JS —— 拷贝方式

59 阅读3分钟

前端的开发过程中,很多时候都需要对数据进行处理,而拷贝则可以获取一个独立的数据进行修改,不造成篡改数据的混乱。

在js中有 3 中数据拷贝的方式

赋值 基本数据类型赋值 则是开辟一块新的空间存储两者之间互不相干 引用数据类型 指向同一个对象,相互之间会影响 而深拷贝这可以解决这个问题

浅拷贝:对于基本数据类型---是对值的复制,对于引用数据类型---是对对象的地址进行复制。浅拷贝只复制指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存。

对象浅拷贝

// 语法
 1Object.assign({},originalObj) // 将原对象自身可以枚举的属性拷贝给新对象,对对象则是引用地址,后返回新对象
 
let obj = {a:"a",b:{c:"c"}}
// 如果 newObj["b"]的值被修改也就等同于 obj["b"]的值被修改 因为两种引用同一个地址
let newObj = Object.assign({},obj) //{a:"a",b:{c:"c"}}

特点:
   1、会拷贝源对象自身的属性(不拷贝继承属性)
   2、不会拷贝对象不可枚举的属性
   3undefinednull无法转换成对象,它们不能成为Object.assign的参数,但可以作为源对象	
   4、属性名为Symbol值的属性,可以被拷贝

数组拷贝

1、数组的 concat() 方法 //合并返回新的数组
  let arr1 = [1,2,3,4]
  let newarr = arr1.concat() //[1,2,3,4]
  
2、数组的 slice() 方法 //截取返回新的数组
  let arr1 = [1,2,3,4]
  let newarr = obj.slice() //[1,2,3,4]  

扩展运算符

let arr = {l:[1,2,3],age:"18"}
let newarr = {...arr} //{l:[1,2,3],age:"18"}

手动封装

function cloneShallow(source) {
  var target = {};
  for (var key in source) {
      if (Object.prototype.hasOwnProperty.call(source, key)) {
          //会返回一个布尔值,指示对象自身属性中是否具有指定的属性(也就是,是否有指定的键)
          target[key] = source[key];
      }
  }
  return target;
}

深拷贝深拷贝会重新开辟一个栈空间,同对象对应不同地址,修改一个属性另一个对象不变。

JSON.parse(JSON.stringfy(obj))

利用现代浏览器支持的JSON对象做一次中转,实现深度克隆。
优点:简单便捷
缺点:不会对函数,symbol进行转换(相当于删除了),新生成的对象不会显示
      对象属性指向对象本身(类似window.window/obj.name = obj)不能使用这种方式进行深拷贝的,否则会报错的(不能将这种循环的结构转成JSON)

目前最优解是通过递归

//判断函数
function isObject(value) {
  const valueType = typeof value
  return (value !== null) && (valueType === "object" || valueType === "function")
}

function deepClone(originValue) {
  // 判断是否是一个Set类型
  if (originValue instanceof Set) {
    return new Set([...originValue])
  }

  // 判断是否是一个Map类型
  if (originValue instanceof Map) {
    return new Map([...originValue])
  }

  // 判断如果是Symbol的value, 那么创建一个新的Symbol
  if (typeof originValue === "symbol") {
    return Symbol(originValue.description)
  }

  // 判断如果是函数类型, 那么直接使用同一个函数
  if (typeof originValue === "function") {
    return originValue
  }

  // 判断传入的originValue是否是一个对象类型
  if (!isObject(originValue)) {
    return originValue
  }

  // 判断传入的对象是数组, 还是对象
  const newObject = Array.isArray(originValue) ? []: {}
  for (const key in originValue) {
    newObject[key] = deepClone(originValue[key])
  }

  // 对Symbol的key进行特殊的处理
  const symbolKeys = Object.getOwnPropertySymbols(originValue)
  for (const sKey of symbolKeys) {
    // const newSKey = Symbol(sKey.description)
    newObject[sKey] = deepClone(originValue[sKey])
  }
  
  return newObject
}