Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JavaScript(一)- 深浅拷贝 #2

Open
bt2ee opened this issue Dec 17, 2020 · 0 comments
Open

JavaScript(一)- 深浅拷贝 #2

bt2ee opened this issue Dec 17, 2020 · 0 comments

Comments

@bt2ee
Copy link
Owner

bt2ee commented Dec 17, 2020

JavaScript 的类型

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

引用类型:存放在堆内存中堆对象,变量保存的是一个指针,指向堆中的对象。

正因为这种机制,当我们复制引用类型的值时,其实是在栈中复制了一个新的指针指向堆中的对象,改变其中一个对象,另一个对象也会改变。

定义

浅拷贝:对对象的属性进行复制,并不会进行递归复制。因此对于基础类型会进行值的拷贝,对于引用类型只会进行地址的拷贝,最终两个指针指向同一个地址。其中一个对象属性修改,另一个对象也会进行属性的修改。

深拷贝:递归拷贝对象的所有属性

实现

浅拷贝

Array.concat
从下面的例子来看,改变引用类型的值,原数组也会跟着改变
类似的 Array.sliceArray.from 也是同等效果

const arr = [1, 2, 3, 4, [5, 6]]
const copy = arr.concat()
copy[0] = 2
copy[4][1] = 10
arr //  [1, 2, 3, 4, [5, 10]]
copy //  [2, 2, 3, 4, [5, 10]]

Object.assign
依然是 改变引用类型的值,原数组也会跟着改变

var x = {
  a: 1,
  b: { f: { g: 1 } },
  c: [ 1, 2, 3 ]
}
var y = Object.assign({}, x)
x.a = 3
x.b.f.g = 15
x    //  { a: 3, b: { f: { g: 15 } }, c: [ 1, 2, 3 ] }
y   //   { a: 1, b: { f: { g: 15 } }, c: [ 1, 2, 3 ] }

自行实现

function cloneShallow(source) {
  var target = {}
  for (var key in source) {
    if (Object.prototype.hasOwnProperty.call(source, key)) {
      target[key] = source[key]
    }
  }
  return target
}

var a = {
  name: 'sqshada',
  book: {
    title: 'I am sqshada',
    price: '22'
  },
  a1: undefined,
  a2: null,
  a3: 123
}

var b = cloneShallow(a)
a.name = 'get new name'
a.book.price = 70

console.log(b)
  // {
  //   name: 'sqshada',
  //   book: { title: 'I am sqshada', price: 70 },
  //   a1: undefined,
  //   a2: null,
  //   a3: 123
  // }

深拷贝

JSON.stringify/parse
发现会出现属性丢失的情况
MDN中可以找到相关描述

undefined, Functions, and Symbols are not valid JSON values. If any such values are encountered during conversion they are either omitted (when found in an object) or changed to null (when found in an array). JSON.stringify() can return undefined when passing in "pure" values like JSON.stringify(function(){}) or JSON.stringify(undefined).

也就是 undefinedfunctionsymbol 会在转换过程中被自动忽略

const originObj = {
  name:'sqshada',
  message:function(){
    console.log('sqshada');
  }
}
console.log(originObj); // {name: "sqshada", message: ƒ}
const cloneObj = JSON.parse(JSON.stringify(originObj));
console.log(cloneObj); // {name: "sqshada"}

递归的方法

  • 简单实现
function cloneShallow(source) {
  var target = {}
  for (var key in source) {
    if (Object.prototype.hasOwnProperty.call(source, key)) {
      if (typeof source[key] === 'object') {
        target[key] = cloneShallow(source[key])
      } else {
        target[key] = source[key]
      }
    }
  }
  return target
}

var a = {
  name: 'sqshada',
  book: {
    title: 'I am sqshada',
    price: '22'
  },
  a1: undefined,
  a2: null,
  a3: 123
}

var b = cloneShallow(a)
a.name = 'get new name'
a.book.price = 70

