One Catalog
No frills front end , What's the difference with salted fish
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 ,Array
、Object
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 :
- A light handwritten copy
- Use
Object.assign
- Using arrays API, Such as
concat
perhapsslice
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
: TraverseObject
objectarr1
, Enumerate enumerable values .hasOwnProperty()
: Check if the enumeration value belongs to the objectarr1
, 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 :
- Hand written deep copy
- With the help of
JSON.parse(JSON.stringify())
- 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
perhapsSet
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 intoJSON
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
- [x] How to write a deep copy of an amazing interviewer ?【 Reading suggestions :2h】
- [x] The ultimate exploration of deep copy (90% People don't know )【 Reading suggestions :1h】
- [x] JavaScript Basic mental method —— Depth copy 【 Reading suggestions :30min】
- [x] JavaScript A deep and shallow copy of the project 【 Reading suggestions :20min】
- [x] javaScript The implementation of shallow copy and deep copy 【 Reading suggestions :20min】
- [x] In depth analysis of JavaScript Deep replication 【 Reading suggestions :20min】
- [x] 「JavaScript」 I'll take you through the deep copy 、 Shallow copy and circular reference 【 Reading suggestions :20min】
- [x] How to realize a deep copy of the interview question 【 Reading suggestions :30min】
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 .