Heim > Web-Frontend > js-Tutorial > Hauptteil

JavaScript-Thema 5: Tiefe und flache Kopie

coldplay.xixi
Freigeben: 2021-03-08 09:36:14
nach vorne
1980 Leute haben es durchsucht

JavaScript-Thema 5: Tiefe und flache Kopie

Verstehen Sie den Prozess hinter dem Kopieren und vermeiden Sie unnötige Fehler gemeinsam an Js spezieller Serie von tiefen und flachen Kopien

3, Deep-Copy-Methode?

4. Implementieren Sie selbst eine tiefe und flache Kopie Situation:

  • Ein Datensatz wird häufig geändert, aber die Originaldaten können verwendet werden.
  • Ich benötige zwei Sätze derselben Daten, möchte aber einen nicht ändern und der andere wird sich ändern.
  • Ich brauche Um Datenoperationen vor und nach dem Vergleich durchzuführen
  • Wenn wir auf ein ähnliches Bedarfsszenario stoßen, fällt uns als Erstes das Kopieren ein, aber wir wissen nicht, dass das Kopieren auch sehr sachkundig ist~
Machen Sie es Kommt Ihnen das einfache Beispiel unten bekannt vor?

1.1 Beispiel für das Kopieren eines Basistyps

var str = 'How are you';var newStr = str;newStr = 10console.log(str); // How are youconsole.log(newStr); // 10
Nach dem Login kopieren
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. str und newStr sind wie zwei identische Räume mit demselben Grundriss, aber ohne Verbindung. 1.2 Referenztyp-Kopierbeispiel
var data = [1, 2, 3, 4, 5];var newData = data;newData[0] = 100;console.log(data[0]); // 100console.log(newData[0]); // 100
Nach dem Login kopieren
Ä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 um
das Wissen über das Zitieren von Datenkopien

.

Wenn Sie Fragen zu den Datentypen von Js haben, können Sie sich auch „Grundlegende Datentypen in JavaScript“ ansehen

    Bildbeschreibung hier einfügen
  1. 2. Flache Kopie
  2. Die Aufteilung der Kopien wird anhand von Referenztypen besprochen. Flache Kopie - wie der Name schon sagt schlägt vor, flache Kopie Es ist eine „flache Kopie“. Tatsächlich erledigt es nur oberflächliche Arbeit:
  3. 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]
    Nach dem Login kopieren
  4. Nichts passiert (Operation) Glücklicherweise ändern sich die in den beiden Variablen gespeicherten Daten, sobald das neue Array bearbeitet wird.

Der Grund, warum eine solche Situation auftritt, liegt auch in den grundlegenden Eigenschaften des Referenztyps:

Der in der Variablen gespeicherte Wert ist ein Zeiger (Punkt), der auf die Speicheradresse von zeigt das gespeicherte Objekt. Das Zuweisen eines Werts zu einer neuen Variablen entspricht dem Zuweisen eines neuen Schlüssels, und der Raum wurde nicht geändert.

Sowohl Slice als auch Concat im Array geben ein neues Array zurück. Probieren wir es gemeinsam aus: strnewStr就好比两个一模一样的房间,布局一致却毫无关联。

1.2 引用类型拷贝示例
var arr = [1,2,3,4];var res = arr.slice();// 或者res = arr.concat()res[0] = 100;console.log(arr); // [1,2,3,4]
Nach dem Login kopieren

类似的代码段,但这次我们使用数组这个引用类型举例,你会发现修改赋值后的数据,原始数据也跟着改变了,这显然不满足我们的需要。本篇文章就来聊一聊引用数据拷贝的学问

如果大家对Js的数据类型存在着疑问,不妨看看《JavaScript中的基本数据类型》

JavaScript-Thema 5: Tiefe und flache Kopie

二、浅拷贝

拷贝的划分都是针对引用类型来讨论的,浅拷贝——顾名思义,浅拷贝就是“浅层拷贝”,实际上只做了表面功夫:

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] ]
Nach dem Login kopieren

不发生事情(操作)还好,一旦对新数组进行了操作,两个变量中所保存的数据都会发生改变。

发生这类情况的原因也是因为引用类型的基本特性:

  • 存储在变量处的值是一个指针(point),指向存储对象的内存地址。赋值给新变量相当于配了一把新钥匙,房间并没有换。

