JavaScript Intermediate

Tagged Template strings

Tagged Template Strings

Tagged Template Strings ermöglichen zusätzliche Verarbeitung, wenn Werte eingebunden werden

Beispiele für Verwendungszwecke:

  • "Escaping" von Werten aus unsicheren Quellen
  • Anpasen der Einrückung
  • Einbinden von Stilen in React
  • ...

Tagged Template Strings

Beispiel: "Escaping" von HTML:

import { safeHtml } from 'common-tags';

const message = 'I <3 U';

const post = safeHtml`
  <div>${message}</div>
`;

Ergebnis:

<div>I &lt;3 U</div>

Errors

Errors

JavaScript versucht oft, keine Fehler zu werfen, sondern Resultate zu liefern (auch wennd diese keinen Sinn machen)

  • 1 + "1" → "11"
  • parseInt("foo") → NaN
  • {} + [] → 0
  • Math.sqrttt → undefined

Arten von Fehlern

  • SyntaxError
  • ReferenceError
  • TypeError
  • ...

Übung: versuche, diese Arten von Fehlern auszulösen

Beispiele: ReferenceErrors und TypeErrors

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

Fehler abfangen

mögliche Fehlerquellen:

  • Fehler des Programmierers
  • Ungültige Eingaben / Werte → können abgefangen und behandelt werden

Fehler abfangen

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' };
}

Fehler abfangen

Beispiel: Fehler in node.js

try {
  fs.writeFileSync('foo.txt', 'foo');
} catch {
  console.log('could not write to file');
}

Fehler abfangen

Zugriff auf das Fehler-Objekt:

try {
  // ...
} catch (error) {
  console.log(error.name); // SyntaxError
  console.log(error.message); // JSON.parse: expected ...
}

Fehler abfangen

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');
}

map, filter, reduce

Array-Methoden für die funktionale Programmierung

map

  • Ändert jeden Eintrag eines Arrays mit Hilfe einer Funktion ab
  • Rückgabewert: neues Array
const myNumbers = [1, 2, 3];

const newNumbers = myNumbers.map((n) => 3 * n);
// [3, 6, 9]

filter

  • Behält nur gewisse Einträge in einem Array
  • Nutzt eine Funktion, um Einträge auf ein bestimmtes Kriterium zu testen
  • Rückgabewert: neues Array
const myNumbers = [1, 2, 3, 4];

const isEven = (n) => n % 2 === 0;

const evenNumbers = myNumbers.filter(isEven);
// [2, 4]

reduce

  • Verarbeitet die Einträge in einem Array zu einem einzelnen Wert
  • Verwendet eine Funktion, die aus zwei bestehenden Werten einen resultierenden Wert erstellt - diese Funktion wird wiederholt aufgerufen

reduce - Beispiel

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

Optional chaining

Optional Chaining

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

Optional chaining mit Funktionsaufrufen:

props.onClick?.();

wenn props.onClick definiert ist, wird es aufgerufen, andernfalls wird der Ausdruck zu undefined ausgewertet

Objektorientierte Programmierung (alt)

Prototypen und Konstruktorfunktionen

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

Prototypen und Konstruktorfunktionen

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;
}

Prototypen und Methodendefinition

Car.prototype.accelerate = function() {
  console.log('wrooom!');
};

Car.prototype.getDescription = function() {
  return this.make + ' ' + this.model;
};

Verwendung von Objekten

var myCar = new Car('VW', 'Golf');

console.log(myCar);
console.log(myCar.getDescription());
myCar.accelerate();

Objektorientierte Programmierung (ab ES2015)

OOP (neu)

class Car {
  constructor(brand, model) {
    this.brand = brand;
    this.model = model;
  }

  accelerate() {
    console.log('wroom!');
  }
}

OOP (neu)

Vererbung

class LuxuryCar extends Car {
  openRoof() {}
}

"this" und seine Quirks in JavaScript

"this" und seine Quirks

In Objektmethoden bezieht sich this üblicherweise auf das aktuelle Objekt

allerdings:

  • jeder Funktionsaufruf setzt this neu (nicht nur Methodenaufrufe)
  • this wird nur richtig gesetzt, wenn die Methode mit der Syntax object.method() aufgerufen wird

Problem: "this" in anonymen Funktionen

class Foo {
  constructor() {
    // this ist set correctly here
    this.foo = true;
    setTimeout(function () {
      // this will be overwritten here (to 'window')
      console.log(this.foo);
    }, 1000);
  }
}

Lösung: Pfeilfunktionen

class Foo {
  constructor() {
    // this ist set correctly here
    this.foo = true;
    setTimeout(() => {
      // this will *not* be overwritten here
      console.log(this.foo);
    }, 1000);
  }
}

Problem: Methodenaufrufe ohne Methodensyntax

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)

Lösung: Pfeil-Methoden

Seit ES2018 einsetzbar:

class Foo {
  constructor() {
    this.message = 'hello';
  }
  greet = () => {
    console.log(this.message);
  };
}

Lösung: Binden von Methoden

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);
  }

Libraries

Libraries

verbreitete Libraries:

  • lodash (Sammlung nützlicher Funktionen)
  • jQuery (Erleichtert das Arbeiten mit dem DOM)
  • immer.js / immutable.js (Arbeiten mit unveränderlichen Objekten)
  • moment.js (Arbeiten mit Zeitangaben)

jQuery

Ändern von Elementen

  • $('#myelement')
  • el.html('content')
  • el.css('color', 'blue')
  • el.addClass('abc')
  • el.prop('style')

jQuery

Erstellen / hinzufügen / entfernen von Elementen

  • $('<div>')
  • parent.append(child)
  • child.remove()

jQuery

Abfragen von Events

  • $(element).on('click', ...)
  • $(element).click(...)

immutable.js

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);

immutable.js

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);