随着前端技术的发展,数据驱动视图的框架设计理念越来越火,而说到数据,就不得不探讨浅拷贝和深拷贝。

学习目的:

1,什么是深拷贝

2,什么是浅拷贝

3,深拷贝和浅拷贝的本质区别

4,深拷贝的方法和使用场景

5,浅拷贝的方法和使用场景

6,怎么比较层次较深的对象是否相等(发生改变)

接下来逐个探讨:

1,深拷贝:


    
     
  1. 1 ,是指拷贝一个对象时,不仅仅把对象的引用进行复制,还把该对象引用的值也一起拷贝;
  2. 2 ,源对象与拷贝对象互相独立,其中任何一个对象的改动都不会对另外一个对象造成影响

2,浅拷贝


    
     
  1. 1 ,指的是拷贝一个对象时,仅仅拷贝对象的引用进行拷贝,但是拷贝对象和源对象还是引用同一份实体;
  2. 2 ,其中一个对象的改变都会影响到另一个对象

3,深拷贝和浅拷贝的本质区别

假设B复制了A,当修改A时,看B是否会发生变化,如果B也跟着变了,说明这是浅拷贝,拿人手短,如果B没变,那就是深拷贝,自食其力
   
    

————————————————————————————————————————————————————————

在讨论以下几个知识点的时候会用到几个概念:栈堆基本数据类型引用数据类型


    
     
  1. 1,基本数据类型:
  2. 字符串( String)、数字( Number)、布尔( Boolean)、对空(Null)、未定义(Undefined)、 Symbol
  3. 2,引用数据类型:对象( Object)、数组( Array)、函数( Function)。

基本数据类型和引用数据类型存储区别:这里用到了堆栈的概念


    
     
  1. 什么是堆栈
  2. 1 ,堆栈是两种数据结构
  3. 2 ,堆栈都是一种数据项按序排列的数据结构,只能在一端(称为栈顶(top))对数据项进行插入和删除
  4. 堆和栈的区别:
  5. 1 ,堆,队列优先,先进先出(FIFO—first in first out)
  6. 2 ,栈,先进后出(FILO—First-In/Last-Out)

基本数据类型存储:名值存储在栈内存中

执行b=a复制时,栈内存会新开辟一个内存

引用数据类型名存在栈内存中,值存在于堆内存中,但是栈内存会提供一个引用的地址指向堆内存中的值

对于引用数据类型:当b=a进行拷贝时,其实复制的是a的引用地址,而并非堆里面的值。


    
     
  1. let a = [ 0, 1, 2, 3, 4];
  2. const b = a;

由上面的例子可以看出深拷贝实现的原理

即在拷贝引用数据类型时,不仅仅是拷贝栈内存指针,而是要在堆内存中重新开辟一块新内存。

4,深拷贝的方法和使用场景


    
     
  1. //深拷贝方法1 递归拷贝
  2. function deepClone(obj){
  3. let objClone = Array.isArray(obj)?[]:{};
  4. if(obj && typeof obj=== "object"){
  5. for(key in obj){
  6. if(obj.hasOwnProperty(key)){
  7. //判断ojb子元素是否为对象,如果是,递归复制
  8. if(obj[key]&& typeof obj[key] === "object"){
  9. objClone[key] = deepClone(obj[key]);
  10. } else{
  11. //如果不是,简单复制
  12. objClone[key] = obj[key];
  13. }
  14. }
  15. }
  16. }
  17. return objClone;
  18. }
  19. let a=[ 1, 2, 3, 4],
  20. b=deepClone(a);
  21. a[ 0]= 2;
  22. console.log(a,b);

    
     
  1. //深度拷贝2,用JSON对象的parse和stringify
  2. function deepClone(obj){
  3. let _obj = JSON.stringify(obj),
  4. objClone = JSON.parse(_obj);
  5. return objClone
  6. }
  7. let a=[ 0, 1,[ 2, 3], 4],
  8. b=deepClone(a);
  9. a[ 0]= 1;
  10. a[ 2][ 0]= 1;
  11. console.log(a,b);

    
     
  1. //深度拷贝方法3,JQ的extend方法
  2. //$.extend( [deep ], target, object1 [, objectN ] )
  3. //deep表示是否深拷贝,为true为深拷贝,为false,则为浅拷贝
  4. //target Object类型 目标对象,其他对象的成员属性将被附加到该对象上。
  5. //object1 objectN可选。 Object类型 第一个以及第N个被合并的对象。
  6. let a=[ 0, 1,[ 2, 3], 4],
  7. b=$.extend( true,[],a);
  8. a[ 0]= 1;
  9. a[ 2][ 0]= 1;
  10. console.log(a,b);

深拷贝应用场景:不对原数据对象造成影响

1、从服务器fetch到数据之后我将其存放在store中,通过props传递给界面,然后我需要对这堆数据进行修改,那涉及到修改就一定有保存和取消,所以我们需要将这堆数据拷贝到其他地方(网友的经历)

2、当你想使用某个对象的值,在修改时不想修改原对象,那么可以用深拷贝来弄一个新的内存对象。

————————————————————————————————————————————————————————

5,浅拷贝的方法和使用场景


    
     
  1. //浅拷贝方法1:直接赋值
  2. let a = {
  3. b: 1,
  4. m: 'p'
  5. }
  6. const acl = a

Object.assign()解析:

1,Object.assign()用于将所有可枚举属性的值从一个或多个源对象复制到目标对象,