数组中的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}]
Nach dem Login kopieren

这个问题这么快就解决了?虽然对这一层数据进行了这样的的处理后,确实解决了问题,但!

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]
Nach dem Login kopieren
Nach dem Login kopieren

果然事情没有那么简单,这也是因为数据类型的不同。

S 不允许我们直接操作内存中的地址,也就是说不能操作对象的内存空间,所以,我们对对象的操作都只是在操作它的引用而已。

既然浅拷贝达不到我们的要求,本着效率的原则,我们找找有没有帮助我们实现深拷贝的方法。

JavaScript-Thema 5: Tiefe und flache Kopie

三、深拷贝的方法?

数据的方法失败了,还有没有其他办法?我们需要实现真正意义上的拷贝出独立的数据。

3.1 JSON

这里我们利用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 } }
Nach dem Login kopieren
Nach dem Login kopieren

这个方法应该是实现深拷贝最简洁的方法,但是,它仍然存在问题,我们先来看看刚才都做了些什么:

  1. 定义一个包含都过类型的数组arr
  2. JSON.stringify(arr), 将一个 JavaScript 对象或值转换为 JSON 字符串
  3. JSON.parse(xxx), 方法用来解析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;}
Nach dem Login kopieren
Nach dem Login kopieren
Ist dieses Problem so schnell gelöst? Obwohl diese Datenschicht auf diese Weise verarbeitet wird, ist das Problem zwar gelöst, aber! 🎜
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;}
Nach dem Login kopieren
Nach dem Login kopieren
🎜Natürlich ist die Sache nicht so einfach, das liegt auch an den unterschiedlichen Datentypen. 🎜🎜S erlaubt uns nicht, die Adresse im Speicher direkt zu bearbeiten, was bedeutet, dass wir den Speicherplatz des Objekts nicht bedienen können. Daher beziehen sich unsere Operationen auf das Objekt nur auf seine Referenz. 🎜🎜Da 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. 🎜🎜Bildbeschreibung hier einfügen🎜🎜🎜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);
Nach dem Login kopieren
Nach dem Login kopieren
🎜 Dies Methode sollte die einfachste Möglichkeit sein, Deep Copy zu implementieren, aber 🎜Es gibt immer noch Probleme🎜 Schauen wir uns zunächst an, was wir gerade getan haben: 🎜🎜🎜Definieren Sie ein Array 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、任意的函数以及 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]
Nach dem Login kopieren
Nach dem Login kopieren

注意,此时仅能对包含纯数字的数组进行深拷贝,因为:

  1. toString无法正确的处理对象和函数
  2. Number无法处理 false、undefined等数据类型

但我愿称它为纯数字数组深拷贝

JavaScript-Thema 5: Tiefe und flache Kopie

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 } }
Nach dem Login kopieren
Nach dem Login kopieren

神奇,第一层属性没有改变,但第二层却同步改变了,这是为什么呢?

因为 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;}
Nach dem Login kopieren
Nach dem Login kopieren

我们只需要将所有属性的引用拷贝一份即可~

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;}
Nach dem Login kopieren
Nach dem Login kopieren

我们用白话来解释一下deepCopy都做了什么

const obj = [1, { a: 1, b: { name: '余光'} } ];const resObj = deepCopy(obj);
Nach dem Login kopieren
Nach dem Login kopieren
  • 读取 obj,创建 第一个newObj
    • 判断类型为 []
    • key为 0 (for in 以任意顺序遍历,我们假定按正常循序遍历)
    • 判断不是引用类型,直接复制
    • key为 1
    • 判断是引用类型
    • 进入递归,重新走了一遍刚才的流程,只不过读取的是obj[1]

另外请注意递归的方式虽然可以深拷贝,但是在性能上肯定不如浅拷贝,大家还是需要结合实际情况来选择。

写在最后

JavaScript-Thema 5: Tiefe und flache Kopie

前端专项进阶系列的第五篇文章,希望它能对大家有所帮助,如果大家有什么建议,可以在评论区留言,能帮到自己和大家就是我最大的动力!

相关免费学习推荐: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!

Verwandte Etiketten:
Quelle:csdn.net
Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage