Tagged Template Strings ermöglichen zusätzliche Verarbeitung, wenn Werte eingebunden werden
Beispiele für Verwendungszwecke:
Beispiel: "Escaping" von HTML:
import { safeHtml } from 'common-tags';
const message = 'I <3 U';
const post = safeHtml`
  <div>${message}</div>
`;
Ergebnis:
<div>I <3 U</div>
JavaScript versucht oft, keine Fehler zu werfen, sondern Resultate zu liefern (auch wennd diese keinen Sinn machen)
1 + "1" → "11"parseInt("foo") → NaN{} + [] → 0Math.sqrttt → undefinedÜbung: versuche, diese Arten von Fehlern auszulösen
console.log(Mathhh);
→ ReferenceError: Mathhh is not defined
Math.sqrttt(2);
→ TypeError: Math.sqrttt is not a function
emptyArray[0].toUpperCase();
→ TypeError: Cannot read properties of undefined
mögliche Fehlerquellen:
Beispiel: Versuch, ungültiges JSON zu parsen
// invalid JSON string
let preferencesString = '{ theme: dark }';
let preferences;
try {
  preferences = JSON.parse(preferencesString);
} catch {
  // error while loading preferences - use default values
  preferences = { theme: 'light' };
}
Beispiel: Fehler in node.js
try {
  fs.writeFileSync('foo.txt', 'foo');
} catch {
  console.log('could not write to file');
}
Zugriff auf das Fehler-Objekt:
try {
  // ...
} catch (error) {
  console.log(error.name); // SyntaxError
  console.log(error.message); // JSON.parse: expected ...
}
Verwendung des finally-Blocks, der immer ausgeführt wird:
try {
  let file = fs.openSync('foo.txt');
  fs.writeSync(file, 'foo\n');
  fs.writeSync(file, 'bar\n');
} catch {
  console.log('error while writing - aborting');
  return;
} finally {
  // "clean up"
  fs.closeSync('foo.txt');
}
const myNumbers = [1, 2, 3];
const newNumbers = myNumbers.map((n) => 3 * n);
// [3, 6, 9]
const myNumbers = [1, 2, 3, 4];
const isEven = (n) => n % 2 === 0;
const evenNumbers = myNumbers.filter(isEven);
// [2, 4]
const initialBalance = 300;
const transactions = [
  { amount: -50, title: 'groceries' },
  { amount: +1000, title: 'salary' },
  { amount: -10, title: 'dinner' },
  { amount: -100, title: 'electricity' },
];
const reducer = (aggregator, transaction) =>
  aggregator + transaction.amount;
const currentBalance = transactions.reduce(
  reducer,
  initialBalance
);
// 300 -> 250 -> 1250 -> 1240 -> 1140
Beispiel für optional chaining:
const userNickname = user?.nickname;
wenn user definiert ist, lies dessen .nickname Property, andernfalls verwende undefined
"konventionelle" Langform:
const userNickname = user ? user.nickname : undefined;
Optional chaining mit Funktionsaufrufen:
props.onClick?.();
wenn props.onClick definiert ist, wird es aufgerufen, andernfalls wird der Ausdruck zu undefined ausgewertet
OOP in JavaScript basiert nicht auf Klassen, sondern auf sogenannten Prototypen
Vergleich aus dem echten Leben: Auto-Objekte
Eine Auto-Klasse wäre ein Bauplan für ein Auto
Ein Auto-Prototyp wäre ein bestehendes Auto auf dessen Vorlage weitere Autos gebaut werden können
Bei OOP in JavaScript beginnen wir damit, die Konstruktorfunktion zu schreiben, die ein Objekt initialisiert:
function Car(brand, model) {
  this.brand = brand;
  this.model = model;
}
Car.prototype.accelerate = function() {
  console.log('wrooom!');
};
Car.prototype.getDescription = function() {
  return this.make + ' ' + this.model;
};
var myCar = new Car('VW', 'Golf');
console.log(myCar);
console.log(myCar.getDescription());
myCar.accelerate();
class Car {
  constructor(brand, model) {
    this.brand = brand;
    this.model = model;
  }
  accelerate() {
    console.log('wroom!');
  }
}
Vererbung
class LuxuryCar extends Car {
  openRoof() {}
}
In Objektmethoden bezieht sich this üblicherweise auf das aktuelle Objekt
allerdings:
object.method() aufgerufen wirdclass Foo {
  constructor() {
    // this ist set correctly here
    this.foo = true;
    setTimeout(function () {
      // this will be overwritten here (to 'window')
      console.log(this.foo);
    }, 1000);
  }
}
class Foo {
  constructor() {
    // this ist set correctly here
    this.foo = true;
    setTimeout(() => {
      // this will *not* be overwritten here
      console.log(this.foo);
    }, 1000);
  }
}
class Foo {
  constructor() {
    this.message = 'hello';
  }
  greet() {
    console.log(this.message);
  }
}
const foo = new Foo();
foo.greet(); // ok
const greet = foo.greet;
greet(); // not ok ("this" is undefined)
Seit ES2018 einsetzbar:
class Foo {
  constructor() {
    this.message = 'hello';
  }
  greet = () => {
    console.log(this.message);
  };
}
const foo = new Foo();
foo.greet(); // ok
const greet = foo.greet.bind(foo);
greet(); // ok
Üblicherweise Zuweisung im Konstruktor:
  constructor() {
    this.greet = this.greet.bind(this);
  }
verbreitete Libraries:
Ändern von Elementen
$('#myelement')el.html('content')el.css('color', 'blue')el.addClass('abc')el.prop('style')Erstellen / hinzufügen / entfernen von Elementen
$('<div>')parent.append(child)child.remove()Abfragen von Events
$(element).on('click', ...)$(element).click(...)Bietet insbesondere die Datenstrukturen List und Map als unveränderliche Alternativen zu Array und Object.
import { List, Map } from 'immutable';
const a1 = List([1, 2, 3]);
const a2 = a1.push(4);
const b1 = Map({ a: 1, b: 2 });
const b2 = b1.set('b', null);
import { fromJS, setIn } from 'immutable';
const todos = fromJS([
  { id: 1, title: 'groceries', completed: false },
  { id: 2, title: 'gardening', completed: false },
]);
const newTodos = todos.setIn([1, 'completed'], true);