This time, thoroughly grasp the depth of JavaScript copy

Hanpeng_ Chen 2021-02-23 16:09:11
time thoroughly grasp depth javascript

About copying , It's also a classic interview question in the front-end interview , In our daily development, we often encounter scenes that need to use deep copy or shallow copy . Next, let's go through this article , To master thoroughly JavaScript Deep and light copies of .

data type

Before we start talking about deep and shallow copy , We need to know first JavaScript Data type of , Mainly as shown in the figure below 8 Kind of :

Object Is a reference type , other 7 It is the basic type .

JavaScript All the data types of will be put in different memory after initialization , Therefore, the above data types can be roughly divided into two categories for storage :

  • The underlying type is stored in stack memory , When quoted or copied , Will create an exactly equal variable
  • Reference types are stored in heap memory , What's stored is the address , Multiple references point to the same address , There will be a “ share ” The concept of .

Let's take a look at the shallow copy first .

Welcome to the official account : Front end geek Technology (FrontGeek), We learn together and make progress together .

The principle and implementation of shallow copy

Definition of light copy :

Create an object to accept the value of the object to be copied or referenced again . If the object property is the basic data type , What is copied is the value of the basic type to the new object ; If the property is a reference data type , The copied address is the address in memory , If one of the objects changes the address in memory , It could affect another object .

Let's see JavaScript What are the ways to achieve shallow copy in the .


object.assign yes ES6 in object One way , This method can be used for JS Object merging and so on , One of the purposes is to make shallow copies . The first parameter of this method is the target object of the copy , The next parameter is the source object of the copy ( It can also be multiple sources ).

object.assign The grammar of is :Object.assign(target, ...sources)

object.assign The sample code of is as follows :

