Primitive und Reference Type – Programmieren lernen mit JavaScript – Thytos
Nächstes Video startet in 3 Sekunden.
Programmieren lernen mit JavaScript

Primitive und Reference Type

Je nach Da­ten­typ wer­den man­che Wer­te in Ja­va­Script nur per Re­fe­renz ge­spei­chert. Bei einer Neu­zu­wei­sung wird dann nur die Re­fe­renz ko­piert, nicht das Ob­jekt.

Wenn ihr in eurer Browser-Konsole zwei Strings miteinander vergleicht, erhaltet ihr true wenn die Strings den gleichen Wert haben.

"foo" === "foo"
// => true

Anders verhält es sich, wenn der String in einem Array steht. Selbst wenn zwei Arrays den gleichen Inhalt haben, ergibt der Vergleich false.

["foo"] === ["foo"]
// => false

["foo"] == ["foo"]
// => false

Das gleiche bei Funktionen: Werden zwei leere Funktionen erstellt und miteinander verglichen, ist auch dabei das Ergebnis false.

var a = function () {};
var b = function () {};

a === b
// => false

a == b
// => false

Und wenn eine Variable mit einer Zahl definiert wird, dann einer zweiten Variable zugewiesen wird und die erste Variable erhöht wird, dann ist die erste Variable höher als die zweite Variable.

var a = 5;
var b = a;
a++;

a;
// => 6

b;
// => 5;

Aber wenn der gleiche Vorgang durchlaufen wird, mit dem einzigen Unterschied, dass a auf ein Array mit einer Zahl gesetzt wird und dann entsprechend die Zahl im Array erhöht wird, haben anschließend a und b den gleichen Wert.

var a = [5];
var b = a;
a[0]++;

a;
// => [6]

b;
// => [6] // ???

Was passiert hier? Woher kommt das unterschiedliche Verhalten?

Unterschiedliche Speicherung

In JavaScript werden Werte je nach Datentyp unterschiedlich gespeichert. Es gibt die primitiven Datentypen und die Referenzdatentypen, im Englischen primitive type und reference type.

Art Datentyp
Primitiv
  • Undefined
  • Boolean
  • Number
  • String
Referenz
  • Function
  • Object

Wenn eine Variable mit einem Wert eines primitiven Datentyps erstellt wird, werden im Speicher die Metainformationen der Variablen abgelegt, zusammen mit ihrem Wert. Wenn dagegen eine Variable mit einem Wert eines Referenzdatentypen erstellt wird, wird im Speicher der Wert abgelegt und die Variable, mit einer Referenz auf diesen Wert. Die Variable zeigt nur auf das Objekt im Speicher, sie besitzt es nicht.

Das hat Einfluss darauf, wie Werte übertragen werden, wenn sie anderen Variablen oder Properties zugewiesen werden. Bei primitiven Typen wird eine Kopie des Werts erstellt. Als im vorletzten Beispiel a mit dem Wert 5 der Variable b zugewiesen wurde, entstand eine Kopie der Zahl 5. a konnte dadurch verändert werden, ohne dass dies Auswirkung auf b hatte.
Bei Referenztypen dagegen wird eine Kopie der Referenz erstellt. Als im letzten Beispiel a mit dem Wert [5] der Variable b zugewiesen wurde, entstand eine Kopie der Referenz zu demselben Array. Deswegen zeigte auch b einen anderen Wert, als die Zahl im Array über a verändert wurde.

Entsprechend verhält es sich mit den Vergleichsoperatoren. Bei primitiven Typen wird der Wert verglichen, während bei Referenztypen die Referenz verglichen wird.

[5] === [5]
// => false

Hier werden zwei Arrays im Speicher erstellt, die zwar inhaltlich gleich sind, aber dennoch getrennt voneinander im Speicher liegen. Beim Vergleich werden die Referenzen zu den Objekten verglichen, die dadurch entsprechend unterschiedlich sind.
Würde dagegen dasselbe Objekt zwei Variablen zugewiesen werden und die beiden gegenübergestellt werden, wäre die Gleichheit erfüllt.

// Erstellung von zwei Objekten
var a = [5];
var b = [5];

// Die Objekte sind nicht identisch
a === b
// => false

// b wird auf dasselbe Objekt wie a gesetzt
b = a;

// Jetzt ist das Objekt identisch
a === b
// => true

In der Arbeit mit Objekten geht es deswegen nicht um Wertgleichheit, sondern um Identität. Zwei Objekte, die die gleichen Werte beinhalten, sind dadurch nicht dasselbe Objekt; genauso wie zwei Zwillinge, die sich gleich kleiden und das gleiche tun, deswegen auch nicht dieselbe Person sind.

Außerdem: Wichtige Array-Methoden