console.log(b)
  // {
  //   name: 'sqshada',
  //   book: { title: 'I am sqshada', price: '22' },
  //   a1: undefined,
  //   a2: {},
  //   a3: 123
  // }

上述简单实现了深拷贝,但是缺少很多东西并且有部分漏洞

  • 兼容数组
function cloneShallow(source) {
  if (!isObject(source)) return source
  var target = Array.isArray(source) ? [] : {}
  for (var key in source) {
    if (Object.prototype.hasOwnProperty.call(source, key)) {
      if (isObject(source[key])) {
        target[key] = cloneShallow(source[key])
      } else {
        target[key] = source[key]
      }
    }
  }
  return target
}

function isObject(obj) {
  return typeof obj === 'object' && obj !== null
}

var a = {
  name: 'sqshada',
  book: {
    title: 'I am sqshada',
    price: '22'
  },
  a1: undefined,
  a2: null,
  a3: 123,
  a4: [1, 2, 3, 4, [5, 6]]
}

var b = cloneShallow(a)
a.name = 'get new name'
a.book.price = 70
a.a4[4][1] = 88

console.log(b)
  // {
  //   name: 'sqshada',
  //   book: { title: 'I am sqshada', price: '22' },
  //   a1: undefined,
  //   a2: null,
  //   a3: 123,
  //   a4: [ 1, 2, 3, 4, [ 5, 6 ] ]
  // }
  • 处理循环引用
function cloneShallow(source, hash = new WeakMap()) {
  if (!isObject(source)) return source

  if (hash.has(source)) return hash.get(source)

  var target = Array.isArray(source) ? [] : {}
  hash.set(source, target)

  for (var key in source) {
    if (Object.prototype.hasOwnProperty.call(source, key)) {
      if (isObject(source[key])) {
        target[key] = cloneShallow(source[key], hash)
      } else {
        target[key] = source[key]
      }
    }
  }
  return target
}

function isObject(obj) {
  return typeof obj === 'object' && obj !== null
}

var a = {
  name: 'sqshada',
  book: {
    title: 'I am sqshada',
    price: '22'
  },
  a1: undefined,
  a2: null,
  a3: 123,
  a4: [1, 2, 3, 4, [5, 6]]
}

a.circleRef = a
var b = cloneShallow(a)

console.log(b)
  // {
  //   a1: undefined
  //   a2: null
  //   a3: 123
  //   a4: (5)[1, 2, 3, 4, Array(2)]
  //   book: { title: "I am sqshada", price: "22" }
  //   circleRef: { name: "sqshada", book: {…}, a1: undefined, a2: null, a3: 123, … }
  //   name: "sqshada"
  // }
  • 处理 Symbol

getOwnPropertySymbols-MDN

function cloneShallow(source, hash = new WeakMap()) {
  if (!isObject(source)) return source

  if (hash.has(source)) return hash.get(source)

  var target = Array.isArray(source) ? [] : {}
  hash.set(source, target)

  let symKeys = Object.getOwnPropertySymbols(source)
  if (symKeys.length) {
    symKeys.forEach(symKey => {
      if (isObject(source[symKey])) {
        target[symKey] = cloneShallow(source[symKey], hash)
      } else {
        target[symKey] = source[symKey]
      }
    })
  }

  for (var key in source) {
    if (Object.prototype.hasOwnProperty.call(source, key)) {
      if (isObject(source[key])) {
        target[key] = cloneShallow(source[key], hash)
      } else {
        target[key] = source[key]
      }
    }
  }
  return target
}

function isObject(obj) {
  return typeof obj === 'object' && obj !== null
}

var a = {
  name: 'sqshada',
  book: {
    title: 'I am sqshada',
    price: '22'
  },
  a1: undefined,
  a2: null,
  a3: 123,
  a4: [1, 2, 3, 4, [5, 6]]
}
var sym1 = Symbol('a')
var sym2 = Symbol.for('b')


a[sym1] = "localSymbol";
a[sym2] = "globalSymbol";

var b = cloneShallow(a);

console.log(b);

参考文章

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant