当前位置:网站首页>Jsliang job series - 09 - hand written shallow copy and deep copy

Jsliang job series - 09 - hand written shallow copy and deep copy

2020-11-10 10:44:00 jsliang

One Catalog

No frills front end , What's the difference with salted fish

Catalog
One Catalog
Two Preface
3、 ... and Shallow copy
3.1 A light handwritten copy
3.2 Object.assign
3.3 Array API
Four Deep copy
4.1 Hand written deep copy
4.2 JSON.parse(JSON.stringify())
4.3 function library Lodash
4.4 5、 ... and frame jQuery
5、 ... and reference

Two Preface

Return directory

interviewer : How to shallow copy and deep copy an array ?

To understand why you ask this question , We have to start with the citation address and the citation value :

  • We copy and reference the underlying data type , Copy the corresponding values
let number1 = 1;
let number2 = 2;

console.log(number1, number2); // 1 2

number2 = 3;

console.log(number1, number2); // 1 3

//  Copy basic data type , And make changes , It doesn't change the original value (number1)
  • We copy and reference complex data types , It's the address of the copy
let obj1 = {
  name: 'jsliang',
};

let obj2 = obj1;

console.log(obj1); // { name: 'jsliang' }
console.log(obj2); // { name: 'jsliang' }

obj2.name = 'zhazhaliang';

console.log(obj1); // { name: 'zhazhaliang' }
console.log(obj2); // { name: 'zhazhaliang' }

//  Copy reference data type , And make changes , Will change the original value (obj1)

Simply speaking ,ArrayObject These complex data types , They open up a space of their own to hold data , They refer to the corresponding address .

You can take Array The data in it is like a family , So look for this family , Is it necessary to have an account , If you modify a family member , Did you change the information of this family's account ?

therefore , Your daily copy of arrays or objects , It's a copy of their address .

If you want to modify it without polluting the original content , You need to do a deep copy .

3、 ... and Shallow copy

Return directory

How to realize shallow copy , I usually answer 3 In this case :

  1. A light handwritten copy
  2. Use Object.assign
  3. Using arrays API, Such as concat perhaps slice And the extension operator

3.1 A light handwritten copy

Return directory
const shallowClone = (target) => {
  //  Set copy result 
  const result = [];

  //  adopt  let ... in ...  Traverse 
  for (let prop in target) {

    //  If the attribute is  target  Self 
    //  Not through the prototype chain , Then assign a value to it 
    if (target.hasOwnProperty(prop)) {
      result[prop] = target[prop];
    }
  }

  //  Return the copy result 
  return result;
}
  • for...in: Traverse Object object arr1, Enumerate enumerable values .
  • hasOwnProperty(): Check if the enumeration value belongs to the object arr1, If it's inherited, remove it , Copy if it's your own .

Of course , This can only copy arrays , What about copying objects ?

We “ slightly ” Perfect it :

//  adopt  Object.prototype.toString.call(xxx)  You can judge the attributes of a target 
// Object  Returns the  [Function Object]
// Array  Returns the  [Function Array]
const checkedType = (target) => {
  return Object.prototype.toString.call(target).slice(8, -1);
}

//  Shallow copy 
const shallowClone = (target) => {
  //  Set result 
  let result;

  //  If the target is an object , Then set it to object 
  if (checkedType(target) === 'Object') {
    result = {};
  } else if (checkedType(target) === 'Array') { //  If the target is an array , Then set it to an array 
    result = [];
  }

  //  adopt  let...in...  Traversing arrays or objects 
  //  And pass  hasOwnProperty  Judge whether it is an attribute of itself 
  for (let i in target) {
    if (target.hasOwnProperty(i)) {
      result[i] = target[i];
    }
  }

  //  Return copy result 
  return result;
};

A case study is a test :

/* ————————————————  Test array  ———————————————— */
const arr1 = [1, 2, ['jsliang', 'JavaScriptLiang'], 4];
Array.prototype.sayHello = () => console.log(' Hello ');
const arr2 = shallowClone(arr1);
arr2[2].push('LiangJunrong');
arr2[3] = 5;

console.log(arr1); // [ 1, 2, [ 'jsliang', 'JavaScriptLiang', 'LiangJunrong' ], 4 ]
console.log(arr2); // [ 1, 2, [ 'jsliang', 'JavaScriptLiang', 'LiangJunrong' ], 5 ]

/* ————————————————  Test object  ———————————————— */
const obj1 = {
  name: 'jsliang',
  like: ['eat', 'play'],
}
Object.prototype.sayHello = () => console.log(' Hello ');
const obj2 = shallowClone(obj1);
console.log(obj2);
obj2.name = 'zhazhaliang';
obj2.like.push('sleep');

console.log(obj1); // { name: 'jsliang', like: [ 'eat', 'play', 'sleep' ] }
console.log(obj2); // { name: 'zhazhaliang', like: [ 'eat', 'play', 'sleep' ] }

3.2 Object.assign

Return directory
const obj1 = {
  username: 'LiangJunrong',
  skill: {
    play: ['basketball', 'computer game'],
    read: 'book',
  },
  girlfriend: ['1  Spare wheel No ', '2  Spare wheel No ', '3  Spare wheel No '],
};

const obj2 = Object.assign({}, obj1);
obj2.username = 'jsliang'; //  Modify the basic type 
obj2.skill.read = 'computer book'; //  Modify the basic type of the second floor 
obj2.skill.play = ['footboll']; //  Modify the second level reference type 
obj2.girlfriend = [' The previous ones were all blatant !']; //  Modify a layer of reference type 

console.log(obj1);
// { username: 'LiangJunrong',
//   skill: { play: [ 'footboll' ], read: 'computer book' },
//   girlfriend: [ '1  Spare wheel No ', '2  Spare wheel No ', '3  Spare wheel No ' ] }
console.log(obj2);
// { username: 'jsliang',
//   skill: { play: [ 'footboll' ], read: 'computer book' },
//   girlfriend: [ ' The previous ones were all blatant !' ] }

Object.assign Used to copy objects .

It's for the first layer , It's a full copy ; For the second floor and above , It's a simple copy .

3.3 Array API

Return directory
  • Array.prototype.concat(target)concat() It's a built-in method for arrays , The user merges two or more arrays . This method does not change the existing array , Instead, it returns a new array .const b = [].concat(a)
  • Array.prototype.slice(start, end)slice() It's also a built-in method for arrays , This method will return a new object .slice() It doesn't change the original array .const b = a.slice()
  • Expand operator [...arr] You can get a shallow copy of the new array .

Four Deep copy

Return directory

If the interviewer asks you about the implementation of deep copy ,jsliang I think the answer should be like this :

  1. Hand written deep copy
  2. With the help of JSON.parse(JSON.stringify())
  3. With the help of third-party libraries Lodash、jQuery etc.

Then if the interviewer asks how to do it by hand , Use the code in the next section !

4.1 Hand written deep copy

Return directory
//  Define function to detect data type 
const checkedType = (target) => {
  return Object.prototype.toString.call(target).slice(8, -1);
}

//  Implement deep clone objects or arrays 
const deepClone = (target) => {
  //  Determine the type of data copied 
  //  Initialize variable  result  Become the final data 
  let result, targetType = checkedType(target);
  if (targetType === 'Object') {
    result = {};
  } else if (targetType === 'Array') {
    result = [];
  } else {
    return target;
  }

  //  Traverse target data 
  for (let i in target) {
    //  Get every value of traversal data structure 
    let value = target[i];
    //  Determine whether there is an object or an array for each value in the target structure 
    if (checkedType(value) === 'Object' || checkedType(value) === 'Array') {
      //  If objects or arrays are nested in them , So continue to traverse 
      result[i] = deepClone(value);
    } else {
      //  Otherwise, assign directly 
      result[i] = value;
    }
  }

  //  Return the final value 
  return result;
}