Im folgenden werden Array-Methoden vorgestellt, die ihr immer wieder beim Programmieren braucht.
Ihr könnt euch ein Array schaffen, mit dem ihr die Methoden ausprobiert, indem ihr beispielsweise die split-Methode nutzt, da es damit sehr einfach ist, ein nicht-leeres Array zu erstellen.

var arr = "abcdefgh".split("");

arr;
// => ["a", "b", "c", "d", "e", "f", "g", "h"]

pop und push

Die pop-Methode entfernt das letzte Element aus dem Array und gibt es zurück.

var letztesElement = arr.pop();

arr;
// => ["a", "b", "c", "d", "e", "f", "g"]

letztesElement;
// => "h"

Die push-Methode hängt ein oder mehrere Elemente an das Ende des Arrays an.

arr.push(letztesElement);

arr;
// => ["a", "b", "c", "d", "e", "f", "g", "h"]

shift und unshift

Die shift-Methode entfernt das erste Element aus dem Array und gibt es zurück.

var erstesElement = arr.shift();

arr;
// => ["b", "c", "d", "e", "f", "g", "h"]

erstesElement;
// => "a"

Die unshift-Methode setzt ein oder mehrere Elemente an den Anfang des Arrays ein.

arr.unshift(erstesElement);

arr;
// => ["a", "b", "c", "d", "e", "f", "g", "h"]

reverse und sort

Die reverse-Methode dreht die Reihenfolge des Arrays um.

arr.reverse();
// => ["h", "g", "f", "e", "d", "c", "b", "a"]

Die sort-Methode sortiert das Array nach Unicode Codepoints. Sie nimmt auch eine Funktion als Parameter an, mit der die Sortierung spezifischen Anforderungen angepasst werden kann.

arr.sort();
// => ["a", "b", "c", "d", "e", "f", "g", "h"]

slice und splice

Die slice-Methode kopiert einen Teil des Arrays und gibt ihn als neues Array zurück.

arr;
// => ["a", "b", "c", "d", "e", "f", "g", "h"]

arr.slice(1, 4); // Index 1 bis Index 4
// => ["b", "c", "d"]

arr; // Das Array selbst hat sich nicht verändert
// => ["a", "b", "c", "d", "e", "f", "g", "h"]

// Ist kein End-Index gegeben, ist das Ende des
// Arrays das Ende des Teil-Arrays
arr.slice(5); // Kein End-Index
// => ["f", "g", "h"]

// Negative Indizes beziehen sich aufs Ende des Arrays
arr.slice(-3);
// => ["f", "g", "h"]

// Ohne Parameter wird eine Kopie des Arrays erstellt
arr.slice();
// => ["a", "b", "c", "d", "e", "f", "g", "h"]

// Die Kopie ist ein neues Objekt, also keine Identität
arr === arr.slice()
// => false

Die splice-Methode weist auf den ersten Blick Parallelen zur slice-Methode auf, denn auch sie gibt ein Teil-Array zurück.
Es gibt aber drei wesentliche Unterschiede:

  • Das Teil-Array, das zurückgegeben wird, wird aus dem Ursprungs-Array gelöscht (bei slice werden die Elemente kopiert)
  • Der erste Parameter ist der Start-Index, der zweite die Anzahl von Elementen, die beginnend am Start-Index gelöscht werden sollen (bei slice ist der zweite Parameter der End-Index)
  • Ab dem zweiten Parameter können beliebige Werte übergeben werden, die an der Stelle des Index eingefügt werden (mit slice können keine Elemente eingefügt werden)
arr;
// => ["a", "b", "c", "d", "e", "f", "g", "h"]

arr.splice(1, 3); // Index 1 und Länge 3
// => ["b", "c", "d"]

arr; // Die Elemente fehlen in arr
// => ["a", "e", "f", "g", "h"]

// Elemente können ausgetauscht werden
arr.splice(1, 1, "b");
// => ["e"]

arr; // "e" wurde durch "b" ersetzt
// => ["a", "b", "f", "g", "h"]

// Mit 0 als zweiten Parameter wird nichts gelöscht
arr.splice(2, 0, "c");
// => []

arr; // "c" wurde hinzugefügt
// => ["a", "b", "c", "f", "g", "h"]

// Ein negativer Start-Index beginnt am Ende des Arrays
arr.splice(-3, 0, "d", "e");
// => ["a", "b", "c", "d", "e", "f", "g", "h"]

concat

Die concat-Methode fügt zwei oder mehr Arrays aneinander und gibt es als neues Array zurück.

arr.concat(arr);
// => ["a", "b", "c", "d", "e", "f", "g", "h", "a", "b", "c", "d", "e", "f", "g", "h"]

arr; // Unbeeinflusst
// => ["a", "b", "c", "d", "e", "f", "g", "h"]

// Die ersten und letzten 3 Elemente zusammen
arr.slice(0, 3).concat(arr.slice(-3));
// => ["a", "b", "c", "f", "g", "h"]