JavaScript 주제 5: 깊은 복사와 얕은 복사
복사 과정을 이해하고 불필요한 실수를 피하세요. Js의 깊고 얕은 복사 시리즈를 함께 열심히 만들어 보세요~
목차
- 1 복사 예
- 2.
- 3 , 딥카피 방식?
4. 직접 Deep Copy와 Shallow Copy를 구현하세요
추천 무료 학습: javascript 비디오 튜토리얼
1.데이터를 조작할 때 이런 문제가 발생할 수 있습니다. 상황:
데이터 세트가 자주 변경되지만 원본 데이터가 사용될 수 있습니다.
- 동일한 데이터 세트 두 개가 필요하지만 하나를 변경하고 싶지 않고 다른 하나가 변경됩니다
- 필요합니다 데이터 연산 전후 비교
- …
- 비슷한 요구 상황에 직면했을 때 가장 먼저 생각하는 것은 복사하는 것인데, 복사도 지식이 많은지 모르겠습니다~
var str = 'How are you';var newStr = str;newStr = 10console.log(str); // How are youconsole.log(newStr); // 10
str
과 newStr
은 레이아웃은 동일하지만 연결이 없는 두 개의 동일한 방과 같습니다. 1.2 참조 유형 복사 예
var data = [1, 2, 3, 4, 5];var newData = data;newData[0] = 100;console.log(data[0]); // 100console.log(newData[0]); // 100
str
和newStr
就好比两个一模一样的房间,布局一致却毫无关联。1.2 引用类型拷贝示例
var arr = [1, 2, 3, 4];var newArr = arr;console.log(arr, newArr); // [1,2,3,4] [1,2,3,4]newArr[0] = 100;console.log(arr, newArr) // [100,2,3,4] [100,2,3,4]
类似的代码段,但这次我们使用数组这个引用类型举例,你会发现修改赋值后的数据,原始数据也跟着改变了,这显然不满足我们的需要。本篇文章就来聊一聊引用数据拷贝的学问。
如果大家对Js的数据类型存在着疑问,不妨看看《JavaScript中的基本数据类型》
二、浅拷贝
拷贝的划分都是针对引用类型来讨论的,浅拷贝——顾名思义,浅拷贝就是“浅层拷贝”,实际上只做了表面功夫:
var arr = [1,2,3,4];var res = arr.slice();// 或者res = arr.concat()res[0] = 100;console.log(arr); // [1,2,3,4]
不发生事情(操作)还好,一旦对新数组进行了操作,两个变量中所保存的数据都会发生改变。
发生这类情况的原因也是因为引用类型
的基本特性:
- 存储在变量处的值是一个指针(point),指向存储对象的内存地址。赋值给新变量相当于配了一把新钥匙,房间并没有换。
数组中的slice和concat都会返回一个新数组,我们一起来试一下:
var arr = [ { age: 23 }, [1,2,3,4] ];var newArr = arr.concat();arr[0].age = 18;arr[1][0] = 100;console.log(arr) // [ {age: 18}, [100,2,3,4] ]console.log(newArr) // [ {age: 18}, [100,2,3,4] ]
这个问题这么快就解决了?虽然对这一层数据进行了这样的的处理后,确实解决了问题,但!
var arr = ['str', 1, true, [1, 2], {age: 23}]var newArr = JSON.parse( JSON.stringify(arr) );newArr[3][0] = 100;console.log(arr); // ['str', 1, true, [1, 2], {age: 23}]console.log(newArr); // ['str', 1, true, [100, 2], {age: 23}]
果然事情没有那么简单,这也是因为数据类型的不同。
S 不允许我们直接操作内存中的地址,也就是说不能操作对象的内存空间,所以,我们对对象的操作都只是在操作它的引用而已。
既然浅拷贝
达不到我们的要求,本着效率的原则,我们找找有没有帮助我们实现深拷贝
的方法。
三、深拷贝的方法?
数据的方法失败了,还有没有其他办法?我们需要实现真正意义上的拷贝出独立的数据。
3.1 JSON
这里我们利用JSON的两个方法,JSON.stringify()
,JSON.parse()
来实现最简洁的深拷贝
var arr = [1,2,3];var newArr = arr.toString().split(',').map(item => Number(item))newArr[0] = 100;console.log(arr); // [1,2,3]console.log(newArr); // [100,2,3]
这个方法应该是实现深拷贝最简洁的方法,但是,它仍然存在问题,我们先来看看刚才都做了些什么:
- 定义一个包含都过类型的数组
arr
- JSON.stringify(arr), 将一个 JavaScript 对象或值转换为
JSON 字符串
- JSON.parse(xxx), 方法用来解析JSON字符串,构造由字符串描述的
值或对象
理解:
我们可以理解为,将原始数据转换为新字符串
,再通过新字符串
还原为一个新对象
,这中改变数据类型的方式,间接的绕过了拷贝对象引用的过程,也就谈不上影响原始数据。
限制:
这种方式成立的根本就是保证数据在“中转”时的完整性,而JSON.stringify()
将值转换为相应的JSON格式
비슷한 코드 조각이지만 이번에는 참조 유형 배열을 예로 사용합니다. 할당된 데이터가 수정되면 원본 데이터도 변경되므로 분명히 일치하지 않습니다. 우리의 필요. 이번 글에서는

