(253-1) (das ist `9007199254740991`) oder kleiner als -(-253-1) für Negative sind. Es handelt sich um eine technische Einschränkung, die durch ihre interne Darstellung bedingt ist.
+=======
+In JavaScript, the "number" type cannot represent integer values larger than (253-1) (that's `9007199254740991`), or less than -(253-1) for negatives. It's a technical limitation caused by their internal representation.
+>>>>>>> a82915575863d33db6b892087975f84dea6cb425
Für die meisten Zwecke reicht das völlig aus, aber manchmal brauchen wir wirklich große Zahlen, z.B. für die Kryptographie oder Zeitstempel mit Mikrosekunden-Genauigkeit.
+=======
+In JavaScript, the "number" type cannot safely represent integer values larger than (253-1) (that's `9007199254740991`), or less than -(253-1) for negatives.
+
+To be really precise, the "number" type can store larger integers (up to 1.7976931348623157 * 10308), but outside of the safe integer range ±(253-1) there'll be a precision error, because not all digits fit into the fixed 64-bit storage. So an "approximate" value may be stored.
+
+For example, these two numbers (right above the safe range) are the same:
+
+```js
+console.log(9007199254740991 + 1); // 9007199254740992
+console.log(9007199254740991 + 2); // 9007199254740992
+```
+
+So to say, all odd integers greater than (253-1) can't be stored at all in the "number" type.
+
+For most purposes ±(253-1) range is quite enough, but sometimes we need the entire range of really big integers, e.g. for cryptography or microsecond-precision timestamps.
+>>>>>>> d694e895efe89922a109702085b6ca1efeffea10
Der Typ `BigInt` wurde kürzlich der Sprache hinzugefügt, um Ganzzahlen beliebiger Länge darzustellen.
@@ -81,10 +114,21 @@ const bigInt = 1234567890123456789012345678901234567890n;
Da `BigInt`-Zahlen selten benötigt werden, behandeln wir sie hier nicht, sondern widmen ihnen ein eigenes Kapitel ±(253-1).
+- `bigint` is for integer numbers of arbitrary length.
+- `string` for strings. A string may have zero or more characters, there's no separate single-character type.
+- `boolean` for `true`/`false`.
+- `null` for unknown values -- a standalone type that has a single value `null`.
+- `undefined` for unassigned values -- a standalone type that has a single value `undefined`.
+- `object` for more complex data structures.
+- `symbol` for unique identifiers.
+>>>>>>> a82915575863d33db6b892087975f84dea6cb425
+=======
+
+There are 8 basic data types in JavaScript.
+
+- Seven primitive data types:
+ - `number` for numbers of any kind: integer or floating-point, integers are limited by ±(253-1).
+ - `bigint` for integer numbers of arbitrary length.
+ - `string` for strings. A string may have zero or more characters, there's no separate single-character type.
+ - `boolean` for `true`/`false`.
+ - `null` for unknown values -- a standalone type that has a single value `null`.
+ - `undefined` for unassigned values -- a standalone type that has a single value `undefined`.
+ - `symbol` for unique identifiers.
+- And one non-primitive data type:
+ - `object` for more complex data structures.
+>>>>>>> d694e895efe89922a109702085b6ca1efeffea10
Mit dem Operator `typeof` können wir sehen, welcher Typ in einer Variablen gespeichert ist.
+<<<<<<< HEAD
- Zwei Formen: `typeof x` oder `typeof(x)`.
- Gibt einen String mit dem Namen des Typs zurück, wie `"string"`.
- Für `null` gibt es `"object"` zurück -- dies ist ein Fehler in der Sprache, es ist eigentlich kein Objekt.
+=======
+- Usually used as `typeof x`, but `typeof(x)` is also possible.
+- Returns a string with the name of the type, like `"string"`.
+- For `null` returns `"object"` -- this is an error in the language, it's not actually an object.
+>>>>>>> 29216730a877be28d0a75a459676db6e7f5c4834
In den nächsten Kapiteln werden wir uns auf skalare Datentypen konzentrieren und, sobald wir mit ihnen vertraut sind, zu Objekten übergehen.
diff --git a/1-js/02-first-steps/06-alert-prompt-confirm/article.md b/1-js/02-first-steps/06-alert-prompt-confirm/article.md
index ca2cd8b6c..f94e7ab31 100644
--- a/1-js/02-first-steps/06-alert-prompt-confirm/article.md
+++ b/1-js/02-first-steps/06-alert-prompt-confirm/article.md
@@ -4,7 +4,11 @@ Da wir den Browser als unsere Demo-Umgebung verwenden, wollen wir einige Funktio
## alert
+<<<<<<< HEAD
Diese haben wir bereits gesehen. Es zeigt eine Meldung an und wartet darauf, dass der Benutzer "OK" drückt.
+=======
+This one we've seen already. It shows a message and waits for the user to press "OK".
+>>>>>>> a82915575863d33db6b892087975f84dea6cb425
Zum Beispiel:
@@ -30,8 +34,13 @@ Es wir ein modales Fenster mit einer Meldung angezeigt, ein Eingabefeld für den
`default`
: Ein optionaler zweiter Parameter, der vor-befüllte Wert für das Eingabefeld.
+<<<<<<< HEAD
```smart header="Eckige Klammer in der Syntax `[...]`"
Die eckigen Klammern um `default` in der obigen Syntax bedeuten, dass der Parameter optional, nicht erforderlich ist.
+=======
+```smart header="The square brackets in syntax `[...]`"
+The square brackets around `default` in the syntax above denote that the parameter is optional, not required.
+>>>>>>> a82915575863d33db6b892087975f84dea6cb425
```
Der Besucher kann etwas in das Eingabefeld eingeben und OK drücken. Dann erhalten wir diesen Text im `Ergebnis`. Oder er kann die Eingabe beenden, indem er Abbrechen drückt oder `key:Esc` drückt, dann erhalten wir `Null` als `Ergebnis`.
diff --git a/1-js/02-first-steps/07-type-conversions/article.md b/1-js/02-first-steps/07-type-conversions/article.md
index 14a040f74..ddde02968 100644
--- a/1-js/02-first-steps/07-type-conversions/article.md
+++ b/1-js/02-first-steps/07-type-conversions/article.md
@@ -6,8 +6,13 @@ Zum Beispiel wandelt `alert` automatisch jeden Wert in den Datentyp "String" um
Es gibt auch Fälle, in denen wir einen Wert explizit in den erwarteten Datentyp umwandeln müssen.
+<<<<<<< HEAD
```smart header="Noch nicht von Objekten sprechen"
In diesem Kapitel werden wir keine Objekte behandeln. Im Moment werden wir nur über Primitive sprechen.
+=======
+```smart header="Not talking about objects yet"
+In this chapter, we won't cover objects. For now, we'll just be talking about primitives.
+>>>>>>> 291b5c05b99452cf8a0d32bd32426926dbcc0ce0
Später, nachdem wir etwas über Objekte gelernt haben, werden wir im Kapitel true und false | `1` und `0` |
| `string` | Leerzeichen am Anfang und am Ende werden entfernt. Wenn der verbleibende String leer ist, ist das Ergebnis `0`. Andernfalls wird die Zahl aus dem String "gelesen". Ein Fehler ergibt `NaN`. |
+=======
+|true and false | `1` and `0` |
+| `string` | Whitespaces (includes spaces, tabs `\t`, newlines `\n` etc.) from the start and end are removed. If the remaining string is empty, the result is `0`. Otherwise, the number is "read" from the string. An error gives `NaN`. |
+>>>>>>> d694e895efe89922a109702085b6ca1efeffea10
Beispiele:
@@ -130,7 +144,11 @@ Die Umwandlung folgt diesen Regeln:
|`undefined`|`NaN`|
|`null`|`0`|
|true / false | `1 / 0` |
+<<<<<<< HEAD
| `string` | Der String wird "als solches" gelesen, Leerzeichen auf beiden Seiten werden ignoriert. Ein leerer String wird zu `0`. Ein Fehler ergibt `NaN`. |
+=======
+| `string` | The string is read "as is", whitespaces (includes spaces, tabs `\t`, newlines `\n` etc.) from both sides are ignored. An empty string becomes `0`. An error gives `NaN`. |
+>>>>>>> d694e895efe89922a109702085b6ca1efeffea10
**`Boolean Umwandlung`** -- Tritt in logischen Operationen auf. Kann mit `Boolean(value)` explizit ausgeführt werden.
diff --git a/1-js/02-first-steps/08-operators/3-primitive-conversions-questions/solution.md b/1-js/02-first-steps/08-operators/3-primitive-conversions-questions/solution.md
index 675aa72ba..4e05ffe74 100644
--- a/1-js/02-first-steps/08-operators/3-primitive-conversions-questions/solution.md
+++ b/1-js/02-first-steps/08-operators/3-primitive-conversions-questions/solution.md
@@ -9,7 +9,6 @@ true + false = 1
"$" + 4 + 5 = "$45"
"4" - 2 = 2
"4px" - 2 = NaN
-7 / 0 = Infinity
" -9 " + 5 = " -9 5" // (3)
" -9 " - 5 = -14 // (4)
null + 1 = 1 // (5)
@@ -17,6 +16,7 @@ undefined + 1 = NaN // (6)
" \t \n" - 2 = -2 // (7)
```
+<<<<<<< HEAD
1. Die Addition mit einem String `"" + 1` konvertiert `1` in einen String: `"" + 1 = "1"`, und dann haben wir `"1" + 0`, die gleiche Regel wird angewendet.
2. Die Subtraktion `-` funktioniert (wie die meisten mathematischen Operationen) nur mit Zahlen. Sie konvertiert eine leere Zeichenfolge `""` in `0`.
3. Die Addition mit einem String fügt dem String die Zahl `5` hinzu.
@@ -24,3 +24,12 @@ undefined + 1 = NaN // (6)
5. `null` wird nach der numerischen Umwandlung zu `0`.
6. `undefined` wird nach der numerischen Umwandlung zu `NaN`.
7. Leerzeichen werden am Anfang und Ende eines Strings abgeschnitten, wenn ein String in eine Zahl umgewandelt wird. Hier besteht der gesamte String aus Leerzeichen wie `\t`, `\n` und einem "regulären" Leerzeichen dazwischen. Ähnlich wie bei einem leeren String wird sie zu `0`.
+=======
+1. The addition with a string `"" + 1` converts `1` to a string: `"" + 1 = "1"`, and then we have `"1" + 0`, the same rule is applied.
+2. The subtraction `-` (like most math operations) only works with numbers, it converts an empty string `""` to `0`.
+3. The addition with a string appends the number `5` to the string.
+4. The subtraction always converts to numbers, so it makes `" -9 "` a number `-9` (ignoring spaces around it).
+5. `null` becomes `0` after the numeric conversion.
+6. `undefined` becomes `NaN` after the numeric conversion.
+7. Space characters are trimmed off string start and end when a string is converted to a number. Here the whole string consists of space characters, such as `\t`, `\n` and a "regular" space between them. So, similarly to an empty string, it becomes `0`.
+>>>>>>> d694e895efe89922a109702085b6ca1efeffea10
diff --git a/1-js/02-first-steps/08-operators/3-primitive-conversions-questions/task.md b/1-js/02-first-steps/08-operators/3-primitive-conversions-questions/task.md
index 5755e4a32..e19c3c528 100644
--- a/1-js/02-first-steps/08-operators/3-primitive-conversions-questions/task.md
+++ b/1-js/02-first-steps/08-operators/3-primitive-conversions-questions/task.md
@@ -16,7 +16,6 @@ true + false
"$" + 4 + 5
"4" - 2
"4px" - 2
-7 / 0
" -9 " + 5
" -9 " - 5
null + 1
diff --git a/1-js/02-first-steps/08-operators/4-fix-prompt/solution.md b/1-js/02-first-steps/08-operators/4-fix-prompt/solution.md
index 04f73fbd0..209a0702c 100644
--- a/1-js/02-first-steps/08-operators/4-fix-prompt/solution.md
+++ b/1-js/02-first-steps/08-operators/4-fix-prompt/solution.md
@@ -9,7 +9,7 @@ let b = "2"; // prompt("Second number?", 2);
alert(a + b); // 12
```
-What we should to is to convert strings to numbers before `+`. For example, using `Number()` or prepending them with `+`.
+What we should do is to convert strings to numbers before `+`. For example, using `Number()` or prepending them with `+`.
For example, right before `prompt`:
diff --git a/1-js/02-first-steps/08-operators/article.md b/1-js/02-first-steps/08-operators/article.md
index 7943b0ec2..0653647b5 100644
--- a/1-js/02-first-steps/08-operators/article.md
+++ b/1-js/02-first-steps/08-operators/article.md
@@ -50,23 +50,40 @@ Das Ergebnis von `a % b` ist der [Rest](https://en.wikipedia.org/wiki/Remainder)
Zum Beispiel:
```js run
+<<<<<<< HEAD
alert( 5 % 2 ); // 1, der Rest von 5 geteilt durch 2
alert( 8 % 3 ); // 2, der Rest von 8 geteilt durch 3
+=======
+alert( 5 % 2 ); // 1, the remainder of 5 divided by 2
+alert( 8 % 3 ); // 2, the remainder of 8 divided by 3
+alert( 8 % 4 ); // 0, the remainder of 8 divided by 4
+>>>>>>> d694e895efe89922a109702085b6ca1efeffea10
```
### Potenzierung **
+
Der Exponentiationsoperator `a ** b` multipliziert `a` mit sich selbst `b` mal.
+
+
Zum Beispiel:
```js run
+
alert( 2 ** 2 ); // 4 (2 mit sich selbst 2 mal multipliziert)
alert( 2 ** 3 ); // 8 (2 * 2 * 2, 3 mal)
alert( 2 ** 4 ); // 16 (2 * 2 * 2 * 2, 4 mal)
+
```
-Mathematically, the exponentiation is defined for non-integer numbers as well. For example, a square root is an exponentiation by `1/2`:
+<<<<<<< HEAD
+Genau wie in der Mathematik ist der Potenzierungsoperator auch für nicht-ganzzahlige Zahlen definiert.
+=======
+Just like in maths, the exponentiation operator is defined for non-integer numbers as well.
+>>>>>>> d694e895efe89922a109702085b6ca1efeffea10
+
+Zum Beispiel ist eine Quadratwurzel eine Potenzierung durch ½:
```js run
alert( 4 ** (1/2) ); // 2 (Die Potenz von 1/2 ist gleich der Quadratwurzel)
@@ -76,7 +93,11 @@ alert( 8 ** (1/3) ); // 2 (Die Potenz von 1/3 ist gleich der dritten Wurzel)
## Binäre String-Verkettung +
+<<<<<<< HEAD
Lernen wir Funktionen von JavaScript-Operatoren kennen, die über das schulische Rechnen hinausgehen.
+=======
+Let's meet the features of JavaScript operators that are beyond school arithmetics.
+>>>>>>> d694e895efe89922a109702085b6ca1efeffea10
Normalerweise summiert der Plus-Operator `+` Zahlen.
@@ -104,8 +125,15 @@ Hier ist ein komplexeres Beispiel:
alert(2 + 2 + '1' ); // "41" und nicht "221"
```
+
Dabei arbeiten die Operatoren einer nach dem anderen. Das erste `+` summiert zwei Zahlen, so dass es `4` zurückgibt, dann fügt das nächste `+` die Zeichenkette `1` hinzu, so dass es `4 + '1' = 41` ergibt.
+
+```js run
+alert('1' + 2 + 2); // "122" and not "14"
+```
+
+
Das binäre `+` ist der einzige Operator, der Zeichenketten auf diese Weise unterstützt. Andere arithmetische Operatoren arbeiten nur mit Zahlen und konvertieren ihre Operanden immer in Zahlen.
Hier ist die Darstellung für Subtraktion und Division:
@@ -180,11 +208,15 @@ Klammern überschreiben alle Prioritäten. Wenn wir mit der Standardreihenfolge
In JavaScript gibt es viele Operatoren. Jeder Operator hat eine entsprechende Vorrangsnummer. Der mit der größeren Nummer wird zuerst ausgeführt. Bei gleicher Rangfolge erfolgt die Ausführung von links nach rechts.
+
Hier ist ein Auszug aus der [Ranglistentabelle](https://developer.mozilla.org/de/docs/Web/JavaScript/Reference/Operators/Operator_Precedence) (Du musst dir das nicht merken, aber beachte, dass unäre Operatoren höher sind als entsprechende binäre):
+
| Vorrang | Name | Zeichen |
|------------|------|------|
| ... | ... | ... |
+<<<<<<< HEAD
+
| 17 | Unäres Plus | `+` |
| 17 | Unäres Minus | `-` |
| 16 | Potenzierung | `**` |
@@ -192,15 +224,34 @@ Hier ist ein Auszug aus der [Ranglistentabelle](https://developer.mozilla.org/de
| 15 | Division | `/` |
| 13 | Addition | `+` |
| 13 | Subtraktion | `-` |
+=======
+| 14 | unary plus | `+` |
+| 14 | unary negation | `-` |
+| 13 | exponentiation | `**` |
+| 12 | multiplication | `*` |
+| 12 | division | `/` |
+| 11 | addition | `+` |
+| 11 | subtraction | `-` |
+>>>>>>> d694e895efe89922a109702085b6ca1efeffea10
| ... | ... | ... |
| 3 | Zuweisung | `=` |
| ... | ... | ... |
+<<<<<<< HEAD
Wie wir sehen können, hat das "unäre Plus" eine Priorität von `17`, die höher ist als die `13` der "Addition" (binäres Plus). Deshalb wirken in dem Ausdruck `"+apples + +oranges"` unäre Pluszeichen vor der Addition.
+
+=======
+As we can see, the "unary plus" has a priority of `14` which is higher than the `11` of "addition" (binary plus). That's why, in the expression `"+apples + +oranges"`, unary pluses work before the addition.
+>>>>>>> d694e895efe89922a109702085b6ca1efeffea10
+
+
## Zuweisung
-Beachten wir, dass eine Zuweisung `=` auch ein Operator ist. Es ist in der Ranglistentabelle mit der sehr niedrigen Priorität `3` aufgeführt.
+
+Beachten wir, dass eine Zuweisung `=` auch ein Operator ist. Sie ist in der Ranglistentabelle mit der sehr niedrigen Priorität `3` aufgeführt.
+
+
Wenn wir also eine Variable wie `x = 2 * 2 + 1` zuweisen, werden zuerst die Berechnungen durchgeführt und dann das `=` ausgewertet, wobei das Ergebnis in `x` gespeichert wird.
@@ -214,7 +265,11 @@ alert( x ); // 5
Die Tatsache, dass `=` ein Operator und kein "magisches" Sprachkonstrukt ist, hat eine interessante Implikation.
-Die meisten Operatoren in JavaScript geben einen Wert zurück. Das ist bei `+` und `-` offensichtlich, gilt aber auch für `=`.
+
+Die meisten Operatoren in JavaScript geben einen Wert zurück. Das ist bei `+` und `-` offensichtlich, dies gilt aber auch für `=`.
+
+
+
Der Aufruf `x = Wert` schreibt den `Wert` in `x` *und gibt ihn dann zurück*.
@@ -232,11 +287,11 @@ alert( a ); // 3
alert( c ); // 0
```
-Im obigen Beispiel ist das Ergebnis des Ausdrucks `(a = b + 1)` der Wert, der `a` zugewiesen wurde (d.h. `3`). Es wird dann für weitere Auswertungen verwendet.
+Im obigen Beispiel ist das Ergebnis des Ausdrucks `(a = b + 1)` der Wert, der `a` zugewiesen wurde (d.h. `3`). Dieser Wert wird dann für weitere Auswertungen verwendet.
-Seltsamer Code, nicht wahr? Wir sollten verstehen, wie er funktioniert, denn manchmal sehen wir ihn in JavaScript-Bibliotheken.
+Seltsamer Code, nicht wahr? Wir sollten uns über seine Funktionalität klar machen, denn Code wie dieser kommt in JavaScript-Bibliotheken vor.
-Trotzdem, bitte schreibe Code nicht so. Solche Tricks machen Code bestimmt nicht klarer oder leserlicher.
+Dennoch, bitte schreibe keinen Code auf diese Weise. Tricks wie diese machen Code weder klarer noch leserlicher.
### Verkettung von Zuweisungen
@@ -256,7 +311,7 @@ alert( c ); // 4
Verkettete Zuweisungen werden von rechts nach links ausgewertet. Zuerst wird der ganz rechte Ausdruck `2 + 2` ausgewertet und dann den Variablen auf der linken Seite zugewiesen: `c`, `b` und `a`. Am Ende teilen sich alle Variablen einen einzigen Wert.
-Noch einmal: Aus Gründen der Lesbarkeit ist es besser, solchen Code in wenige Zeilen aufzuteilen:
+Auch hier gilt: Aufgrund der Lesbarkeit ist es besser, solchen Code in wenige Zeilen aufzuteilen:
```js
c = 2 + 2;
@@ -267,7 +322,7 @@ Das ist leichter zu lesen, besonders wenn man den Code mit den Augen schnell sca
## An Ort und Stelle modifizieren
-Wir müssen oft einen Operator auf eine Variable anwenden und das neue Ergebnis in derselben Variable speichern.
+Häufig müssen wir einen Operator auf eine Variable anwenden und das neue Ergebnis in derselben Variable speichern.
Zum Beispiel:
@@ -277,7 +332,7 @@ n = n + 5;
n = n * 2;
```
-Diese Notation kann mit den Operatoren `+=` und `*=` gekürzt werden:
+Diese Notation kann man mit den Operatoren `+=` und `*=` abkürzen:
```js run
let n = 2;
@@ -289,14 +344,18 @@ alert( n ); // 14
Für alle arithmetischen und bitweisen Operatoren gibt es kurze "modifizieren-und-zuweisen"-Operatoren: `/=`, `-=`, usw.
-Solche Operatoren haben den gleichen Stellenwert wie eine normale Zuweisung, so dass sie nach den anderen Berechnungen durchgeführt werden:
+Solche Operatoren haben den gleichen Stellenwert wie eine normale Zuweisung, sodass sie nach den anderen Berechnungen durchgeführt werden:
```js run
let n = 2;
-n *= 3 + 5;
+n *= 3 + 5; // right part evaluated first, same as n *= 8
+<<<<<<< HEAD
alert( n ); // 16 (der rechte Teil wird zuerst ausgewertet, wie n *= 8)
+=======
+alert( n ); // 16
+>>>>>>> d694e895efe89922a109702085b6ca1efeffea10
```
## Inkrementieren/Dekrementieren
@@ -335,18 +394,18 @@ Beide Anweisungen tun dasselbe: erhöhen `counter` um `1`.
Gibt es da einen Unterschied? Ja, aber wir können es nur sehen, wenn wir den zurückgegebenen Wert von `++/-` verwenden.
-Lass uns klären. Wie wir wissen, geben alle Operatoren einen Wert zurück. Inkrementieren/Dekrementieren ist keine Ausnahme. Die Präfix-Form gibt den neuen Wert zurück, während die Postfix-Form den alten Wert zurück gibt (vor dem Inkrementieren/Dekrementieren).
+Lass es uns aufklären. Wie wir wissen, geben alle Operatoren einen Wert zurück. Inkrementieren/Dekrementieren ist keine Ausnahme. Die Präfix-Form gibt den neuen Wert zurück, während die Postfix-Form den alten Wert zurück gibt (vor dem Inkrementieren/Dekrementieren).
-Um den Unterschied zu sehen, hier ein Beispiel:
+Um den Unterschied zu erkennen, hier ein Beispiel:
```js run
let counter = 1;
let a = ++counter; // (*)
-alert(a); // *!*2*/!*
+alert(a); // 2
```
-In der Zeile `(*)` erhöht die *Präfix*-Form von `++counter` den Wert von `counter` und gibt den neuen Wert `2` zurück. Der `alert` zeigt also `2`.
+In der Zeile `(*)` erhöht die *Präfix*-Form von `++counter` den Wert von `counter` und gibt den neuen Wert `2` zurück. Der `alert` gibt also `2` zurück.
Verwenden wir nun die Postfix-Form:
@@ -354,10 +413,10 @@ Verwenden wir nun die Postfix-Form:
let counter = 1;
let a = counter++; // (*) ++counter zu counter++ geändert
-alert(a); // *!*1*/!*
+alert(a); // 1
```
-In der Zeile `(*)` erhöht die *Postfix*-Form `counter++` ebenfalls `counter`, gibt aber den *alten* Wert (vor dem Inkrementieren) zurück. Der `alert` zeigt also `1`.
+In der Zeile `(*)` erhöht die *Postfix*-Form `counter++` ebenfalls den Wert von `counter`, gibt aber den *alten* Wert (vor dem Inkrementieren) zurück. Der `alert` gibt also `1` zurück.
Zusammenfassend:
@@ -369,13 +428,13 @@ Zusammenfassend:
++counter;
alert( counter ); // 2, die obigen Zeilen haben dasselbe getan
```
-- Wenn wir einen Wert erhöhen *und* sofort das Ergebnis des Operators verwenden möchten, benötigen wir die Präfix-Formu:
+- Wenn wir einen Wert erhöhen *und* sofort das Ergebnis des Operators verwenden möchten, benötigen wir die Präfix-Form:
```js run
let counter = 0;
alert( ++counter ); // 1
```
-- Wenn wir einen Wert erhöhen, aber seinen vorherigen Wert verwenden möchten, benötigen wir die Postfix-Form:
+- Wenn wir einen Wert erhöhen, aber seinen vorherigen Wert noch verwenden möchten, benötigen wir die Postfix-Form:
```js run
let counter = 0;
@@ -399,7 +458,7 @@ let counter = 1;
alert( 2 * counter++ ); // 2, weil counter ++ den "alten" Wert zurückgibt
```
-Obwohl technisch in Ordnung, macht eine solche Notation Code normalerweise weniger lesbar. Eine Zeile macht mehrere Dinge -- nicht gut.
+Obwohl dies technisch in Ordnung ist, macht eine solche Notation Code normalerweise unlesbarer. Eine Zeile in der mehrere Operationen ausgeführt werden -- nicht gut.
Beim Lesen von Code kann ein schneller "vertikaler" Augenscan leicht etwas wie `counter++` übersehen, und es ist nicht offensichtlich, dass die Variable größer wird.
@@ -428,13 +487,20 @@ Die Liste der Operatoren:
- Rechtsverschiebung ( `>>` )
- Null füllende Rechtsverschiebung ( `>>>` )
-Diese Operatoren werden sehr selten verwendet, wenn wir mit Zahlen auf der untersten (bitweisen) Ebene herumspielen müssen. Wir werden diese Operatoren in absehbarer Zeit nicht brauchen, da die Webentwicklung kaum Gebrauch von ihnen macht, aber in einigen speziellen Bereichen, wie der Kryptographie, sind sie nützlich. Bei bedarf kannst du den Artikel [Bitweise Operatoren](https://developer.mozilla.org/de/docs/Web/JavaScript/Reference/Operators/Bitwise_Operatoren) auf MDN lesen.
+
+Diese Operatoren werden sehr selten verwendet, z.B. wenn wir mit Zahlen auf der untersten (bitweisen) Ebene herumspielen müssen. Wir werden diese Operatoren in absehbarer Zeit nicht brauchen, da in der Webentwicklung kaum Gebrauch von ihnen gemacht wird, aber in einigen speziellen Bereichen, wie der Kryptographie, sind sie nützlich. Bei bedarf kannst du den Artikel [Bitweise Operatoren](https://developer.mozilla.org/de/docs/Web/JavaScript/Reference/Operators/Bitwise_Operatoren) auf MDN lesen.
+
+
+<<<<<<< HEAD
+=======
+These operators are used very rarely, when we need to fiddle with numbers on the very lowest (bitwise) level. We won't need these operators any time soon, as web development has little use of them, but in some special areas, such as cryptography, they are useful. You can read the [Bitwise Operators](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#bitwise_operators) chapter on MDN when a need arises.
+>>>>>>> 291b5c05b99452cf8a0d32bd32426926dbcc0ce0
## Komma
Der Komma-Operator `,` ist einer der seltensten und ungewöhnlichsten Operatoren. Manchmal wird damit kürzerer Code geschrieben, sodass wir ihn kennen müssen, um zu verstehen, was vor sich geht.
-Mit dem Komma-Operator können wir mehrere Ausdrücke auswerten und durch ein Komma `,` trennen. Jeder von ihnen wird ausgewertet, aber nur das Ergebnis des letzten wird zurückgegeben.
+Mit dem Komma-Operator können wir mehrere Ausdrücke auswerten und sie durch ein Komma `,` trennen. Jeder von ihnen wird ausgewertet, aber nur das Ergebnis des letzten Ausdrucks wird zurückgegeben.
Zum Beispiel:
@@ -449,14 +515,14 @@ alert( a ); // 7 (das Ergebnis von 3 + 4)
Hier wird der erste Ausdruck `1 + 2` ausgewertet und sein Ergebnis verworfen. Dann wird `3 + 4` ausgewertet und als Ergebnis zurückgegeben.
```smart header="Komma hat eine sehr geringe Priorität"
-Bitte beachten Sie, dass der Komma-Operator eine sehr niedrige Priorität hat, niedriger als `=`, daher sind Klammern im obigen Beispiel wichtig.
+Bitte beachte, dass der Komma-Operator eine sehr niedrige Priorität hat, niedriger als `=`, daher sind Klammern im obigen Beispiel wichtig.
-Ohne sie: `a = 1 + 2, 3 + 4` wertet zuerst `+` aus, summiert die Zahlen zu `a = 3, 7`, dann weist der Zuweisungsoperator `=` `a = 3` zu, und der Rest ist ignoriert. Es ist wie `(a = 1 + 2), 3 + 4`.
+Ohne Klammern: `a = 1 + 2, 3 + 4` wertet zuerst `+` aus, summiert die Zahlen zu `a = 3, 7`, dann weist der Zuweisungsoperator `=` `a = 3` zu, der Rest wird ignoriert. Es ist wie `(a = 1 + 2), 3 + 4`.
```
-Warum brauchen wir einen Operator, der alles außer dem letzten Ausdruck wegwirft?
+Wozu brauchen wir einen Operator, der alles außer dem letzten Ausdruck verwirft?
-Manchmal wird es in komplexeren Konstrukten verwendet, um mehrere Aktionen in eine Zeile zu setzen.
+Manchmal wird der Operator in komplexeren Konstrukten verwendet, um mehrere Aktionen in eine Zeile zu setzen.
Zum Beispiel:
@@ -467,4 +533,4 @@ for (*!*a = 1, b = 3, c = a * b*/!*; a < 10; a++) {
}
```
-Solche Tricks werden in vielen JavaScript-Frameworks verwendet. Deshalb erwähnen wir sie. In der Regel verbessern sie jedoch nicht die Lesbarkeit des Codes, daher sollten wir vor der Verwendung gut überlegen.
+Solche Tricks werden in vielen JavaScript-Frameworks verwendet. Deshalb erwähnen wir sie. In der Regel verbessern sie jedoch nicht die Lesbarkeit des Codes, daher sollten wir uns ihre Verwendung gut überlegen.
diff --git a/1-js/02-first-steps/09-comparison/1-comparison-questions/solution.md b/1-js/02-first-steps/09-comparison/1-comparison-questions/solution.md
index a86a9f73e..9fd324071 100644
--- a/1-js/02-first-steps/09-comparison/1-comparison-questions/solution.md
+++ b/1-js/02-first-steps/09-comparison/1-comparison-questions/solution.md
@@ -10,12 +10,12 @@ null == "\n0\n" → false
null === +"\n0\n" → false
```
-Some of the reasons:
+Begründungen:
-1. Obviously, true.
-2. Dictionary comparison, hence false. `"a"` is smaller than `"p"`.
-3. Again, dictionary comparison, first char of `"2"` is greater than the first char of `"1"`.
-4. Values `null` and `undefined` equal each other only.
-5. Strict equality is strict. Different types from both sides lead to false.
-6. Similar to `(4)`, `null` only equals `undefined`.
-7. Strict equality of different types.
+1. Offensichtlich true.
+2. Wörterbuchvergleich, daher false. `"a"` ist kleiner als `"p"`.
+3. Wieder, Wörterbuchvergleich, erstes Zeichen `"2"` ist größer als das erste Zeichen `"1"`.
+4. Die Werte `null` und `undefined` sind nur gegenseitig gleich.
+5. Strikte Gleichheit ist streng. Verschiedene Typen auf beiden Seiten führen zu `false`.
+6. Ähnlich wie `(4)`, `null` ist nur gleich `undefined`.
+7. Strikte Gleichheit von verschiedenen Datentypen.
diff --git a/1-js/02-first-steps/09-comparison/1-comparison-questions/task.md b/1-js/02-first-steps/09-comparison/1-comparison-questions/task.md
index be7f75ddd..4777faa75 100644
--- a/1-js/02-first-steps/09-comparison/1-comparison-questions/task.md
+++ b/1-js/02-first-steps/09-comparison/1-comparison-questions/task.md
@@ -2,9 +2,9 @@ importance: 5
---
-# Comparisons
+# Vergleiche
-What will be the result for these expressions?
+Was wird das Ergebnis dieser Ausdrücke sein?
```js no-beautify
5 > 4
diff --git a/1-js/02-first-steps/09-comparison/article.md b/1-js/02-first-steps/09-comparison/article.md
index a323dc93d..a271a5885 100644
--- a/1-js/02-first-steps/09-comparison/article.md
+++ b/1-js/02-first-steps/09-comparison/article.md
@@ -1,47 +1,41 @@
-# Comparisons
+# Vergleiche
-We know many comparison operators from maths.
+Wir kennen viele Vergleichsoperatoren aus der Mathematik:
-In JavaScript they are written like this:
+- Größer/kleiner als: a > b, a < b.
+- Größer/kleiner als oder gleich: a >= b, a <= b.
+- Gleich: `a == b` (beachte bitte das doppelte Gleichheitszeichen `=`. Ein einfaches Gleichheitszeiten `a = b` würde eine Zuweisung bedeuten).
+- Ungleich. In der Mathematik lautet die Notation ≠, aber in JavaScript wird es als eine Zuweisung mit einem Ausrufezeichen davor geschrieben: a != b.
-- Greater/less than: a > b, a < b.
-- Greater/less than or equals: a >= b, a <= b.
-- Equals: `a == b`, please note the double equality sign `==` means the equality test, while a single one `a = b` means an assignment.
-- Not equals. In maths the notation is ≠, but in JavaScript it's written as a != b.
+## Das Ergebnis ist Boolean
-In this article we'll learn more about different types of comparisons, how JavaScript makes them, including important peculiarities.
+Wie alle anderen Operatoren liefert ein Vergleich einen Wert. In diesem Fall ist der Wert ein boolscher Wert.
-At the end you'll find a good recipe to avoid "javascript quirks"-related issues.
+- `true` -- bedeutet "ja", "richtig" oder "die Wahrheit".
+- `false` -- bedeutet "nein", "falsch" oder "nicht die Wahrheit".
-## Boolean is the result
-
-All comparison operators return a boolean value:
-
-- `true` -- means "yes", "correct" or "the truth".
-- `false` -- means "no", "wrong" or "not the truth".
-
-For example:
+Zum Beispiel:
```js run
-alert( 2 > 1 ); // true (correct)
-alert( 2 == 1 ); // false (wrong)
-alert( 2 != 1 ); // true (correct)
+alert( 2 > 1 ); // true (richtig)
+alert( 2 == 1 ); // false (falsch)
+alert( 2 != 1 ); // true (richtig)
```
-A comparison result can be assigned to a variable, just like any value:
+Ein Vergleichsergebnis kann einer Variablen zugewiesen werden, genau wie jeder andere Wert:
```js run
-let result = 5 > 4; // assign the result of the comparison
-alert( result ); // true
+let result = 5 > 4; // das Ergebnis des Vergleichs zuweisen
+alert( result ); // true
```
-## String comparison
+## String Vergleiche
-To see whether a string is greater than another, JavaScript uses the so-called "dictionary" or "lexicographical" order.
+Um zu sehen, ob ein String größer als ein anderer ist, verwendet JavaScript die so genannte "Wörterbuch-" oder "lexikografische" Reihenfolge.
-In other words, strings are compared letter-by-letter.
+Mit anderen Worten, Strings werden Buchstabe für Buchstabe verglichen.
-For example:
+Zum Beispiel:
```js run
alert( 'Z' > 'A' ); // true
@@ -49,53 +43,53 @@ alert( 'Glow' > 'Glee' ); // true
alert( 'Bee' > 'Be' ); // true
```
-The algorithm to compare two strings is simple:
+Der Algorithmus zum Vergleichen zweier Strings ist einfach:
-1. Compare the first character of both strings.
-2. If the first character from the first string is greater (or less) than the other string's, then the first string is greater (or less) than the second. We're done.
-3. Otherwise, if both strings' first characters are the same, compare the second characters the same way.
-4. Repeat until the end of either string.
-5. If both strings end at the same length, then they are equal. Otherwise, the longer string is greater.
+1. Vergleiche das erste Zeichen der beiden Strings.
+2. Wenn das erste Zeichen aus dem ersten String größer (oder kleiner) als das des anderen Strings ist, dann ist der erste String größer (oder kleiner) als der zweite. Das war's.
+3. Andernfalls, wenn die ersten Zeichen beider Strings gleich sind, vergleiche die zweiten Zeichen auf die gleiche Weise.
+4. Wiederhole dies bis zum Ende einer der beiden Strings.
+5. Wenn beide Strings mit der gleichen Länge enden, dann sind sie gleich. Andernfalls ist der längere String größer.
-In the examples above, the comparison `'Z' > 'A'` gets to a result at the first step while the strings `"Glow"` and `"Glee"` are compared character-by-character:
+In den obigen Beispielen führt der Vergleich `'Z' > 'A'` im ersten Schritt zu einem Ergebnis, während die Zeichenketten `"Glow"` und `"Glee"` zeichenweise verglichen werden:
-1. `G` is the same as `G`.
-2. `l` is the same as `l`.
-3. `o` is greater than `e`. Stop here. The first string is greater.
+1. `G` ist gleich wie `G`.
+2. `l` ist gleich wie `l`.
+3. `o` ist größer als `e`. Halte hier an. Der erste String ist größer.
-```smart header="Not a real dictionary, but Unicode order"
-The comparison algorithm given above is roughly equivalent to the one used in dictionaries or phone books, but it's not exactly the same.
+```smart header="Kein echtes Wörterbuch, aber Unicode-Reihenfolge"
+Der oben angegebene Vergleichsalgorithmus entspricht in etwa dem, der in Wörterbüchern oder Telefonbüchern verwendet wird, aber er ist nicht genau derselbe.
-For instance, case matters. A capital letter `"A"` is not equal to the lowercase `"a"`. Which one is greater? The lowercase `"a"`. Why? Because the lowercase character has a greater index in the internal encoding table JavaScript uses (Unicode). We'll get back to specific details and consequences of this in the chapter 253 or be less than -253. As bigints are used in few special areas, we devote them a special chapter (253-1) or be less than -(253-1), as we mentioned earlier in the chapter func`string`. The function `func` is called automatically, receives the string and embedded expressions and can process them. This is called "tagged templates". This feature makes it easier to implement custom templating, but is rarely used in practice. You can read more about it in the [manual](mdn:/JavaScript/Reference/Template_literals#Tagged_templates).
+Backticks erlauben es uns auch, eine "Template-Funktion" vor dem ersten Backtick anzugeben. Die Syntax lautet: func`string`. Die Funktion `func` wird automatisch aufgerufen, erhält die Zeichenkette `string` und eingebettete Ausdrücke und kann sie verarbeiten. Diese Funktion wird "tagged templates" genannt, sie ist selten zu sehen, aber du kannst darüber auf MDN lesen unter: [Template literals](mdn:/JavaScript/Reference/Template_literals#Tagged_templates).
-## Special characters
+## Spezielle Zeichen
-It is still possible to create multiline strings with single and double quotes by using a so-called "newline character", written as `\n`, which denotes a line break:
+Es ist immer noch möglich, mehrzeilige Zeichenketten mit einfachen und doppelten Anführungszeichen zu erstellen, indem man ein sogenanntes "neue Zeile-Zeichen", dargestellt als `\n`, verwendet, das einen Zeilenumbruch darstellt:
```js run
-let guestList = "Guests:\n * John\n * Pete\n * Mary";
+let guestList = "Gäste:\n * John\n * Pete\n * Mary";
-alert(guestList); // a multiline list of guests
+alert(guestList); // eine mehrzeilige Gästeliste, wie oben
```
-For example, these two lines are equal, just written differently:
+Als einfacheres Beispiel sind diese beiden Zeilen gleich, nur unterschiedlich geschrieben:
```js run
-let str1 = "Hello\nWorld"; // two lines using a "newline symbol"
+let str1 = "Hallo\nWelt"; // zwei zeilen mit einem "neue Zeile-Symbol"
-// two lines using a normal newline and backticks
-let str2 = `Hello
-World`;
+// zwei zeilen mit einer normalen neuen Zeile und Backticks
+let str2 = `Hallo
+Welt`;
alert(str1 == str2); // true
```
-There are other, less common "special" characters.
+Es gibt andere, weniger gebräuchliche spezielle Zeichen:
-Here's the full list:
-
-| Character | Description |
+| Zeichen | Beschreibung |
|-----------|-------------|
-|`\n`|New line|
-|`\r`|Carriage return: not used alone. Windows text files use a combination of two characters `\r\n` to represent a line break. |
-|`\'`, `\"`|Quotes|
+|`\n`|Neue Zeile|
+|`\r`|In Windows-Textdateien wird ein Zeilenumbruch durch eine Kombination von zwei Zeichen `\r\n` dargestellt, während es in Nicht-Windows-Betriebssystemen nur `\n` ist. Das ist historisch bedingt, die meisten Windows-Programme verstehen auch `\n`. |
+|`\'`, `\"`, \\`|Anführungszeichen|
|`\\`|Backslash|
-|`\t`|Tab|
-|`\b`, `\f`, `\v`| Backspace, Form Feed, Vertical Tab -- kept for compatibility, not used nowadays. |
-|`\xXX`|Unicode character with the given hexadecimal unicode `XX`, e.g. `'\x7A'` is the same as `'z'`.|
-|`\uXXXX`|A unicode symbol with the hex code `XXXX` in UTF-16 encoding, for instance `\u00A9` -- is a unicode for the copyright symbol `©`. It must be exactly 4 hex digits. |
-|`\u{X…XXXXXX}` (1 to 6 hex characters)|A unicode symbol with the given UTF-32 encoding. Some rare characters are encoded with two unicode symbols, taking 4 bytes. This way we can insert long codes. |
-
-Examples with unicode:
+|`\t`|Tabulator|
+|`\b`, `\f`, `\v`| Backspace, Formularvorschub, Vertikaler Tabulator -- nur der Vollständigkeit halber erwähnt, stammen aus alter Zeit, werden heutzutage nicht genutzt (du kannst sie direkt vergessen). |
-```js run
-alert( "\u00A9" ); // ©
-alert( "\u{20331}" ); // 佫, a rare Chinese hieroglyph (long unicode)
-alert( "\u{1F60D}" ); // 😍, a smiling face symbol (another long unicode)
-```
+Wie du siehst, beginnen alle speziellen Zeichen mit dem Backslash-Zeichen `\`. Es wird auch als "Maskierungszeichen" ("escape character") bezeichnet.
-All special characters start with a backslash character `\`. It is also called an "escape character".
-
-We might also use it if we wanted to insert a quote into the string.
-
-For instance:
+Weil es so besonders ist, wenn wir einen tatsächlichen Backslash `\` innerhalb der Zeichenkette zeigen müssen, müssen wir ihn verdoppeln:
```js run
-alert( 'I*!*\'*/!*m the Walrus!' ); // *!*I'm*/!* the Walrus!
+alert( `Der Backslash: \\` ); // Der Backslash: \
```
-As you can see, we have to prepend the inner quote by the backslash `\'`, because otherwise it would indicate the string end.
+Sogenannte maskierte Anführungszeichen `\'`, `\"`, \\` werden verwendet, um ein Anführungszeichen in eine Zeichenkette mit den gleichen Anführungszeichen einzufügen.
-Of course, only to the quotes that are the same as the enclosing ones need to be escaped. So, as a more elegant solution, we could switch to double quotes or backticks instead:
+Zum Beispiel:
```js run
-alert( `I'm the Walrus!` ); // I'm the Walrus!
+alert( 'Ich*!*\'*/!* bin das Walross!' ); // *!*Ich bin*/!* das Walross!
```
-Note that the backslash `\` serves for the correct reading of the string by JavaScript, then disappears. The in-memory string has no `\`. You can clearly see that in `alert` from the examples above.
-
-But what if we need to show an actual backslash `\` within the string?
+Wie du sehen kannst, müssen wir dem inneren Anführungszeichen ein Backslash `\'` voranstellen, da es sonst das Ende der Zeichenkette anzeigen würde.
-That's possible, but we need to double it like `\\`:
+Natürlich müssen nur jene Anführungszeichen maskiert werden, die gleich wie die umgebenden sind. Also könnten wir als elegantere Lösung stattdessen auf doppelte Anführungszeichen oder Backticks wechseln:
```js run
-alert( `The backslash: \\` ); // The backslash: \
+alert( "Ich bin das Walross!" ); // Ich bin das Walross!
```
-## String length
+Neben diesen speziellen Zeichen gibt es auch eine spezielle Notation für Unicode-Codes `\u…`, sie wird selten verwendet und ist im optionalem Kapitel über [Unicode](info:unicode) behandelt.
-The `length` property has the string length:
+## Zeichenkettenlänge
+
+Die Eigenschaft `length` gibt die Länge der Zeichenkette an:
```js run
-alert( `My\n`.length ); // 3
+alert( `Mein\n`.length ); // 3
```
-Note that `\n` is a single "special" character, so the length is indeed `3`.
+Beachte, dass `\n` ein einzelnes "spezielles" Zeichen ist und die Länge tatsächlich `3` ist.
-```warn header="`length` is a property"
-People with a background in some other languages sometimes mistype by calling `str.length()` instead of just `str.length`. That doesn't work.
+```warn header="`length` ist eine Eigenschaft"
+Personen mit Erfahrung in einigen anderen Sprachen vertippen sich manchmal, indem sie `str.length()` anstelle von einfach `str.length` aufrufen. Das funktioniert nicht.
-Please note that `str.length` is a numeric property, not a function. There is no need to add parenthesis after it.
+Bitte beachte, dass `str.length` eine numerische Eigenschaft ist, keine Funktion. Es ist nicht notwendig, Klammern dahinter zu setzen. Nicht `.length()`, sondern `.length`.
```
-## Accessing characters
+## Auf Zeichen zugreifen
-To get a character at position `pos`, use square brackets `[pos]` or call the method [str.charAt(pos)](mdn:js/String/charAt). The first character starts from the zero position:
+Um ein Zeichen an der Position `pos` zu erhalten, verwende eckige Klammern `[pos]` oder rufe die Methode [str.at(pos)](mdn:js/String/at) auf. Das erste Zeichen beginnt bei der Position Null:
```js run
-let str = `Hello`;
+let str = `Hallo`;
-// the first character
+// das erste Zeichen
alert( str[0] ); // H
-alert( str.charAt(0) ); // H
+alert( str.at(0) ); // H
-// the last character
+// das letzte Zeichen
alert( str[str.length - 1] ); // o
+alert( str.at(-1) ); // o
```
-The square brackets are a modern way of getting a character, while `charAt` exists mostly for historical reasons.
+Wie du sehen kannst, hat die Methode `.at(pos)` den Vorteil, dass sie negative Positionen zulässt. Wenn `pos` negativ ist, wird es vom Ende der Zeichenkette gezählt.
+
+Also bedeutet `.at(-1)` das letzte Zeichen und `.at(-2)` das davor usw.
-The only difference between them is that if no character is found, `[]` returns `undefined`, and `charAt` returns an empty string:
+Die eckigen Klammern geben `undefined` für negative Indizes zurück, zum Beispiel:
```js run
-let str = `Hello`;
+let str = `Hallo`;
-alert( str[1000] ); // undefined
-alert( str.charAt(1000) ); // '' (an empty string)
+alert( str[-2] ); // undefined
+alert( str.at(-2) ); // l
```
-We can also iterate over characters using `for..of`:
+Wir können auch mit `for..of` über Zeichen iterieren:
```js run
-for (let char of "Hello") {
- alert(char); // H,e,l,l,o (char becomes "H", then "e", then "l" etc)
+for (let char of "Hallo") {
+ alert(char); // H,e,l,l,o (char wird "H", dann "e", dann "l" usw)
}
```
-## Strings are immutable
+## Zeichenketten sind unveränderlich
-Strings can't be changed in JavaScript. It is impossible to change a character.
+Zeichenketten können in JavaScript nicht verändert werden. Es ist unmöglich, ein Zeichen zu ändern.
-Let's try it to show that it doesn't work:
+Versuchen wir es, um zu zeigen, dass es nicht funktioniert:
```js run
let str = 'Hi';
-str[0] = 'h'; // error
-alert( str[0] ); // doesn't work
+str[0] = 'h'; // Fehler
+alert( str[0] ); // funktioniert nicht
```
-The usual workaround is to create a whole new string and assign it to `str` instead of the old one.
+Die übliche Vorgehensweise besteht darin, eine ganz neue Zeichenkette zu erstellen und sie anstelle der alten `str` zuzuweisen.
-For instance:
+Zum Beispiel:
```js run
let str = 'Hi';
-str = 'h' + str[1]; // replace the string
+str = 'h' + str[1]; // ersetze die Zeichenkette
alert( str ); // hi
```
-In the following sections we'll see more examples of this.
+In den folgenden Abschnitten werden wir weitere Beispiele dafür sehen.
-## Changing the case
+## Die Groß-/Kleinschreibung ändern
-Methods [toLowerCase()](mdn:js/String/toLowerCase) and [toUpperCase()](mdn:js/String/toUpperCase) change the case:
+Die Methoden [toLowerCase()](mdn:js/String/toLowerCase) und [toUpperCase()](mdn:js/String/toUpperCase) ändern die Groß-/Kleinschreibung:
```js run
-alert( 'Interface'.toUpperCase() ); // INTERFACE
-alert( 'Interface'.toLowerCase() ); // interface
+alert( 'Schnittstelle'.toUpperCase() ); // SCHNITTSTELLE
+alert( 'Schnittstelle'.toLowerCase() ); // schnittstelle
```
-Or, if we want a single character lowercased:
+Oder wenn wir nur einen einzelnen Buchstaben kleingeschrieben haben wollen:
-```js
-alert( 'Interface'[0].toLowerCase() ); // 'i'
+```js run
+alert( 'Schnittstelle'[0].toLowerCase() ); // 's'
```
-## Searching for a substring
+## Nach einer Teilzeichenkette suchen
-There are multiple ways to look for a substring within a string.
+Es gibt mehrere Möglichkeiten, innerhalb einer Zeichenkette nach einer Teilzeichenkette zu suchen.
### str.indexOf
-The first method is [str.indexOf(substr, pos)](mdn:js/String/indexOf).
+Die erste Methode ist [str.indexOf(substr, pos)](mdn:js/String/indexOf).
-It looks for the `substr` in `str`, starting from the given position `pos`, and returns the position where the match was found or `-1` if nothing can be found.
+Sie sucht `substr` in `str`, beginnend bei der gegebenen Position `pos`, und gibt die Position zurück, an der die Übereinstimmung gefunden wurde oder `-1`, wenn nichts gefunden werden kann.
-For instance:
+Zum Beispiel:
```js run
-let str = 'Widget with id';
+let str = 'Widget mit id';
-alert( str.indexOf('Widget') ); // 0, because 'Widget' is found at the beginning
-alert( str.indexOf('widget') ); // -1, not found, the search is case-sensitive
+alert( str.indexOf('Widget') ); // 0, weil 'Widget' am Anfang gefunden wird
+alert( str.indexOf('widget') ); // -1, nicht gefunden, die Suche ist groß-/kleinschreibungsempfindlich
-alert( str.indexOf("id") ); // 1, "id" is found at the position 1 (..idget with id)
+alert( str.indexOf("id") ); // 1, "id" wird an der Position 1 gefunden (..idget mit id)
```
-The optional second parameter allows us to search starting from the given position.
+Der optionale zweite Parameter ermöglicht es uns, die Suche ab einer bestimmten Position zu starten.
-For instance, the first occurrence of `"id"` is at position `1`. To look for the next occurrence, let's start the search from position `2`:
+Zum Beispiel ist das erste Vorkommen von `"id"` an Position `1`. Um nach dem nächsten Vorkommen zu suchen, starten wir die Suche ab Position `2`:
```js run
-let str = 'Widget with id';
+let str = 'Widget mit id';
-alert( str.indexOf('id', 2) ) // 12
+alert( str.indexOf('id', 2) ) // 11
```
-If we're interested in all occurrences, we can run `indexOf` in a loop. Every new call is made with the position after the previous match:
+Wenn wir an allen Vorkommen interessiert sind, können wir `indexOf` in einer Schleife ausführen. Jeder neue Aufruf erfolgt mit der Position nach dem vorherigen Treffer:
```js run
-let str = 'As sly as a fox, as strong as an ox';
+let str = 'So listig wie ein Fuchs, so stark wie ein Ochse';
-let target = 'as'; // let's look for it
+let target = 'so'; // danach wollen wir suchen
let pos = 0;
while (true) {
let foundPos = str.indexOf(target, pos);
if (foundPos == -1) break;
- alert( `Found at ${foundPos}` );
- pos = foundPos + 1; // continue the search from the next position
+ alert( `Gefunden bei ${foundPos}` );
+ pos = foundPos + 1; // setze die Suche ab der nächsten Position fort
}
```
-The same algorithm can be layed out shorter:
+Der gleiche Algorithmus kann kürzer dargestellt werden:
```js run
-let str = "As sly as a fox, as strong as an ox";
-let target = "as";
+let str = "So listig wie ein Fuchs, so stark wie ein Ochse";
+let target = "so";
*!*
let pos = -1;
@@ -281,232 +269,192 @@ while ((pos = str.indexOf(target, pos + 1)) != -1) {
```
```smart header="`str.lastIndexOf(substr, position)`"
-There is also a similar method [str.lastIndexOf(substr, position)](mdn:js/String/lastIndexOf) that searches from the end of a string to its beginning.
+Es gibt auch eine ähnliche Methode [str.lastIndexOf(substr, position)](mdn:js/String/lastIndexOf), die vom Ende eines Strings zum Anfang durchsucht.
-It would list the occurrences in the reverse order.
+Sie würde die Vorkommen in umgekehrter Reihenfolge auflisten.
```
-There is a slight inconvenience with `indexOf` in the `if` test. We can't put it in the `if` like this:
+Ein kleines Problem bei `indexOf` ist die Verwendung im `if`. Wir können es nicht wie folgt in die `if`-Bedingung setzen:
```js run
-let str = "Widget with id";
+let str = "Widget mit id";
if (str.indexOf("Widget")) {
- alert("We found it"); // doesn't work!
+ alert("Wir haben es gefunden"); // funktioniert nicht!
}
```
-The `alert` in the example above doesn't show because `str.indexOf("Widget")` returns `0` (meaning that it found the match at the starting position). Right, but `if` considers `0` to be `false`.
+Das `alert` im Beispiel oben erscheint nicht, weil `str.indexOf("Widget")` `0` zurückgibt (das bedeutet, dass es die Übereinstimmung am Anfang gefunden hat). Richtig, aber `if` betrachtet `0` als `false`.
-So, we should actually check for `-1`, like this:
+Wir sollten also tatsächlich nach `-1` überprüfen, so wie hier:
```js run
-let str = "Widget with id";
+let str = "Widget mit id";
*!*
if (str.indexOf("Widget") != -1) {
*/!*
- alert("We found it"); // works now!
+ alert("Wir haben es gefunden"); // jetzt funktioniert es!
}
```
-#### The bitwise NOT trick
-
-One of the old tricks used here is the [bitwise NOT](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators#Bitwise_NOT) `~` operator. It converts the number to a 32-bit integer (removes the decimal part if exists) and then reverses all bits in its binary representation.
-
-In practice, that means a simple thing: for 32-bit integers `~n` equals `-(n+1)`.
-
-For instance:
-
-```js run
-alert( ~2 ); // -3, the same as -(2+1)
-alert( ~1 ); // -2, the same as -(1+1)
-alert( ~0 ); // -1, the same as -(0+1)
-*!*
-alert( ~-1 ); // 0, the same as -(-1+1)
-*/!*
-```
-
-As we can see, `~n` is zero only if `n == -1` (that's for any 32-bit signed integer `n`).
-
-So, the test `if ( ~str.indexOf("...") )` is truthy only if the result of `indexOf` is not `-1`. In other words, when there is a match.
-
-People use it to shorten `indexOf` checks:
-
-```js run
-let str = "Widget";
-
-if (~str.indexOf("Widget")) {
- alert( 'Found it!' ); // works
-}
-```
-
-It is usually not recommended to use language features in a non-obvious way, but this particular trick is widely used in old code, so we should understand it.
-
-Just remember: `if (~str.indexOf(...))` reads as "if found".
-
-To be precise though, as big numbers are truncated to 32 bits by `~` operator, there exist other numbers that give `0`, the smallest is `~4294967295=0`. That makes such check is correct only if a string is not that long.
-
-Right now we can see this trick only in the old code, as modern JavaScript provides `.includes` method (see below).
-
### includes, startsWith, endsWith
-The more modern method [str.includes(substr, pos)](mdn:js/String/includes) returns `true/false` depending on whether `str` contains `substr` within.
+Die modernere Methode [str.includes(substr, pos)](mdn:js/String/includes) gibt `true/false` zurück, je nachdem, ob `str` `substr` enthält.
-It's the right choice if we need to test for the match, but don't need its position:
+Das ist die richtige Wahl, wenn wir auf das Vorhandensein testen müssen, aber dessen Position nicht benötigen:
```js run
-alert( "Widget with id".includes("Widget") ); // true
+alert( "Widget mit id".includes("Widget") ); // true
-alert( "Hello".includes("Bye") ); // false
+alert( "Hallo".includes("Tschüss") ); // false
```
-The optional second argument of `str.includes` is the position to start searching from:
+Das optionale zweite Argument von `str.includes` ist die Position, ab der gesucht werden soll:
```js run
alert( "Widget".includes("id") ); // true
-alert( "Widget".includes("id", 3) ); // false, from position 3 there is no "id"
+alert( "Widget".includes("id", 3) ); // false, ab Position 3 gibt es kein "id"
```
-The methods [str.startsWith](mdn:js/String/startsWith) and [str.endsWith](mdn:js/String/endsWith) do exactly what they say:
+Die Methoden [str.startsWith](mdn:js/String/startsWith) und [str.endsWith](mdn:js/String/endsWith) tun genau das, was sie ausdrücken:
```js run
-alert( "Widget".startsWith("Wid") ); // true, "Widget" starts with "Wid"
-alert( "Widget".endsWith("get") ); // true, "Widget" ends with "get"
+alert( "*!*Wid*/!*get".startsWith("Wid") ); // true, "Widget" beginnt mit "Wid"
+alert( "Wid*!*get*/!*".endsWith("get") ); // true, "Widget" endet mit "get"
```
-## Getting a substring
+## Einen Teilstring erhalten
-There are 3 methods in JavaScript to get a substring: `substring`, `substr` and `slice`.
+Es gibt in JavaScript drei Methoden, um einen Teilstring zu erhalten: `substring`, `substr` und `slice`.
`str.slice(start [, end])`
-: Returns the part of the string from `start` to (but not including) `end`.
+: Gibt den Teil der Zeichenkette von `start` bis (aber nicht einschließlich) `end` zurück.
- For instance:
+ Zum Beispiel:
```js run
let str = "stringify";
- alert( str.slice(0, 5) ); // 'strin', the substring from 0 to 5 (not including 5)
- alert( str.slice(0, 1) ); // 's', from 0 to 1, but not including 1, so only character at 0
+ alert( str.slice(0, 5) ); // 'strin', der Teilstring von 0 bis 5 (5 nicht eingeschlossen)
+ alert( str.slice(0, 1) ); // 's', von 0 bis 1, aber nicht inklusive 1, also nur das Zeichen bei 0
```
- If there is no second argument, then `slice` goes till the end of the string:
+ Wenn es keinen zweiten Argument gibt, dann geht `slice` bis zum Ende der Zeichenkette:
```js run
let str = "st*!*ringify*/!*";
- alert( str.slice(2) ); // 'ringify', from the 2nd position till the end
+ alert( str.slice(2) ); // 'ringify', von der 2. Position bis zum Ende
```
- Negative values for `start/end` are also possible. They mean the position is counted from the string end:
+ Negative Werte für `start/end` sind ebenfalls möglich. Sie bedeuten, dass die Position vom Ende des Strings gezählt wird:
```js run
let str = "strin*!*gif*/!*y";
- // start at the 4th position from the right, end at the 1st from the right
+ // beginne bei der 4. Position von rechts, endet bei der 1. von rechts
alert( str.slice(-4, -1) ); // 'gif'
```
`str.substring(start [, end])`
-: Returns the part of the string *between* `start` and `end`.
+: Gibt den Teil der Zeichenkette *zwischen* `start` und `end` zurück (end nicht eingeschlossen).
- This is almost the same as `slice`, but it allows `start` to be greater than `end`.
+ Dies ist fast das Gleiche wie `slice`, aber es erlaubt `start`, größer als `end` zu sein (in diesem Fall werden einfach die `start`- und `end`-Werte getauscht).
- For instance:
+ Zum Beispiel:
```js run
let str = "st*!*ring*/!*ify";
- // these are same for substring
+ // diese sind gleich für substring
alert( str.substring(2, 6) ); // "ring"
alert( str.substring(6, 2) ); // "ring"
- // ...but not for slice:
- alert( str.slice(2, 6) ); // "ring" (the same)
- alert( str.slice(6, 2) ); // "" (an empty string)
+ // ...aber nicht für slice:
+ alert( str.slice(2, 6) ); // "ring" (das gleiche)
+ alert( str.slice(6, 2) ); // "" (ein leerer String)
```
- Negative arguments are (unlike slice) not supported, they are treated as `0`.
+ Negative Argumente werden (im Gegensatz zu slice) nicht unterstützt und als `0` behandelt.
`str.substr(start [, length])`
-: Returns the part of the string from `start`, with the given `length`.
+: Gibt den Teil der Zeichenkette von `start` bis zur gegebenen Länge `length` zurück.
- In contrast with the previous methods, this one allows us to specify the `length` instead of the ending position:
+ Im Gegensatz zu den vorherigen Methoden erlaubt diese, die `length` anstelle der Endposition anzugeben:
```js run
let str = "st*!*ring*/!*ify";
- alert( str.substr(2, 4) ); // 'ring', from the 2nd position get 4 characters
+ alert( str.substr(2, 4) ); // 'ring', ab der 2. Position 4 Zeichen bekommen
```
- The first argument may be negative, to count from the end:
+ Das erste Argument kann negativ sein, um vom Ende zu zählen:
```js run
let str = "strin*!*gi*/!*fy";
- alert( str.substr(-4, 2) ); // 'gi', from the 4th position get 2 characters
+ alert( str.substr(-4, 2) ); // 'gi', ab der 4. Position 2 Zeichen bekommen
```
-Let's recap these methods to avoid any confusion:
+ Diese Methode ist im [Annex B](https://tc39.es/ecma262/#sec-string.prototype.substr) der Sprachspezifikation enthalten. Das bedeutet, dass sie nur von in Browsern gehosteten Javascript-Engines unterstützt werden sollte, und es wird nicht empfohlen, sie zu verwenden. In der Praxis wird sie jedoch überall unterstützt.
+
+Lass uns diese Methoden rekapitulieren, um jegliche Verwirrung zu vermeiden:
-| method | selects... | negatives |
+| Methode | selektiert... | negatives |
|--------|-----------|-----------|
-| `slice(start, end)` | from `start` to `end` (not including `end`) | allows negatives |
-| `substring(start, end)` | between `start` and `end` | negative values mean `0` |
-| `substr(start, length)` | from `start` get `length` characters | allows negative `start` |
+| `slice(start, end)` | von `start` bis `end` (ohne `end` einzuschließen) | erlaubt negative Werte |
+| `substring(start, end)` | zwischen `start` und `end` (ohne `end` einzuschließen) | negative Werte bedeuten `0` |
+| `substr(start, length)` | von `start` `length` Zeichen holen | erlaubt negatives `start` |
+
+```smart header="Welche soll man wählen?"
+Alle können die Aufgabe erfüllen. Formal hat `substr` einen kleinen Nachteil: Es wird nicht in der Hauptspezifikation von JavaScript beschrieben, sondern in Anhang B, der Browser-spezifische Funktionen umfasst, die hauptsächlich aus historischen Gründen existieren. Daher könnte es sein, dass Nicht-Browser-Umgebungen sie nicht unterstützen. Aber in der Praxis funktioniert sie überall.
-```smart header="Which one to choose?"
-All of them can do the job. Formally, `substr` has a minor drawback: it is described not in the core JavaScript specification, but in Annex B, which covers browser-only features that exist mainly for historical reasons. So, non-browser environments may fail to support it. But in practice it works everywhere.
+Von den anderen beiden Varianten ist `slice` ein bisschen flexibler, es erlaubt negative Argumente und ist kürzer zu schreiben.
-Of the other two variants, `slice` is a little bit more flexible, it allows negative arguments and shorter to write. So, it's enough to remember solely `slice` of these three methods.
+Praktisch gesehen ist es also genug, sich nur `slice` zu merken.
```
-## Comparing strings
+## Strings vergleichen
-As we know from the chapter alert will trigger immediately.
+
+## FinalizationRegistry
+
+Now it is time to talk about finalizers. Before we move on, let's clarify the terminology:
+
+**Cleanup callback (finalizer)** - is a function that is executed, when an object, registered in the `FinalizationRegistry`, is deleted from memory by the garbage collector.
+
+Its purpose - is to provide the ability to perform additional operations, related to the object, after it has been finally deleted from memory.
+
+**Registry** (or `FinalizationRegistry`) - is a special object in JavaScript that manages the registration and unregistration of objects and their cleanup callbacks.
+
+This mechanism allows registering an object to track and associate a cleanup callback with it.
+Essentially it is a structure that stores information about registered objects and their cleanup callbacks, and then automatically invokes those callbacks when the objects are deleted from memory.
+
+To create an instance of the `FinalizationRegistry`, it needs to call its constructor, which takes a single argument - the cleanup callback (finalizer).
+
+Syntax:
+
+```js
+function cleanupCallback(heldValue) {
+ // cleanup callback code
+}
+
+const registry = new FinalizationRegistry(cleanupCallback);
+```
+
+Here:
+
+- `cleanupCallback` - a cleanup callback that will be automatically called when a registered object is deleted from memory.
+- `heldValue` - the value that is passed as an argument to the cleanup callback. If `heldValue` is an object, the registry keeps a strong reference to it.
+- `registry` - an instance of `FinalizationRegistry`.
+
+`FinalizationRegistry` methods:
+
+- `register(target, heldValue [, unregisterToken])` - used to register objects in the registry.
+
+ `target` - the object being registered for tracking. If the `target` is garbage collected, the cleanup callback will be called with `heldValue` as its argument.
+
+ Optional `unregisterToken` – an unregistration token. It can be passed to unregister an object before the garbage collector deletes it. Typically, the `target` object is used as `unregisterToken`, which is the standard practice.
+- `unregister(unregisterToken)` - the `unregister` method is used to unregister an object from the registry. It takes one argument - `unregisterToken` (the unregister token that was obtained when registering the object).
+
+Now let's move on to a simple example. Let's use the already-known `user` object and create an instance of `FinalizationRegistry`:
+
+```js
+let user = { name: "John" };
+
+const registry = new FinalizationRegistry((heldValue) => {
+ console.log(`${heldValue} has been collected by the garbage collector.`);
+});
+```
+
+Then, we will register the object, that requires a cleanup callback by calling the `register` method:
+
+```js
+registry.register(user, user.name);
+```
+
+The registry does not keep a strong reference to the object being registered, as this would defeat its purpose. If the registry kept a strong reference, then the object would never be garbage collected.
+
+If the object is deleted by the garbage collector, our cleanup callback may be called at some point in the future, with the `heldValue` passed to it:
+
+```js
+// When the user object is deleted by the garbage collector, the following message will be printed in the console:
+"John has been collected by the garbage collector."
+```
+
+There are also situations where, even in implementations that use a cleanup callback, there is a chance that it will not be called.
+
+For example:
+- When the program fully terminates its operation (for example, when closing a tab in a browser).
+- When the `FinalizationRegistry` instance itself is no longer reachable to JavaScript code.
+ If the object that creates the `FinalizationRegistry` instance goes out of scope or is deleted, the cleanup callbacks registered in that registry might also not be invoked.
+
+## Caching with FinalizationRegistry
+
+Returning to our *weak* cache example, we can notice the following:
+- Even though the values wrapped in the `WeakRef` have been collected by the garbage collector, there is still an issue of "memory leakage" in the form of the remaining keys, whose values have been collected by the garbage collector.
+
+Here is an improved caching example using `FinalizationRegistry`:
+
+```js
+function fetchImg() {
+ // abstract function for downloading images...
+}
+
+function weakRefCache(fetchImg) {
+ const imgCache = new Map();
+
+ *!*
+ const registry = new FinalizationRegistry((imgName) => { // (1)
+ const cachedImg = imgCache.get(imgName);
+ if (cachedImg && !cachedImg.deref()) imgCache.delete(imgName);
+ });
+ */!*
+
+ return (imgName) => {
+ const cachedImg = imgCache.get(imgName);
+
+ if (cachedImg?.deref()) {
+ return cachedImg?.deref();
+ }
+
+ const newImg = fetchImg(imgName);
+ imgCache.set(imgName, new WeakRef(newImg));
+ *!*
+ registry.register(newImg, imgName); // (2)
+ */!*
+
+ return newImg;
+ };
+}
+
+const getCachedImg = weakRefCache(fetchImg);
+```
+
+1. To manage the cleanup of "dead" cache entries, when the associated `WeakRef` objects are collected by the garbage collector, we create a `FinalizationRegistry` cleanup registry.
+
+ The important point here is, that in the cleanup callback, it should be checked, if the entry was deleted by the garbage collector and not re-added, in order not to delete a "live" entry.
+2. Once the new value (image) is downloaded and put into the cache, we register it in the finalizer registry to track the `WeakRef` object.
+
+This implementation contains only actual or "live" key/value pairs.
+In this case, each `WeakRef` object is registered in the `FinalizationRegistry`.
+And after the objects are cleaned up by the garbage collector, the cleanup callback will delete all `undefined` values.
+
+Here is a visual representation of the updated code:
+
+
+
+A key aspect of the updated implementation is that finalizers allow parallel processes to be created between the "main" program and cleanup callbacks.
+In the context of JavaScript, the "main" program - is our JavaScript-code, that runs and executes in our application or web page.
+
+Hence, from the moment an object is marked for deletion by the garbage collector, and to the actual execution of the cleanup callback, there may be a certain time gap.
+It is important to understand that during this time gap, the main program can make any changes to the object or even bring it back to memory.
+
+That's why, in the cleanup callback, we must check to see if an entry has been added back to the cache by the main program to avoid deleting "live" entries.
+Similarly, when searching for a key in the cache, there is a chance that the value has been deleted by the garbage collector, but the cleanup callback has not been executed yet.
+
+Such situations require special attention if you are working with `FinalizationRegistry`.
+
+## Using WeakRef and FinalizationRegistry in practice
+
+Moving from theory to practice, imagine a real-life scenario, where a user synchronizes their photos on a mobile device with some cloud service
+(such as [iCloud](https://en.wikipedia.org/wiki/ICloud) or [Google Photos](https://en.wikipedia.org/wiki/Google_Photos)),
+and wants to view them from other devices. In addition to the basic functionality of viewing photos, such services offer a lot of additional features, for example:
+
+- Photo editing and video effects.
+- Creating "memories" and albums.
+- Video montage from a series of photos.
+- ...and much more.
+
+Here, as an example, we will use a fairly primitive implementation of such a service.
+The main point - is to show a possible scenario of using `WeakRef` and `FinalizationRegistry` together in real life.
+
+Here is what it looks like:
+
+
+
+weakRefCache function checks whether the required image is in the cache.
+If not, it downloads it from the cloud and puts it in the cache for further use.
+This happens for each selected image:
+Messages:
+ +Logger:
+Logger:
'; +}; + +const downloadCollage = () => { + const date = new Date(); + const fileName = `Collage-${date.getDay()}-${date.getMonth()}-${date.getFullYear()}.png`; + const img = canvasEl.toDataURL("image/png"); + const link = document.createElement("a"); + link.download = fileName; + link.href = img; + link.click(); + link.remove(); +}; + +const changeLayout = ({ target }) => { + state.currentLayout = JSON.parse(target.value); +}; + +// Listeners. +selectEl.addEventListener("change", changeLayout); +createCollageBtn.addEventListener("click", createCollage); +startOverBtn.addEventListener("click", startOver); +downloadBtn.addEventListener("click", downloadCollage); diff --git a/1-js/99-js-misc/07-weakref-finalizationregistry/weakref-finalizationregistry.view/utils.js b/1-js/99-js-misc/07-weakref-finalizationregistry/weakref-finalizationregistry.view/utils.js new file mode 100644 index 000000000..f0140c116 --- /dev/null +++ b/1-js/99-js-misc/07-weakref-finalizationregistry/weakref-finalizationregistry.view/utils.js @@ -0,0 +1,321 @@ +const loggerContainerEl = document.querySelector(".loggerContainer"); + +export const images = [ + { + img: "https://images.unsplash.com/photo-1471357674240-e1a485acb3e1", + }, + { + img: "https://images.unsplash.com/photo-1589118949245-7d38baf380d6", + }, + { + img: "https://images.unsplash.com/photo-1527631746610-bca00a040d60", + }, + { + img: "https://images.unsplash.com/photo-1500835556837-99ac94a94552", + }, + { + img: "https://images.unsplash.com/photo-1503220317375-aaad61436b1b", + }, + { + img: "https://images.unsplash.com/photo-1501785888041-af3ef285b470", + }, + { + img: "https://images.unsplash.com/photo-1528543606781-2f6e6857f318", + }, + { + img: "https://images.unsplash.com/photo-1523906834658-6e24ef2386f9", + }, + { + img: "https://images.unsplash.com/photo-1539635278303-d4002c07eae3", + }, + { + img: "https://images.unsplash.com/photo-1533105079780-92b9be482077", + }, + { + img: "https://images.unsplash.com/photo-1516483638261-f4dbaf036963", + }, + { + img: "https://images.unsplash.com/photo-1502791451862-7bd8c1df43a7", + }, + { + img: "https://plus.unsplash.com/premium_photo-1663047367140-91adf819d007", + }, + { + img: "https://images.unsplash.com/photo-1506197603052-3cc9c3a201bd", + }, + { + img: "https://images.unsplash.com/photo-1517760444937-f6397edcbbcd", + }, + { + img: "https://images.unsplash.com/photo-1518684079-3c830dcef090", + }, + { + img: "https://images.unsplash.com/photo-1505832018823-50331d70d237", + }, + { + img: "https://images.unsplash.com/photo-1524850011238-e3d235c7d4c9", + }, + { + img: "https://plus.unsplash.com/premium_photo-1661277758451-b5053309eea1", + }, + { + img: "https://images.unsplash.com/photo-1541410965313-d53b3c16ef17", + }, + { + img: "https://images.unsplash.com/photo-1528702748617-c64d49f918af", + }, + { + img: "https://images.unsplash.com/photo-1502003148287-a82ef80a6abc", + }, + { + img: "https://plus.unsplash.com/premium_photo-1661281272544-5204ea3a481a", + }, + { + img: "https://images.unsplash.com/photo-1503457574462-bd27054394c1", + }, + { + img: "https://images.unsplash.com/photo-1499363536502-87642509e31b", + }, + { + img: "https://images.unsplash.com/photo-1551918120-9739cb430c6d", + }, + { + img: "https://plus.unsplash.com/premium_photo-1661382219642-43e54f7e81d7", + }, + { + img: "https://images.unsplash.com/photo-1497262693247-aa258f96c4f5", + }, + { + img: "https://images.unsplash.com/photo-1525254134158-4fd5fdd45793", + }, + { + img: "https://plus.unsplash.com/premium_photo-1661274025419-4c54107d5c48", + }, + { + img: "https://images.unsplash.com/photo-1553697388-94e804e2f0f6", + }, + { + img: "https://images.unsplash.com/photo-1574260031597-bcd9eb192b4f", + }, + { + img: "https://images.unsplash.com/photo-1536323760109-ca8c07450053", + }, + { + img: "https://images.unsplash.com/photo-1527824404775-dce343118ebc", + }, + { + img: "https://images.unsplash.com/photo-1612278675615-7b093b07772d", + }, + { + img: "https://images.unsplash.com/photo-1522010675502-c7b3888985f6", + }, + { + img: "https://images.unsplash.com/photo-1501555088652-021faa106b9b", + }, + { + img: "https://plus.unsplash.com/premium_photo-1669223469435-27e091439169", + }, + { + img: "https://images.unsplash.com/photo-1506012787146-f92b2d7d6d96", + }, + { + img: "https://images.unsplash.com/photo-1511739001486-6bfe10ce785f", + }, + { + img: "https://images.unsplash.com/photo-1553342385-111fd6bc6ab3", + }, + { + img: "https://images.unsplash.com/photo-1516546453174-5e1098a4b4af", + }, + { + img: "https://images.unsplash.com/photo-1527142879-95b61a0b8226", + }, + { + img: "https://images.unsplash.com/photo-1520466809213-7b9a56adcd45", + }, + { + img: "https://images.unsplash.com/photo-1516939884455-1445c8652f83", + }, + { + img: "https://images.unsplash.com/photo-1545389336-cf090694435e", + }, + { + img: "https://plus.unsplash.com/premium_photo-1669223469455-b7b734c838f4", + }, + { + img: "https://images.unsplash.com/photo-1454391304352-2bf4678b1a7a", + }, + { + img: "https://images.unsplash.com/photo-1433838552652-f9a46b332c40", + }, + { + img: "https://images.unsplash.com/photo-1506125840744-167167210587", + }, + { + img: "https://images.unsplash.com/photo-1522199873717-bc67b1a5e32b", + }, + { + img: "https://images.unsplash.com/photo-1495904786722-d2b5a19a8535", + }, + { + img: "https://images.unsplash.com/photo-1614094082869-cd4e4b2905c7", + }, + { + img: "https://images.unsplash.com/photo-1474755032398-4b0ed3b2ae5c", + }, + { + img: "https://images.unsplash.com/photo-1501554728187-ce583db33af7", + }, + { + img: "https://images.unsplash.com/photo-1515859005217-8a1f08870f59", + }, + { + img: "https://images.unsplash.com/photo-1531141445733-14c2eb7d4c1f", + }, + { + img: "https://images.unsplash.com/photo-1500259783852-0ca9ce8a64dc", + }, + { + img: "https://images.unsplash.com/photo-1510662145379-13537db782dc", + }, + { + img: "https://images.unsplash.com/photo-1573790387438-4da905039392", + }, + { + img: "https://images.unsplash.com/photo-1512757776214-26d36777b513", + }, + { + img: "https://images.unsplash.com/photo-1518855706573-84de4022b69b", + }, + { + img: "https://images.unsplash.com/photo-1500049242364-5f500807cdd7", + }, + { + img: "https://images.unsplash.com/photo-1528759335187-3b683174c86a", + }, +]; +export const THUMBNAIL_PARAMS = "w=240&h=240&fit=crop&auto=format"; + +// Console styles. +export const CONSOLE_BASE_STYLES = [ + "font-size: 12px", + "padding: 4px", + "border: 2px solid #5a5a5a", + "color: white", +].join(";"); +export const CONSOLE_PRIMARY = [ + CONSOLE_BASE_STYLES, + "background-color: #13315a", +].join(";"); +export const CONSOLE_SUCCESS = [ + CONSOLE_BASE_STYLES, + "background-color: #385a4e", +].join(";"); +export const CONSOLE_ERROR = [ + CONSOLE_BASE_STYLES, + "background-color: #5a1a24", +].join(";"); + +// Layouts. +export const LAYOUT_4_COLUMNS = { + name: "Layout 4 columns", + columns: 4, + itemWidth: 240, + itemHeight: 240, +}; +export const LAYOUT_8_COLUMNS = { + name: "Layout 8 columns", + columns: 8, + itemWidth: 240, + itemHeight: 240, +}; +export const LAYOUTS = [LAYOUT_4_COLUMNS, LAYOUT_8_COLUMNS]; + +export const createImageFile = async (src) => + new Promise((resolve, reject) => { + const img = new Image(); + img.src = src; + img.onload = () => resolve(img); + img.onerror = () => reject(new Error("Failed to construct image.")); + }); + +export const loadImage = async (url) => { + try { + const response = await fetch(url); + if (!response.ok) { + throw new Error(String(response.status)); + } + + return await response.blob(); + } catch (e) { + console.log(`%cFETCHED_FAILED: ${e}`, CONSOLE_ERROR); + } +}; + +export const weakRefCache = (fetchImg) => { + const imgCache = new Map(); + const registry = new FinalizationRegistry(({ imgName, size, type }) => { + const cachedImg = imgCache.get(imgName); + if (cachedImg && !cachedImg.deref()) { + imgCache.delete(imgName); + console.log( + `%cCLEANED_IMAGE: Url: ${imgName}, Size: ${size}, Type: ${type}`, + CONSOLE_ERROR, + ); + + const logEl = document.createElement("div"); + logEl.classList.add("logger-item", "logger--error"); + logEl.innerHTML = `CLEANED_IMAGE: Url: ${imgName}, Size: ${size}, Type: ${type}`; + loggerContainerEl.appendChild(logEl); + loggerContainerEl.scrollTop = loggerContainerEl.scrollHeight; + } + }); + + return async (imgName) => { + const cachedImg = imgCache.get(imgName); + + if (cachedImg?.deref() !== undefined) { + console.log( + `%cCACHED_IMAGE: Url: ${imgName}, Size: ${cachedImg.size}, Type: ${cachedImg.type}`, + CONSOLE_SUCCESS, + ); + + const logEl = document.createElement("div"); + logEl.classList.add("logger-item", "logger--success"); + logEl.innerHTML = `CACHED_IMAGE: Url: ${imgName}, Size: ${cachedImg.size}, Type: ${cachedImg.type}`; + loggerContainerEl.appendChild(logEl); + loggerContainerEl.scrollTop = loggerContainerEl.scrollHeight; + + return cachedImg?.deref(); + } + + const newImg = await fetchImg(imgName); + console.log( + `%cFETCHED_IMAGE: Url: ${imgName}, Size: ${newImg.size}, Type: ${newImg.type}`, + CONSOLE_PRIMARY, + ); + + const logEl = document.createElement("div"); + logEl.classList.add("logger-item", "logger--primary"); + logEl.innerHTML = `FETCHED_IMAGE: Url: ${imgName}, Size: ${newImg.size}, Type: ${newImg.type}`; + loggerContainerEl.appendChild(logEl); + loggerContainerEl.scrollTop = loggerContainerEl.scrollHeight; + + imgCache.set(imgName, new WeakRef(newImg)); + registry.register(newImg, { + imgName, + size: newImg.size, + type: newImg.type, + }); + + return newImg; + }; +}; + +export const stateObj = { + loading: false, + drawing: true, + collageRendered: false, + currentLayout: LAYOUTS[0], + selectedImages: new Set(), +}; diff --git a/2-ui/1-document/01-browser-environment/article.md b/2-ui/1-document/01-browser-environment/article.md index 56b568833..eedc28fb3 100644 --- a/2-ui/1-document/01-browser-environment/article.md +++ b/2-ui/1-document/01-browser-environment/article.md @@ -1,10 +1,10 @@ # Browser environment, specs -The JavaScript language was initially created for web browsers. Since then it has evolved and become a language with many uses and platforms. +The JavaScript language was initially created for web browsers. Since then, it has evolved into a language with many uses and platforms. -A platform may be a browser, or a web-server or another *host*, even a "smart" coffee machine, if it can run JavaScript. Each of them provides platform-specific functionality. The JavaScript specification calls that a *host environment*. +A platform may be a browser, or a web-server or another *host*, or even a "smart" coffee machine if it can run JavaScript. Each of these provides platform-specific functionality. The JavaScript specification calls that a *host environment*. -A host environment provides own objects and functions additional to the language core. Web browsers give a means to control web pages. Node.js provides server-side features, and so on. +A host environment provides its own objects and functions in addition to the language core. Web browsers give a means to control web pages. Node.js provides server-side features, and so on. Here's a bird's-eye view of what we have when JavaScript runs in a web browser: @@ -15,9 +15,9 @@ There's a "root" object called `window`. It has two roles: 1. First, it is a global object for JavaScript code, as described in the chapterHello
`. + - [Comment](https://dom.spec.whatwg.org/#interface-comment) -- the class for comments. They are not shown, but each comment becomes a member of DOM. + +- [Element](https://dom.spec.whatwg.org/#interface-element) -- is the base class for DOM elements. + + It provides element-level navigation like `nextElementSibling`, `children` and searching methods like `getElementsByTagName`, `querySelector`. + + A browser supports not only HTML, but also XML and SVG. So the `Element` class serves as a base for more specific classes: `SVGElement`, `XMLElement` (we don't need them here) and `HTMLElement`. + +- Finally, [HTMLElement](https://html.spec.whatwg.org/multipage/dom.html#htmlelement) is the basic class for all HTML elements. We'll work with it most of the time. + + It is inherited by concrete HTML elements: - [HTMLInputElement](https://html.spec.whatwg.org/multipage/forms.html#htmlinputelement) -- the class for `` elements, - [HTMLBodyElement](https://html.spec.whatwg.org/multipage/semantics.html#htmlbodyelement) -- the class for `` elements, - [HTMLAnchorElement](https://html.spec.whatwg.org/multipage/semantics.html#htmlanchorelement) -- the class for `` elements, - - ...and so on, each tag has its own class that may provide specific properties and methods. + - ...and so on. + +There are many other tags with their own classes that may have specific properties and methods, while some elements, such as ``, `