js数据类型

基本类型:(存在于栈当中)

Null,Undefined,Number,String,Boolean,Symbol

引用类型:(存在于堆当中)

Object, 数组、函数、正则等,都属于js的对象。

JavaScript不允许直接访问内存当中的位置,也就是不允许直接操作对象的内存空间。

变量的赋值(Copy)

如果是基本类型的赋值,前后互相不影响,

如果是引用类型的赋值,拷贝的其实是内存地址的引用,所以当改变其中某一个的时候,领一个也会发生改变。

浅拷贝(Shallow Copy)

什么是浅拷贝

对象属性的拷贝,如果是基本类型,拷贝的是基本类型的值,
如果是引用类型,拷贝的是内存地址的引用,
所以使用浅拷贝的话,改变其中一个,另外一个也会跟着改变。

场景

  • Object.assign()

其实是一个浅拷贝,并非一个深拷贝

拷贝的是对象中所有可枚举属性的值,从源对象复制到目标对象,并返回目标对象。

  • 展开语法 Spread
  1. let a = {
  2. name: '',
  3. info: {
  4. gender: 'man',
  5. }
  6. }
  7. let b = {...a};

其实是和Object.assign()同样的效果。

  • Array.prototype.slice和Array.prototype.concat

如果数组的项是一个基本类型的值,相互是不影响的,
如果是引用类型的值,一个改变都会发生改变。
说明sliceconcat方法是一个浅拷贝的方法。(Deep Copy)

深拷贝

什么是深拷贝

深拷贝会拷贝所有的属性,并拷贝属性指向的动态分配的内存。
深拷贝实现速度慢,花销较大,拷贝前后两个对象互不影响。

场景

  • JSON.parse(JSON.stringify(object))

不管是数组还是对象,使用序列化之后,改变前后互不影响

但是该方法有以下几个问题。

1、会忽略 undefined

忽略掉

2、会忽略 symbol

忽略掉

3、不能序列化函数

忽略掉

4、不能解决循环引用的对象(类似套娃一样)

报错,

  1. //
  2. let obj = {
  3. a: 1,
  4. b: {
  5. c: 2,
  6. d: 3
  7. }
  8. }
  9. obj.a = obj.b;
  10. obj.b.c = obj.a;
  11. let b = JSON.parse(JSON.stringify(obj));
  12. // Uncaught TypeError: Converting circular structure to JSON

5、不能正确处理new Date()

  1. // 木易杨
  2. new Date();
  3. // Mon Dec 24 2018 10:59:14 GMT+0800 (China Standard Time)
  4. JSON.stringify(new Date());
  5. // ""2018-12-24T02:59:25.776Z""
  6. JSON.parse(JSON.stringify(new Date()));
  7. // "2018-12-24T02:59:41.523Z"

解决方法转成字符串或者时间戳就好了。

  1. // 木易杨
  2. let date = (new Date()).valueOf();
  3. // 1545620645915
  4. JSON.stringify(date);
  5. // "1545620673267"
  6. JSON.parse(JSON.stringify(date));
  7. // 1545620658688

6、不能处理正则

结果为{},

  1. // toDO
  2. // PS:为什么会存在这些问题可以学习一下 JSON。

7、会抛弃对象的constructor,所有的构造函数会指向Object

  1. let Stu = function(name) {
  2. this.name = name
  3. }
  4. let stu = new Stu('fff')
  5. stu.constructor
  6. // ƒ (name) {
  7. // this.name = name
  8. // }
  9. stu.constructor === Stu // true
  10. let stuC = JSON.parse(JSON.stringify(stu))
  11. stuC.constructor
  12. // ƒ Object() { [native code] }
  13. stu.constructor === Object // true
  • jQuery.extend() 和 lodash.cloneDeep()实现深拷贝。

实现一个深拷贝

  1. /**
  2. * @Description 实现一个引用类型的深拷贝
  3. * @Author forguo
  4. * @Date 2020/1/14
  5. */
  6. // let array = [
  7. // {number: 1},
  8. // {number: 2},
  9. // {number: 3}
  10. // ];
  11. let array = {
  12. number: 1,
  13. name: 'www',
  14. info: {
  15. name: 'forguo',
  16. age: 26
  17. }
  18. };
  19. function deepCopy(obj) {
  20. // 舒适化返回结果,判断是否是数组
  21. let newobj = obj.constructor === Array ? [] : {};
  22. if (typeof obj !== 'object' || obj == null) {
  23. // obj是null,或者不是数组或者对象,直接返回即可
  24. return obj;
  25. }
  26. for (let key in obj) {
  27. // 遍历每个属性,递归拷贝
  28. if (obj.hasOwnProperty(key)) {
  29. // 保证key不是原型的属性
  30. newobj[key] = deepCopy(obj[key]);
  31. }
  32. }
  33. return newobj
  34. }
  35. Object.keys(array).map((item) => {
  36. console.log(item);
  37. });
  38. let copyArray = deepCopy(array);
  39. copyArray.number = 100;
  40. console.log(array); // [{number: 1}, { number: 2 }, { number: 3 }]
  41. console.log(copyArray); // [{number: 100}, { number: 2 }, { number: 3 }]

循环引用的解决

https://juejin.cn/post/6844903998823088141
循环引用

  1. var circle = {}
  2. circle.circle = circle
  3. //或者
  4. var a = {}, b = {}
  5. a.b = b
  6. b.a = a

对于循环引用的对象使用深拷贝会直接栈溢出。

相同引用

  1. var arr = [1,2,3]
  2. var obj = {}
  3. obj.arr1 = arr
  4. obj.arr2 = arr

而对于包含相同对象引用的问题在于,因为复制之前obj.arr1和obj.arr2是指向相同对象的,修改其中一个另一个也会改动

使用Map来解决

  1. /**
  2. * 循环引用的解决
  3. */
  4. /**
  5. * @Description 实现一个引用类型的深拷贝
  6. * @Author forguo
  7. * @Date 2020/1/14
  8. */
  9. // let array = [
  10. // {number: 1},
  11. // {number: 2},
  12. // {number: 3}
  13. // ];
  14. let array = {
  15. number: 1,
  16. name: 'www',
  17. info: {
  18. name: 'forguo',
  19. age: 26
  20. }
  21. };
  22. function deepCopy(obj, map = new Map) {
  23. // 舒适化返回结果,判断是否是数组
  24. let newobj = obj.constructor === Array ? [] : {};
  25. if (typeof obj !== 'object' || obj == null) {
  26. // obj是null,或者不是数组或者对象,直接返回即可
  27. return obj;
  28. }
  29. if (map.get(obj)) {
  30. return map.get(obj);
  31. }
  32. map.set(obj, newobj);
  33. for (let key in obj) {
  34. // 遍历每个属性,递归拷贝
  35. if (obj.hasOwnProperty(key)) {
  36. // 保证key不是原型的属性
  37. newobj[key] = deepCopy(obj[key], map);
  38. }
  39. }
  40. return newobj
  41. }
  42. // a循环引用
  43. array.a = array;
  44. let copyArray = deepCopy(array);
  45. console.log(copyArray);