3, Deep-Copy-Methode?Verstehen Sie den Prozess hinter dem Kopieren und vermeiden Sie unnötige Fehler gemeinsam an Js spezieller Serie von tiefen und flachen Kopien
4. Implementieren Sie selbst eine tiefe und flache Kopie Situation:
Machen Sie es Kommt Ihnen das einfache Beispiel unten bekannt vor?das Wissen über das Zitieren von Datenkopien1.1 Beispiel für das Kopieren eines Basistyps
Wie sich jeder vorstellen kann, ist String ein Basistyp und sein Wert wird im Stapel gespeichert. Beim Kopieren wird tatsächlich neuer Platz für neue Variablen geöffnet.var str = 'How are you';var newStr = str;newStr = 10console.log(str); // How are youconsole.log(newStr); // 10Nach dem Login kopierenstr
undnewStr
sind wie zwei identische Räume mit demselben Grundriss, aber ohne Verbindung. 1.2 Referenztyp-KopierbeispielÄhnlicher Codeausschnitt, aber dieses Mal verwenden wir den Referenztyp eines Arrays als Beispiel. Sie werden feststellen, dass sich bei einer Änderung der zugewiesenen Daten auch die Originaldaten ändern, was offensichtlich nicht der Fall ist unsere Bedürfnisse. In diesem Artikel geht es umvar data = [1, 2, 3, 4, 5];var newData = data;newData[0] = 100;console.log(data[0]); // 100console.log(newData[0]); // 100Nach dem Login kopieren
.
Wenn Sie Fragen zu den Datentypen von Js haben, können Sie sich auch „Grundlegende Datentypen in JavaScript“ ansehenvar 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]
Der Grund, warum eine solche Situation auftritt, liegt auch in den grundlegenden Eigenschaften des Referenztyps
:
Sowohl Slice als auch Concat im Array geben ein neues Array zurück. Probieren wir es gemeinsam aus: str
和newStr
就好比两个一模一样的房间,布局一致却毫无关联。
var arr = [1,2,3,4];var res = arr.slice();// 或者res = arr.concat()res[0] = 100;console.log(arr); // [1,2,3,4]
类似的代码段,但这次我们使用数组这个引用类型举例,你会发现修改赋值后的数据,原始数据也跟着改变了,这显然不满足我们的需要。本篇文章就来聊一聊引用数据拷贝的学问。
如果大家对Js的数据类型存在着疑问,不妨看看《JavaScript中的基本数据类型》
二、浅拷贝
拷贝的划分都是针对引用类型来讨论的,浅拷贝——顾名思义,浅拷贝就是“浅层拷贝”,实际上只做了表面功夫:
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] ]
不发生事情(操作)还好,一旦对新数组进行了操作,两个变量中所保存的数据都会发生改变。
发生这类情况的原因也是因为引用类型
的基本特性:
数组中的slice和concat都会返回一个新数组,我们一起来试一下:
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}]
这个问题这么快就解决了?虽然对这一层数据进行了这样的的处理后,确实解决了问题,但!
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]
果然事情没有那么简单,这也是因为数据类型的不同。
S 不允许我们直接操作内存中的地址,也就是说不能操作对象的内存空间,所以,我们对对象的操作都只是在操作它的引用而已。
既然浅拷贝
达不到我们的要求,本着效率的原则,我们找找有没有帮助我们实现深拷贝
的方法。
三、深拷贝的方法?
数据的方法失败了,还有没有其他办法?我们需要实现真正意义上的拷贝出独立的数据。
这里我们利用JSON的两个方法,JSON.stringify()
,JSON.parse()
来实现最简洁的深拷贝
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 } }
这个方法应该是实现深拷贝最简洁的方法,但是,它仍然存在问题,我们先来看看刚才都做了些什么:
arr
JSON 字符串
值或对象
理解:
我们可以理解为,将原始数据转换为新字符串
,再通过新字符串
还原为一个新对象
,这中改变数据类型的方式,间接的绕过了拷贝对象引用的过程,也就谈不上影响原始数据。
限制:
这种方式成立的根本就是保证数据在“中转”时的完整性,而JSON.stringify()
将值转换为相应的JSON格式
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
unsere Anforderungen nicht erfüllen kann, suchen wir nach dem Prinzip der 🎜Effizienz🎜 nach Möglichkeiten, die uns bei der Implementierung von deep copy
unterstützen. 🎜🎜🎜🎜🎜Drei, tiefe Wie kopieren? 🎜🎜🎜Die Datenmethode ist fehlgeschlagen. Gibt es eine andere Möglichkeit? Wir müssen eine echte Kopie unabhängiger Daten erreichen. 🎜🎜3.1 JSON🎜🎜Hier verwenden wir zwei Methoden von JSON, JSON.stringify()
, JSON.parse()
, um die prägnanteste tiefe Kopie zu erreichen🎜const obj = [1, { a: 1, b: { name: '余光'} } ];const resObj = deepCopy(obj);
arr, das alle Codetypen enthält.🎜🎜 JSON.stringify(arr), konvertiert ein JavaScript-Objekt oder einen JavaScript-Wert in einen <code>JSON-String
🎜🎜JSON.parse(xxx), die Methode wird zum Parsen eines JSON-Strings verwendet, der aus dem beschriebenen Wert bzw. String erstellt wurde Objekt
🎜🎜🎜🎜Verstehen: 🎜🎜🎜Wir können es so verstehen, dass die Originaldaten in einen neuen String
konvertiert werden und dann der neue String
wiederhergestellt wird zu einem neuen Objekt
Die Methode zum Ändern des Datentyps umgeht indirekt den Prozess des Kopierens der Objektreferenz und hat keinen Einfluss auf die Originaldaten. 🎜🎜🎜Einschränkungen: 🎜🎜🎜Der grundlegende Zweck dieser Methode besteht darin, die Integrität der Daten während der „Übertragung“ sicherzustellen, und JSON.stringify()
konvertiert den Wert in das entsprechende JSON-Format
hat auch Mängel: 🎜所以当我们拷贝函数、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]
注意,此时仅能对包含纯数字的数组进行深拷贝,因为:
但我愿称它为纯数字数组深拷贝!
有的人会认为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传送门
四、自己实现深浅拷贝
既然现有的方法无法实现深拷贝,不妨我们自己来实现一个吧~
我们只需要将所有属性即其嵌套属性原封不动的复制给新变量一份即可,抛开现有的方法,我们应该怎么做呢?
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;}
我们只需要将所有属性的引用拷贝一份即可~
相信大家在实现深拷贝的时候都会想到递归,同样是判断属性值,但如果当前类型为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[]
0
(for in
以任意顺序遍历,我们假定按正常循序遍历)1
obj[1]
另外请注意递归的方式虽然可以深拷贝,但是在性能上肯定不如浅拷贝,大家还是需要结合实际情况来选择。
写在最后
前端专项进阶系列的第五篇文章
,希望它能对大家有所帮助,如果大家有什么建议,可以在评论区留言,能帮到自己和大家就是我最大的动力!
相关免费学习推荐:javascript(视频)
Das obige ist der detaillierte Inhalt vonJavaScript-Thema 5: Tiefe und flache Kopie. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!