var obj = {a: 1, b: { c: 2 } }var newObj = Object.assign({}, obj)newObj.a = 100;newObj.b.c = 200;console.log(obj); // {a: 1, b: { c: 200 } }console.log(newObj) // {a: 100, b: { c: 200 } }
참조 유형
의 기본 특성 때문입니다. 🎜🎜🎜변수에 저장된 값은 메모리 주소를 가리키는 포인터(포인트)입니다. 저장된 개체. 새 변수에 값을 할당하는 것은 새 키를 할당하는 것과 동일하며 방은 변경되지 않았습니다. 🎜🎜🎜 배열의 슬라이스와 연결 모두 새 배열을 반환합니다. 함께 시도해 보겠습니다. 🎜var shallowCopy = function(obj) { if (typeof obj !== 'object') return; // 根据obj的类型判断是新建一个数组还是对象 var newObj = obj instanceof Array ? [] : {}; // 遍历obj,并且判断是obj的属性才拷贝 for (var key in obj) { if (obj.hasOwnProperty(key)) { newObj[key] = obj[key]; } } return newObj;}
var deepCopy = function(obj) { if (typeof obj !== 'object') return; var newObj = obj instanceof Array ? [] : {}; for (var key in obj) { if (obj.hasOwnProperty(key)) { newObj[key] = typeof obj[key] === 'object' ? deepCopy(obj[key]) : obj[key]; } } return newObj;}
shallow copy
는 우리의 요구 사항을 충족할 수 없기 때문에 🎜efficiency🎜 원칙에 따라 deep copy
를 구현하는 데 도움이 되는 방법을 찾고 있습니다. 🎜🎜
JSON.stringify()
, JSON.parse()
를 사용하여 가장 간결한 전체 복사를 달성합니다🎜const obj = [1, { a: 1, b: { name: '余光'} } ];const resObj = deepCopy(obj);
arr 배열을 정의합니다.🎜🎜 JSON.stringify(arr)는 JavaScript 개체 또는 값을 <code>JSON 문자열
🎜🎜JSON.parse(xxx)로 변환합니다. 메서드는 JSON 문자열을 구문 분석하는 데 사용되며 문자열에서 구성됩니다. 설명된 값 또는 object
🎜🎜🎜🎜이해: 🎜🎜🎜원본 데이터를 새 문자열
로 변환한 다음 새 문자열
을 전달하면 복원된다고 이해하면 됩니다. 데이터 유형을 변경하는 방법은 개체 참조를 복사하는 과정을 간접적으로 우회하며 원본 데이터에는 영향을 주지 않습니다. 🎜🎜🎜제한 사항: 🎜🎜🎜이 방법의 기본 목적은 "전송" 중에 데이터의 무결성을 보장하는 것이며 JSON.stringify()
는 값을 해당 JSON 형식으로 변환합니다.
에도 결함이 있습니다: 🎜- undefined、任意的函数以及 symbol 值,在序列化过程中会被忽略(出现在非数组对象的属性值中时)或者被转换成 null(出现在数组中时)。
- 函数、undefined 被单独转换时,会返回 undefined,
- 如JSON.stringify(function(){})
- JSON.stringify(undefined)
- 对包含循环引用的对象(对象之间相互引用,形成无限循环)执行此方法,会抛出错误。
- NaN 和 Infinity 格式的数值及 null 都会被当做 null。
- 其他类型的对象,包括 Map/Set/WeakMap/WeakSet,仅会序列化可枚举的属性。
所以当我们拷贝函数、undefined等stringify
转换有问题的数据时,就会出错,我们在实际开发中也要结合实际情况使用。
举一反三:
既然是通过改变数据类型来绕过拷贝引用这一过程,那么单纯的数组深拷贝是不是可以通过现有的几个API来实现呢?
var arr = [1,2,3];var newArr = arr.toString().split(',').map(item => Number(item))newArr[0] = 100;console.log(arr); // [1,2,3]console.log(newArr); // [100,2,3]
注意,此时仅能对包含纯数字的数组进行深拷贝,因为:
- toString无法正确的处理对象和函数
- Number无法处理 false、undefined等数据类型
但我愿称它为纯数字数组深拷贝!
3.2 Object.assign()
有的人会认为Object.assign()
,可以做到深拷贝,我们来看一下
var obj = {a: 1, b: { c: 2 } }var newObj = Object.assign({}, obj)newObj.a = 100;newObj.b.c = 200;console.log(obj); // {a: 1, b: { c: 200 } }console.log(newObj) // {a: 100, b: { c: 200 } }
神奇,第一层属性没有改变,但第二层却同步改变了,这是为什么呢?
因为 Object.assign()拷贝的是(可枚举)属性值。
假如源值是一个对象的引用,它仅仅会复制其引用值。MDN传送门
四、自己实现深浅拷贝
既然现有的方法无法实现深拷贝,不妨我们自己来实现一个吧~
4.1 浅拷贝
我们只需要将所有属性即其嵌套属性原封不动的复制给新变量一份即可,抛开现有的方法,我们应该怎么做呢?
var shallowCopy = function(obj) { if (typeof obj !== 'object') return; // 根据obj的类型判断是新建一个数组还是对象 var newObj = obj instanceof Array ? [] : {}; // 遍历obj,并且判断是obj的属性才拷贝 for (var key in obj) { if (obj.hasOwnProperty(key)) { newObj[key] = obj[key]; } } return newObj;}
我们只需要将所有属性的引用拷贝一份即可~
4.2 深拷贝
相信大家在实现深拷贝的时候都会想到递归,同样是判断属性值,但如果当前类型为object
则证明需要继续递归,直到最后
var deepCopy = function(obj) { if (typeof obj !== 'object') return; var newObj = obj instanceof Array ? [] : {}; for (var key in obj) { if (obj.hasOwnProperty(key)) { newObj[key] = typeof obj[key] === 'object' ? deepCopy(obj[key]) : obj[key]; } } return newObj;}
我们用白话来解释一下deepCopy
都做了什么
const obj = [1, { a: 1, b: { name: '余光'} } ];const resObj = deepCopy(obj);
- 读取
obj
,创建 第一个newObj- 判断类型为
[]
- key为
0
(for in
以任意顺序遍历,我们假定按正常循序遍历) - 判断不是引用类型,直接复制
- key为
1
- 判断是引用类型
- 进入递归,重新走了一遍刚才的流程,只不过读取的是
obj[1]
- 判断类型为
另外请注意递归的方式虽然可以深拷贝,但是在性能上肯定不如浅拷贝,大家还是需要结合实际情况来选择。
写在最后
前端专项进阶系列的第五篇文章
,希望它能对大家有所帮助,如果大家有什么建议,可以在评论区留言,能帮到自己和大家就是我最大的动力!
相关免费学习推荐:javascript(视频)
위 내용은 JavaScript 주제 5: 깊은 복사와 얕은 복사의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

핫 AI 도구

Undresser.AI Undress
사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover
사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool
무료로 이미지를 벗다

Clothoff.io
AI 옷 제거제

AI Hentai Generator
AI Hentai를 무료로 생성하십시오.

인기 기사

뜨거운 도구

메모장++7.3.1
사용하기 쉬운 무료 코드 편집기

SublimeText3 중국어 버전
중국어 버전, 사용하기 매우 쉽습니다.

스튜디오 13.0.1 보내기
강력한 PHP 통합 개발 환경

드림위버 CS6
시각적 웹 개발 도구

SublimeText3 Mac 버전
신 수준의 코드 편집 소프트웨어(SublimeText3)

뜨거운 주제











WebSocket 및 JavaScript를 사용하여 온라인 음성 인식 시스템을 구현하는 방법 소개: 지속적인 기술 개발로 음성 인식 기술은 인공 지능 분야의 중요한 부분이 되었습니다. WebSocket과 JavaScript를 기반으로 한 온라인 음성 인식 시스템은 낮은 대기 시간, 실시간, 크로스 플랫폼이라는 특징을 갖고 있으며 널리 사용되는 솔루션이 되었습니다. 이 기사에서는 WebSocket과 JavaScript를 사용하여 온라인 음성 인식 시스템을 구현하는 방법을 소개합니다.

WebSocket과 JavaScript: 실시간 모니터링 시스템 구현을 위한 핵심 기술 서론: 인터넷 기술의 급속한 발전과 함께 실시간 모니터링 시스템이 다양한 분야에서 널리 활용되고 있다. 실시간 모니터링을 구현하는 핵심 기술 중 하나는 WebSocket과 JavaScript의 조합입니다. 이 기사에서는 실시간 모니터링 시스템에서 WebSocket 및 JavaScript의 적용을 소개하고 코드 예제를 제공하며 구현 원칙을 자세히 설명합니다. 1. 웹소켓 기술

JavaScript 및 WebSocket을 사용하여 실시간 온라인 주문 시스템을 구현하는 방법 소개: 인터넷의 대중화와 기술의 발전으로 점점 더 많은 레스토랑에서 온라인 주문 서비스를 제공하기 시작했습니다. 실시간 온라인 주문 시스템을 구현하기 위해 JavaScript 및 WebSocket 기술을 사용할 수 있습니다. WebSocket은 TCP 프로토콜을 기반으로 하는 전이중 통신 프로토콜로 클라이언트와 서버 간의 실시간 양방향 통신을 실현할 수 있습니다. 실시간 온라인 주문 시스템에서는 사용자가 요리를 선택하고 주문을 하면

WebSocket과 JavaScript를 사용하여 온라인 예약 시스템을 구현하는 방법 오늘날의 디지털 시대에는 점점 더 많은 기업과 서비스에서 온라인 예약 기능을 제공해야 합니다. 효율적인 실시간 온라인 예약 시스템을 구현하는 것이 중요합니다. 이 기사에서는 WebSocket과 JavaScript를 사용하여 온라인 예약 시스템을 구현하는 방법을 소개하고 구체적인 코드 예제를 제공합니다. 1. WebSocket이란 무엇입니까? WebSocket은 단일 TCP 연결의 전이중 방식입니다.

JavaScript 및 WebSocket: 효율적인 실시간 일기 예보 시스템 구축 소개: 오늘날 일기 예보의 정확성은 일상 생활과 의사 결정에 매우 중요합니다. 기술이 발전함에 따라 우리는 날씨 데이터를 실시간으로 획득함으로써 보다 정확하고 신뢰할 수 있는 일기예보를 제공할 수 있습니다. 이 기사에서는 JavaScript 및 WebSocket 기술을 사용하여 효율적인 실시간 일기 예보 시스템을 구축하는 방법을 알아봅니다. 이 문서에서는 특정 코드 예제를 통해 구현 프로세스를 보여줍니다. 우리

JavaScript 튜토리얼: HTTP 상태 코드를 얻는 방법, 특정 코드 예제가 필요합니다. 서문: 웹 개발에서는 서버와의 데이터 상호 작용이 종종 포함됩니다. 서버와 통신할 때 반환된 HTTP 상태 코드를 가져와서 작업의 성공 여부를 확인하고 다양한 상태 코드에 따라 해당 처리를 수행해야 하는 경우가 많습니다. 이 기사에서는 JavaScript를 사용하여 HTTP 상태 코드를 얻는 방법과 몇 가지 실용적인 코드 예제를 제공합니다. XMLHttpRequest 사용

사용법: JavaScript에서 insertBefore() 메서드는 DOM 트리에 새 노드를 삽입하는 데 사용됩니다. 이 방법에는 삽입할 새 노드와 참조 노드(즉, 새 노드가 삽입될 노드)라는 두 가지 매개 변수가 필요합니다.

JavaScript는 웹 개발에 널리 사용되는 프로그래밍 언어인 반면 WebSocket은 실시간 통신에 사용되는 네트워크 프로토콜입니다. 두 가지의 강력한 기능을 결합하면 효율적인 실시간 영상 처리 시스템을 만들 수 있습니다. 이 기사에서는 JavaScript와 WebSocket을 사용하여 이 시스템을 구현하는 방법을 소개하고 구체적인 코드 예제를 제공합니다. 첫째, 실시간 영상처리 시스템의 요구사항과 목표를 명확히 할 필요가 있다. 실시간 이미지 데이터를 수집할 수 있는 카메라 장치가 있다고 가정해 보겠습니다.