Note that there is still a problem with this deep copy , for example :

  • Circular reference ( adopt Map perhaps Set Record storage space )(WeakMap It's better here )
  • Peer reference
  • …… etc.

I will not talk about specific solutions here .

Take a simple test :

/* ————————————————  Test array  ———————————————— */
const arr1 = [1, 2, ['jsliang', 'JavaScriptLiang'], 4];
Array.prototype.sayHello = () => console.log(' Hello ');
const arr2 = deepClone(arr1);
arr2[2].push('LiangJunrong');
arr2[3] = 5;

console.log(arr1); // [ 1, 2, [ 'jsliang', 'JavaScriptLiang' ], 4 ]
console.log(arr2); // [ 1, 2, [ 'jsliang', 'JavaScriptLiang', 'LiangJunrong' ], 5 ]

/* ————————————————  Test object  ———————————————— */
const obj1 = {
  name: 'jsliang',
  like: ['eat', 'play'],
}
Object.prototype.sayHello = () => console.log(' Hello ');
const obj2 = deepClone(obj1);
obj2.name = 'zhazhaliang';
obj2.like.push('sleep');

console.log(obj1); // { name: 'jsliang', like: [ 'eat', 'play' ] }
console.log(obj2); // { name: 'zhazhaliang', like: [ 'eat', 'play', 'sleep' ] } 

4.2 JSON.parse(JSON.stringify())

Return directory
  • JSON.stringify(): Turn an object into JSON character string .
  • JSON.parse(): Parse a string into an object .

adopt JSON.parse(JSON.stringify()) take JavaScript Object deserialization ( convert to JSON character string ), And then reduce it to JavaScript object , As soon as we go, we have a new object , And objects will open up new stacks , To achieve deep copy .

Be careful , The limitations of this method :
1、 You can't store functions or Undefined, Otherwise, the function or Undefined;
2、 Don't store time objects , Otherwise it will be in string form ;
3、 Cannot be stored RegExp、Error object , Otherwise it will become an empty object ;
4、 Cannot be stored NaN、Infinity、-Infinity, Otherwise it will become null;
5、…… Please fill in more holes by yourself , To be specific JavaScript and JSON Differences exist , If the two are not compatible, there will be problems .
const arr1 = [
  1,
  {
    username: 'jsliang',
  },
];

let arr2 = JSON.parse(JSON.stringify(arr1));
arr2[0] = 2;
arr2[1].username = 'LiangJunrong';
console.log(arr1);
// [ 1, { username: 'jsliang' } ]
console.log(arr2);
// [ 2, { username: 'LiangJunrong' } ]

4.3 function library Lodash

Return directory

Lodash As a beloved 、 first-class JavaScript function library / Tool library , It has a very easy to use package of good functions , You can try :

Let's take a look at its cloneDeep() Method :

You can see , This method will recursively copy value.

ad locum , Let's experience it cloneDeep()

//  npm i -S lodash
var _ = require('lodash');

const obj1 = [
  1,
  'Hello!',
  { name: 'jsliang1' },
  [
    {
      name: 'LiangJunrong1',
    }
  ],
]
const obj2 = _.cloneDeep(obj1);
obj2[0] = 2;
obj2[1] = 'Hi!';
obj2[2].name = 'jsliang2';
obj2[3][0].name = 'LiangJunrong2';

console.log(obj1);
// [
//   1,
//   'Hello!',
//   { name: 'jsliang1' },
//   [
//     { name: 'LiangJunrong1' },
//   ],
// ]

console.log(obj2);
// [
//   2,
//   'Hi!',
//   { name: 'jsliang2' }, 
//   [
//     { name: 'LiangJunrong2' },
//   ],
// ]

4.4 5、 ... and frame jQuery

Return directory

Of course , Your company is still using it jQuery, It may also need to be compatible IE6/7/8, Or you use React, But some scenes use jQuery, After all jQuery It's a powerful framework .

You can try to use jQuery Of extend() Make a deep copy , There's no way to post it here .

5、 ... and reference

Return directory

jsliang The document library of is made up of Liang Junrong use Knowledge sharing A signature - Noncommercial use - Share in the same way 4.0 The international license agreement Licensing .<br/> be based on https://github.com/LiangJunrong/document-library Creation of works on .<br/> Use rights other than those authorized by this license agreement can be obtained from https://creativecommons.org/licenses/by-nc-sa/2.5/cn/ To obtain .

版权声明
本文为[jsliang]所创,转载请带上原文链接,感谢