2,Object.assign()第一级属性深拷贝,以后级别属性浅拷贝


    
     
  1. let o ={ name: { asd: '123'}}
  2. let p = Object.assign({}, o)
  3. p.name = '123456789'
  4. console.log(p, o); //{name: "123456789"},{name: {asd: "123"}}

es6扩展运算符...  ...第一级属性深拷贝,以后级别属性浅拷贝


    
     
  1. let a={ title:{ info: "二级属性"}};
  2. let b={...a, content: "一个教程"};
  3. b.title.info = '二级属性都被修改了'
  4. console.log(a.title);
  5. console.log(b.title);

6,怎么比较层次较深的对象是否相等(发生改变)

除了使用第三发库还有以下方法

① 方法一:通过JSON.stringfy(obj)来判断两个对象转后的字符串是否相等

优点:用法简单,对于顺序相同的两个对象可以快速进行比较得到结果
缺点:这种方法有限制就是当两个对比的对象中key的顺序不是完全相同时会比较出错

② 方法二:对Object扩展一个方法chargeObjectEqual


    
     
  1. // 对Object扩展一个方法chargeObjectEqual
  2. Object.prototype.chargeObjectEqual = function(obj){
  3. // 当前Object对象
  4. var propsCurr = Object.getOwnPropertyNames( this);
  5. // 要比较的另外一个Object对象
  6. var propsCompare = Object.getOwnPropertyNames(obj);
  7. if (propsCurr.length != propsCompare.length) {
  8. return false;
  9. }
  10. for ( var i = 0,max = propsCurr.length; i < max; i++) {
  11. var propName = propsCurr[i];
  12. if ( this[propName] !== obj[propName]) {
  13. return false;
  14. }
  15. }
  16. return true;
  17. }

getOwnPropertyNames该方法可以将Object对象的第一层key获取到并返回一个由第一层key组成的数组。

优点:相对方法一进行了优化,可以应对不同顺序的Object进行比较,不用担心顺序不同而对比出错
缺点:从方法中可以看到只能获取到第一层的key组成的数组,当对象是复合对象时无法进行多层对象的比较

③ 方法三:封装函数方法,递归比较


    
     
  1. function deepCompare(x, y) {
  2. var i, l, leftChain, rightChain;
  3. function compare2Objects(x, y) {
  4. var p;
  5. // remember that NaN === NaN returns false
  6. // and isNaN(undefined) returns true
  7. if ( isNaN(x) && isNaN(y) && typeof x === 'number' && typeof y === 'number') {
  8. return true;
  9. }
  10. // Compare primitives and functions.
  11. // Check if both arguments link to the same object.
  12. // Especially useful on the step where we compare prototypes
  13. if (x === y) {
  14. return true;
  15. }
  16. // Works in case when functions are created in constructor.
  17. // Comparing dates is a common scenario. Another built-ins?
  18. // We can even handle functions passed across iframes
  19. if (( typeof x === 'function' && typeof y === 'function') ||
  20. (x instanceof Date && y instanceof Date) ||
  21. (x instanceof RegExp && y instanceof RegExp) ||
  22. (x instanceof String && y instanceof String) ||
  23. (x instanceof Number && y instanceof Number)) {
  24. return x.toString() === y.toString();
  25. }
  26. // At last checking prototypes as good as we can
  27. if (!(x instanceof Object && y instanceof Object)) {
  28. return false;
  29. }
  30. if (x.isPrototypeOf(y) || y.isPrototypeOf(x)) {
  31. return false;
  32. }
  33. if (x.constructor !== y.constructor) {
  34. return false;
  35. }
  36. if (x.prototype !== y.prototype) {
  37. return false;
  38. }
  39. // Check for infinitive linking loops
  40. if (leftChain.indexOf(x) > -1 || rightChain.indexOf(y) > -1) {
  41. return false;
  42. }
  43. // Quick checking of one object being a subset of another.
  44. // todo: cache the structure of arguments[0] for performance
  45. for (p in y) {
  46. if (y.hasOwnProperty(p) !== x.hasOwnProperty(p)) {
  47. return false;
  48. } else if ( typeof y[p] !== typeof x[p]) {
  49. return false;
  50. }
  51. }
  52. for (p in x) {
  53. if (y.hasOwnProperty(p) !== x.hasOwnProperty(p)) {
  54. return false;
  55. } else if ( typeof y[p] !== typeof x[p]) {
  56. return false;
  57. }
  58. switch ( typeof(x[p])) {
  59. case 'object':
  60. case 'function':
  61. leftChain.push(x);
  62. rightChain.push(y);
  63. if (!compare2Objects(x[p], y[p])) {
  64. return false;
  65. }
  66. leftChain.pop();
  67. rightChain.pop();
  68. break;
  69. default:
  70. if (x[p] !== y[p]) {
  71. return false;
  72. }
  73. break;
  74. }
  75. }
  76. return true;
  77. }
  78. if ( arguments.length < 1) {
  79. return true; //Die silently? Don't know how to handle such case, please help...
  80. // throw "Need two or more arguments to compare";
  81. }
  82. for (i = 1, l = arguments.length; i < l; i++) {
  83. leftChain = []; //Todo: this can be cached
  84. rightChain = [];
  85. if (!compare2Objects( arguments[ 0], arguments[i])) {
  86. return false;
  87. }
  88. }
  89. return true;
  90. }

参考:CSDN

          CSDN

          CSDN

    出处:CSDN


评论关闭
IT虾米网

微信公众号号:IT虾米 (左侧二维码扫一扫)欢迎添加!

ES5和ES6数组常见方法总结