let target = {}
let source = {
a: {
b: 1
Object.assign(target, source)
source.a.b = 10;
console.log(source); // { a: { b: 10 } }; 
console.log(target); // { a: { b: 10 } };
 Copy code 

From the above code we can see , First, through Object.assign take source copy to target In the object , take source Object b Attribute from 1 It is amended as follows 10. As can be seen from the execution results target and source Of the two objects b All attributes change to 10 了 , prove Object.assign For the time being, the copy effect we wanted .

Use Object.assign Method to achieve shallow copy, there are several points to note :

  • Object.assign Method does not copy the inherited properties of the object
  • Object.assign Method does not copy the object's non enumerable properties
  • You can copy Symbol Properties of type
let obj1 = { a: { b: 1 }, sym: Symbol(1) };
Object.defineProperty(obj1, "innumerable", {
value: " Cannot enumerate properties ",
enumerable: false,
let obj2 = {};
Object.assign(obj2, obj1);
obj1.a.b = 10;
console.log("obj1", obj1); // obj1 { a: { b: 10 }, sym: Symbol(1) }
console.log("obj2", obj2); // obj1 { a: { b: 10 }, sym: Symbol(1) }
 Copy code 

As you can see from the sample code above , utilize object.assign It can also be copied Symbol Object of type , But if you get to the second layer of the object's properties obj1.a.b When it's here , The change of the value of the former will also affect the value of the second layer attribute of the latter , It shows that there is still the problem of accessing common heap memory , In other words, this method can not be further copied , And just finished the shallow copy function .

Extension operator methods

We can also use it JS The extension operator of , In the construction of objects at the same time to complete the shallow copy function .

let obj = {a: 1, b: {c: 10}}
let obj2 = {...obj}
obj2.a = 10
let arr = [1, 3, 4, 5, [10, 20]]
let arr2 = [...arr]
 Copy code 

Extension operator and object.assign With the same flaws , That is to say, the function of shallow copy is almost the same , But if the properties are all values of the base type , It's more convenient to use extension operators for shallow copy .

concat Copy an array

Array of concat The method is also shallow copy , So when you join an array with a reference type , You need to pay attention to modifying the attributes of the elements in the original array , Because it will affect the array connected after copying .

let arr = [1, 2, 3];
let newArr = arr.concat();
newArr[1]= 10;
 Copy code 

slice Copy an array

slice The method also has limitations , Because it's only for array types .

slice Method returns a new array object , This object determines the start and end time of the original array interception by the first two parameters of the method , Will not affect and change the original array .

let arr = [1, 2, {val: 4}];
let newArr = arr.slice();
newArr[2].val = 1000;
console.log(arr); //[ 1, 2, { val: 1000 } ]
 Copy code 

As you can see from the code above , This is the limit of shallow copy —— It can only copy one layer of objects . If there is nesting of objects , So there's nothing a shallow copy can do . So deep copy was created to solve this problem , It can solve the problem of multi-layer object nesting , Complete copy . Later in this lecture, I will introduce the content related to deep copy .

Make a shallow copy by hand

In addition to the methods mentioned above , We can also write a shallow copy method , Ideas as follows :

  • Make a basic copy of the basic data type ;
  • Open up a new store for reference types , And copy a layer of object properties .
const shallowCopy = (target) => {
if (typeof target === "object" && target !== null) {
const copyTarget = Array.isArray(target) ? [] : {};
for (let key in target) {
if (target.hasOwnProperty(key)) {
copyTarget[key] = target[key];
return copyTarget;
} else {
return target;
 Copy code 

The principle and implementation of deep copy

Shallow copy just creates a new object , Copy the value of the basic type of the original object , The reference data type only copies one layer of properties , No matter how deep it is, it still can't be copied . Deep copies are different , For complex reference data types , It completely opens up a memory address in heap memory , And copy the original object completely .

These two objects are independent of each other 、 Unaffected , Complete separation of memory . in general , The principle of deep copy can be summarized as follows :

A complete copy of an object from memory and a copy to the target object , And from the heap memory to open up a new space to store new objects , And the modification of the new object does not completely change the source object , There is a real separation between the two .

After knowing the principle of deep copy , Let's see what deep copy methods are available :

The beggar version :JSON.stringfy

JSON.stringfy() Method is the most common and simplest deep copy method in development , It's basically serializing an object to JSON String , And convert the content of the object to a string , Last but not least JSON.parse() Methods will JSON String to generate a new object .

let obj = {a: 1, b: [1, 2, 3]};
let str = JSON.stringify(obj);
let newObj = JSON.parse(str);
obj.a = 2;
 Copy code 

As you can see from the code above , adopt JSON.stringfy Can initially achieve a deep copy of the object , By changing obj Of a attribute , In fact, we can see that newObj This object is not affected either .

Let's take a look at the following uses JSON.stringify Method for deep copy :

function Obj() {
this.func = function () { alert(1) };
this.obj = {a:1};
this.arr = [1,2,3];
this.und = undefined;
this.reg = /123/; = new Date(0);
this.NaN = NaN;
this.infinity = Infinity;
this.sym = Symbol(1);
let obj1 = new Obj();
let str = JSON.stringify(obj1);
let obj2 = JSON.parse(str);
 Copy code 

From the execution results above ,JSON.stringify The way to achieve deep copy is not perfect , Let's take a look JSON.stringify The problems in implementing deep copy :

  • If the copied object has a function 、undefined、symbol There are several types , after JSON.stringify The key value pair disappears in the serialized string ;
  • Copy Date The reference type becomes a string
  • Can't copy properties that can't be enumerated
  • Can't copy prototype chain of object
  • Copy RegExp Reference type becomes empty
  • Object contains NaN、Infinity、-Infinity,JSON The result of serialization would be null
  • Can't copy circular application of object , That is to say, the object forms a ring , for example obj[key]=obj

use JSON.stringify Method to implement deep copy object , Although so far there are many functions that can't be realized , But this method is enough to meet the daily development needs , And it's the easiest and quickest .

If the deep copy method needs to support Function、Symbol And so on , We can only implement a deep copy method by ourselves .

Handwriting for deep copy ( Basic Edition )

Next, we implement a deep copy method ourselves , Ideas as follows :

adopt for in Traverse the property value of the incoming parameter , If the value refers to the type, the method is called recursively again , If so, the underlying type is copied directly .

function deepCopy(target) {
let copyTarget = Array.isArray(target) ? [] : {};
for (let key in target) {
if (typeof target[key] === "object") {
copyTarget[key] = deepCopy(target[key]);
} else {
copyTarget[key] = target[key];
return copyTarget;
let obj1 = {
a: {
b: 1,
c: 10,
let obj2 = deepCopy(obj1);
obj1.a.b = 100;
obj1.c = 22
 Copy code 

Above, we implemented deep copy recursively , But there are still some problems to be solved :

  • Cannot copy non enumerable properties and Symbol type ;
  • Only the values of common reference types are recursively copied , But for the Array、Date、RegExp、Error、Function These reference types don't copy properly .
  • The properties of the object are looped , That is, circular reference is not solved .

Next, let's improve the deep copy method :

Improved deep copy method

In view of the above problems , Let's see what the solution is :

  • For enumerable properties and Symbol type , have access to Reflect.ownKeys Method ;
  • Parameter is Date、RegExp Type , Directly generate a new instance and return ;
  • utilize Object Of getOwnPropertyDescriptors Method to get all the properties of an object , And the corresponding characteristics , By the way Object.create() Method creates a new object , And inherits the chain of prototypes passed in to the original object ;
  • utilize WeakMap Type as Hash surface , because WeakMap Weak reference type , Can effectively prevent memory leakage , It helps to detect circular references , If there is a cycle , Then the reference returns WeakMap Stored values .

According to the above , Let's rewrite the deep copy method :

function deepCopy(obj, hash = new WeakMap()) {
// The date object directly returns a new date object 
if (obj.constructor === Date) return new Date(obj);
// If it is a regular object, return a new regular object directly 
if (obj.constructor === RegExp) return new RegExp(obj);
// If a loop reference is made, use WeakMap To solve 
if (hash.has(obj)) return hash.get(obj);
// Traverses the properties of all keys in the incoming parameter 
let allDesc = Object.getOwnPropertyDescriptor(obj);
// Inherit the prototype chain 
let copyObj = Object.create(Object.getPrototypeOf(obj), allDesc);
hash.set(obj, copyObj)
for (let key of Reflect.ownKeys(obj)) {
copyObj[key] = (isComplexDataType(obj[key]) && typeof obj[key] !== 'function') ? deepCopy(obj[key], hash) : obj[key]
return copyObj
function isComplexDataType(obj) {
return (typeof obj === 'object' || typeof obj === 'function') && obj !== null
// Here's the validation code 
let obj = {
num: 0,
str: '',
boolean: true,
unf: undefined,
nul: null,
obj: { name: ' I'm an object ', id: 1 },
arr: [0, 1, 2],
func: function () { console.log(' I'm a function ') },
date: new Date(0),
reg: new RegExp('/ I'm a regular /ig'),
[Symbol('1')]: 1,
Object.defineProperty(obj, 'innumerable', {
enumerable: false, value: ' Cannot enumerate properties ' }
obj = Object.create(obj, Object.getOwnPropertyDescriptors(obj))
obj.loop = obj // Set up loop Properties referenced in a loop 
let cloneObj = deepCopy(obj)
console.log('obj', obj)
console.log('cloneObj', cloneObj)
 Copy code 

Performance issues

Although using deep copy can completely clone a new object , No side effects , But because the deep copy method uses recursion , Performance is not as good as shallow copy , In the actual development process , We should choose according to the actual situation .

If you think this is going to help you :

1、 give the thumbs-up Support me , So that more people can see the content

2、 Official account : Front end geek Technology (FrontGeek), We learn together and make progress together .

本文为[Hanpeng_ Chen]所创,转载请带上原文链接,感谢

  1. RxHttp - 轻量级、可扩展、易使用、完美兼容MVVM、MVC架构的网络封装类库
  2. Rxhttp - lightweight, extensible, easy to use, perfectly compatible with MVVM, MVC architecture network encapsulation class library
  3. Rxhttp - lightweight, extensible, easy to use, perfectly compatible with MVVM, MVC architecture network encapsulation class library
  4. 前端面试常考题:JS垃圾回收机制
  5. Frequently asked questions in front end interview: JS garbage collection mechanism
  6. Rxhttp - lightweight, extensible, easy to use, perfectly compatible with MVVM, MVC architecture network encapsulation class library
  7. Java之HTTP网络编程(一):TCP/SSL网页下载
  8. HTTP network programming in Java (1): TCP / SSL web page download
  9. Java之HTTP网络编程(一):TCP/SSL网页下载
  10. HTTP network programming in Java (1): TCP / SSL web page download
  11. 使用vite搭建vue项目
  12. 在组件中展示pdf文件:vue-pdf (由于未找到方法滑动加载下一页,只能点击加载下一页pdf文件)
  13. 在 vue 中通过 express 连接数据库
  14. Using vite to build Vue project
  15. Display PDF file in component: Vue pdf
  16. Connecting database through express in Vue
  17. 2021届秋招哈啰出行前端面经(一面)
  18. vue使用sdk进行七牛云上传
  19. Javascript性能优化【内联缓存】 V8引擎特性
  20. Small true wireless smart headset evaluation: put intelligence into the ear
  21. The front end experience of the 2021 autumn recruitment
  22. Vue uses SDK to upload Qi Niu cloud
  23. 深入理解 Web 协议 (三):HTTP 2
  24. dhtmlxGantt如何重新排序任务
  25. JavaScript performance optimization [inline cache] V8 engine features
  26. 深入理解 Web 协议 (三):HTTP 2
  27. Deep understanding of Web protocol (3): http 2
  28. 深入理解 Web 协议 (三):HTTP 2
  29. How dhtmlxgantt reorders tasks
  30. 深入理解 Web 协议 (三):HTTP 2
  31. JavaScriptBOM操作
  32. JavaScriptBOM操作
  33. Deep understanding of Web protocol (3): http 2
  34. Deep understanding of Web protocol (3): http 2
  35. dhtmlxGantt甘特图重新排序任视频教程
  36. vue实现七牛云上传图片功能
  37. vue.js环境配置步骤及npm run dev报错解决方案
  38. Deep understanding of Web protocol (3): http 2
  39. JavaScript BOM operation
  40. JavaScript BOM operation
  41. Dhtmlxgantt reordering video tutorial
  42. Vue to achieve seven cattle cloud upload image function
  43. vue.js Environment configuration steps and NPM run dev error reporting solution
  44. 什么是HTTPS以及如何实施HTTPS?
  45. vue使用sdk进行七牛上传
  46. JavaScript 邮箱验证 - 正则验证
  47. Codeless development platform cloud watch sets off a "new revolution" in enterprise Digitalization
  48. Codeless development of cloud watch for enterprise digital solution
  49. What is HTTPS and how to implement it?
  50. Vue uses SDK to upload seven cows
  51. JavaScript mailbox verification - regular verification
  52. JavaScriptBOM操作
  53. JavaScript BOM operation
  54. How to create a new NPM package and publish it to the NPM community
  55. vue --tinymce 解决上传图片的方法
  56. Vue development
  57. A simple solution for vite to batch introduce SVG symbol
  58. Building 3D rendering engine from scratch with JS (2)
  59. Vue -- tinymece solution to upload pictures
  60. Common configuration of nginx