Dies ist ein erster Blick auf die JavaScript-Syntax. Machen Sie sich keine Sorgen, wenn einige Dinge noch keinen Sinn ergeben. Sie werden alle später in diesem Buch detaillierter erklärt.
Auch diese Übersicht ist nicht erschöpfend. Sie konzentriert sich auf das Wesentliche.
// single-line comment
/*
Comment with
multiple lines
*/Booleans
true
falseZahlen
1.141
-123Der grundlegende Zahlentyp wird sowohl für Gleitkommazahlen (Doubles) als auch für Ganzzahlen verwendet.
Bigints
17n
-49nDer grundlegende Zahlentyp kann Ganzzahlen nur innerhalb eines Bereichs von 53 Bits plus Vorzeichen korrekt darstellen. Bigints können beliebig groß werden.
Strings
'abc'
"abc"
`String with interpolated values: ${256} and ${true}`JavaScript hat keinen separaten Typ für Zeichen. Es verwendet Strings, um sie darzustellen.
Eine Assertion beschreibt, wie das Ergebnis einer Berechnung aussehen soll, und löst eine Ausnahme aus, wenn diese Erwartungen nicht erfüllt sind. Zum Beispiel besagt die folgende Assertion, dass das Ergebnis der Berechnung 7 plus 1 gleich 8 sein muss
assert.equal(7 + 1, 8);assert.equal() ist ein Methodenaufruf (das Objekt ist assert, die Methode ist .equal()) mit zwei Argumenten: dem tatsächlichen Ergebnis und dem erwarteten Ergebnis. Es ist Teil einer Node.js Assertion API, die später in diesem Buch erklärt wird.
Es gibt auch assert.deepEqual(), das Objekte tief vergleicht.
Protokollierung auf der Konsole eines Browsers oder Node.js
// Printing a value to standard out (another method call)
console.log('Hello!');
// Printing error information to standard error
console.error('Something went wrong!');// Operators for booleans
assert.equal(true && false, false); // And
assert.equal(true || false, true); // Or
// Operators for numbers
assert.equal(3 + 4, 7);
assert.equal(5 - 1, 4);
assert.equal(3 * 4, 12);
assert.equal(10 / 4, 2.5);
// Operators for bigints
assert.equal(3n + 4n, 7n);
assert.equal(5n - 1n, 4n);
assert.equal(3n * 4n, 12n);
assert.equal(10n / 4n, 2n);
// Operators for strings
assert.equal('a' + 'b', 'ab');
assert.equal('I see ' + 3 + ' monkeys', 'I see 3 monkeys');
// Comparison operators
assert.equal(3 < 4, true);
assert.equal(3 <= 4, true);
assert.equal('abc' === 'abc', true);
assert.equal('abc' !== 'def', true);JavaScript hat auch einen Vergleichsoperator ==. Ich empfehle, ihn zu vermeiden – warum, wird in §13.4.3 „Empfehlung: Immer strikte Gleichheit verwenden“ erklärt.
const erstellt unveränderliche Variablenbindungen: Jede Variable muss sofort initialisiert werden und wir können später keinen anderen Wert zuweisen. Der Wert selbst kann jedoch veränderlich sein und wir können seinen Inhalt ändern. Anders ausgedrückt: const macht Werte nicht unveränderlich.
// Declaring and initializing x (immutable binding):
const x = 8;
// Would cause a TypeError:
// x = 9;let erstellt veränderliche Variablenbindungen
// Declaring y (mutable binding):
let y;
// We can assign a different value to y:
y = 3 * 5;
// Declaring and initializing z:
let z = 3 * 5;// add1() has the parameters a and b
function add1(a, b) {
return a + b;
}
// Calling function add1()
assert.equal(add1(5, 2), 7);Pfeilfunktionsausdrücke werden insbesondere als Argumente von Funktionsaufrufen und Methodenaufrufen verwendet
const add2 = (a, b) => { return a + b };
// Calling function add2()
assert.equal(add2(5, 2), 7);
// Equivalent to add2:
const add3 = (a, b) => a + b;Der vorherige Code enthält die folgenden beiden Pfeilfunktionen (die Begriffe Ausdruck und Anweisung werden später in diesem Kapitel erklärt)
// An arrow function whose body is a code block
(a, b) => { return a + b }
// An arrow function whose body is an expression
(a, b) => a + b// Creating a plain object via an object literal
const obj = {
first: 'Jane', // property
last: 'Doe', // property
getFullName() { // property (method)
return this.first + ' ' + this.last;
},
};
// Getting a property value
assert.equal(obj.first, 'Jane');
// Setting a property value
obj.first = 'Janey';
// Calling the method
assert.equal(obj.getFullName(), 'Janey Doe');// Creating an Array via an Array literal
const arr = ['a', 'b', 'c'];
assert.equal(arr.length, 3);
// Getting an Array element
assert.equal(arr[1], 'b');
// Setting an Array element
arr[1] = 'β';
// Adding an element to an Array:
arr.push('d');
assert.deepEqual(
arr, ['a', 'β', 'c', 'd']);Bedingte Anweisung
if (x < 0) {
x = -x;
}for-of-Schleife
const arr = ['a', 'b'];
for (const element of arr) {
console.log(element);
}
// Output:
// 'a'
// 'b'Jedes Modul ist eine einzelne Datei. Betrachten Sie zum Beispiel die folgenden beiden Dateien mit Modulen darin
file-tools.mjs
main.mjs
Das Modul in file-tools.mjs exportiert seine Funktion isTextFilePath()
export function isTextFilePath(filePath) {
return filePath.endsWith('.txt');
}Das Modul in main.mjs importiert das gesamte Modul path und die Funktion isTextFilePath()
// Import whole module as namespace object `path`
import * as path from 'path';
// Import a single export of module file-tools.mjs
import {isTextFilePath} from './file-tools.mjs';class Person {
constructor(name) {
this.name = name;
}
describe() {
return `Person named ${this.name}`;
}
static logNames(persons) {
for (const person of persons) {
console.log(person.name);
}
}
}
class Employee extends Person {
constructor(name, title) {
super(name);
this.title = title;
}
describe() {
return super.describe() +
` (${this.title})`;
}
}
const jane = new Employee('Jane', 'CTO');
assert.equal(
jane.describe(),
'Person named Jane (CTO)');function throwsException() {
throw new Error('Problem!');
}
function catchesException() {
try {
throwsException();
} catch (err) {
assert.ok(err instanceof Error);
assert.equal(err.message, 'Problem!');
}
}Hinweis
try-finally und try-catch-finally werden ebenfalls unterstützt.Error und seinen Unterklassen unterstützt.Die grammatikalische Kategorie von Variablennamen und Eigenschaftsnamen wird Bezeichner genannt.
Bezeichner dürfen folgende Zeichen enthalten
A–Z, a–z (usw.)$, _0–9 (usw.)Einige Wörter haben in JavaScript eine besondere Bedeutung und werden als reserviert bezeichnet. Beispiele hierfür sind: if, true, const.
Reservierte Wörter dürfen nicht als Variablennamen verwendet werden
const if = 123;
// SyntaxError: Unexpected token ifSie sind jedoch als Eigenschaftsnamen zulässig
> const obj = { if: 123 };
> obj.if
123Gängige Schreibweisen für die Verkettung von Wörtern sind
threeConcatenatedWords three_concatenated_words three-concatenated-words Im Allgemeinen verwendet JavaScript Camel Case, mit Ausnahme von Konstanten.
Kleinbuchstaben
myFunctionobj.myMethodspecial-classspecialClassGroßbuchstaben
MyClassMY_CONSTANTmyConstantDie folgenden Namenskonventionen sind in JavaScript beliebt.
Wenn der Name eines Parameters mit einem Unterstrich beginnt (oder ein Unterstrich ist), bedeutet dies, dass dieser Parameter nicht verwendet wird – zum Beispiel
arr.map((_x, i) => i)Wenn der Name einer Eigenschaft eines Objekts mit einem Unterstrich beginnt, gilt diese Eigenschaft als privat
class ValueWrapper {
constructor(value) {
this._value = value;
}
}Am Ende einer Anweisung
const x = 123;
func();Aber nicht, wenn diese Anweisung mit einer geschweiften Klammer endet
while (false) {
// ···
} // no semicolon
function func() {
// ···
} // no semicolonDas Hinzufügen eines Semikolons nach einer solchen Anweisung ist jedoch kein Syntaxfehler – es wird als leere Anweisung interpretiert
// Function declaration followed by empty statement:
function func() {
// ···
}; Quiz: Grundlagen
Siehe Quiz-App.
Alle verbleibenden Abschnitte dieses Kapitels sind fortgeschritten.
Erstes Zeichen
é und ü sowie Zeichen aus nicht-lateinischen Alphabeten wie α)$_Nachfolgende Zeichen
Beispiele
const ε = 0.0001;
const строка = '';
let _tmp = 0;
const $foo2 = true;Reservierte Wörter dürfen keine Variablennamen sein, aber sie dürfen Eigenschaftsnamen sein.
Alle JavaScript-Schlüsselwörter sind reservierte Wörter:
awaitbreakcasecatchclassconstcontinuedebuggerdefaultdeletedoelseexportextendsfinallyforfunctionifimportininstanceofletnewreturnstaticsuperswitchthisthrowtrytypeofvarvoidwhilewithyield
Die folgenden Tokens sind ebenfalls Schlüsselwörter, werden aber derzeit in der Sprache nicht verwendet
enumimplementspackageprotectedinterfaceprivatepublic
Die folgenden Literale sind reservierte Wörter
truefalsenull
Technisch gesehen sind diese Wörter nicht reserviert, aber Sie sollten sie ebenfalls vermeiden, da sie praktisch Schlüsselwörter sind
InfinityNaNundefinedasync
Sie sollten auch nicht die Namen globaler Variablen (String, Math usw.) für Ihre eigenen Variablen und Parameter verwenden.
In diesem Abschnitt untersuchen wir, wie JavaScript zwei Arten von syntaktischen Konstrukten unterscheidet: Anweisungen und Ausdrücke. Danach werden wir sehen, dass dies Probleme verursachen kann, da die gleiche Syntax unterschiedliche Bedeutungen haben kann, je nachdem, wo sie verwendet wird.
Wir tun so, als gäbe es nur Anweisungen und Ausdrücke
Zur Vereinfachung tun wir so, als gäbe es in JavaScript nur Anweisungen und Ausdrücke.
Eine Anweisung ist ein Code-Teil, der ausgeführt werden kann und eine Art Aktion ausführt. Zum Beispiel ist if eine Anweisung
let myStr;
if (myBool) {
myStr = 'Yes';
} else {
myStr = 'No';
}Ein weiteres Beispiel für eine Anweisung: eine Funktionsdeklaration.
function twice(x) {
return x + x;
}Ein Ausdruck ist ein Code-Teil, der ausgewertet werden kann, um einen Wert zu erzeugen. Zum Beispiel ist der Code zwischen den Klammern ein Ausdruck
let myStr = (myBool ? 'Yes' : 'No');Der Operator _?_:_, der zwischen den Klammern verwendet wird, wird als ternärer Operator bezeichnet. Es ist die Ausdrucksversion der if-Anweisung.
Sehen wir uns weitere Beispiele für Ausdrücke an. Wir geben Ausdrücke ein, und die REPL wertet sie für uns aus
> 'ab' + 'cd'
'abcd'
> Number('123')
123
> true || false
trueDie aktuelle Position im JavaScript-Quellcode bestimmt, welche Art von syntaktischen Konstrukten Sie verwenden dürfen
Der Körper einer Funktion muss eine Folge von Anweisungen sein
function max(x, y) {
if (x > y) {
return x;
} else {
return y;
}
}Die Argumente eines Funktionsaufrufs oder eines Methodenaufrufs müssen Ausdrücke sein
console.log('ab' + 'cd', Number('123'));Ausdrücke können jedoch als Anweisungen verwendet werden. Dann werden sie Ausdrucksanweisungen genannt. Das Gegenteil ist nicht der Fall: Wenn der Kontext einen Ausdruck erfordert, können Sie keine Anweisung verwenden.
Der folgende Code zeigt, dass jeder Ausdruck bar() entweder Ausdruck oder Anweisung sein kann – es hängt vom Kontext ab
function f() {
console.log(bar()); // bar() is expression
bar(); // bar(); is (expression) statement
}JavaScript hat mehrere Programmierkonstrukte, die syntaktisch mehrdeutig sind: Die gleiche Syntax wird unterschiedlich interpretiert, je nachdem, ob sie im Anweisungs- oder im Ausdruckskontext verwendet wird. Dieser Abschnitt untersucht das Phänomen und die Fallstricke, die es verursacht.
Eine Funktionsdeklaration ist eine Anweisung
function id(x) {
return x;
}Ein Funktionsausdruck ist ein Ausdruck (rechte Seite von =)
const id = function me(x) {
return x;
};Im folgenden Code ist {} ein Objekt-Literal: ein Ausdruck, der ein leeres Objekt erstellt.
const obj = {};Dies ist ein leerer Codeblock (eine Anweisung)
{
}Die Mehrdeutigkeiten sind nur im Anweisungskontext ein Problem: Wenn der JavaScript-Parser auf mehrdeutige Syntax stößt, weiß er nicht, ob es sich um eine einfache Anweisung oder eine Ausdrucksanweisung handelt. Zum Beispiel
function beginnt: Ist es eine Funktionsdeklaration oder ein Funktionsausdruck?{ beginnt: Ist es ein Objekt-Literal oder ein Codeblock?Um die Mehrdeutigkeit aufzulösen, werden Anweisungen, die mit function oder { beginnen, niemals als Ausdrücke interpretiert. Wenn Sie möchten, dass eine Ausdrucksanweisung mit einem dieser Tokens beginnt, müssen Sie sie in Klammern einschließen
(function (x) { console.log(x) })('abc');
// Output:
// 'abc'In diesem Code
Wir erstellen zunächst eine Funktion über einen Funktionsausdruck
function (x) { console.log(x) }Dann rufen wir diese Funktion auf: ('abc')
Das in (1) gezeigte Codefragment wird nur als Ausdruck interpretiert, weil wir es in Klammern einschließen. Wenn wir es nicht täten, erhielten wir einen Syntaxfehler, da JavaScript dann eine Funktionsdeklaration erwartet und sich über den fehlenden Funktionsnamen beschwert. Außerdem können Sie keinen Funktionsaufruf direkt nach einer Funktionsdeklaration platzieren.
Später in diesem Buch werden wir weitere Beispiele für Fallstricke sehen, die durch syntaktische Mehrdeutigkeit verursacht werden
Jede Anweisung wird durch ein Semikolon beendet
const x = 3;
someFunction('abc');
i++;außer Anweisungen, die mit Blöcken enden
function foo() {
// ···
}
if (y > 0) {
// ···
}Der folgende Fall ist etwas knifflig
const func = () => {}; // semicolon!Die gesamte const-Deklaration (eine Anweisung) endet mit einem Semikolon, aber darin befindet sich ein Pfeilfunktionsausdruck. Das heißt, nicht die Anweisung selbst endet mit einer geschweiften Klammer; es ist der eingebettete Pfeilfunktionsausdruck. Deshalb steht am Ende ein Semikolon.
Der Körper einer Kontrollanweisung ist selbst eine Anweisung. Zum Beispiel ist dies die Syntax der while-Schleife
while (condition)
statementDer Körper kann eine einzelne Anweisung sein
while (a > 0) a--;Aber Blöcke sind auch Anweisungen und daher zulässige Körper von Kontrollanweisungen
while (a > 0) {
a--;
}Wenn Sie möchten, dass eine Schleife einen leeren Körper hat, ist Ihre erste Option eine leere Anweisung (die nur ein Semikolon ist)
while (processNextItem() > 0);Ihre zweite Option ist ein leerer Block
while (processNextItem() > 0) {}Obwohl ich empfehle, immer Semikolons zu schreiben, sind die meisten davon in JavaScript optional. Der Mechanismus, der dies ermöglicht, wird automatische Semikolon-Einfügung (ASI) genannt. In gewisser Weise korrigiert er Syntaxfehler.
ASI funktioniert wie folgt. Das Parsen einer Anweisung wird fortgesetzt, bis entweder Folgendes eintritt:
Mit anderen Worten, ASI kann als Einfügung von Semikolons an Zeilenumbrüchen betrachtet werden. Die nächsten Unterabschnitte behandeln die Fallstricke von ASI.
Die gute Nachricht über ASI ist, dass es – wenn Sie sich nicht darauf verlassen und immer Semikolons schreiben – nur einen Fallstrick gibt, den Sie beachten müssen. Es ist, dass JavaScript Zeilenumbrüche nach bestimmten Tokens verbietet. Wenn Sie einen Zeilenumbruch einfügen, wird auch ein Semikolon eingefügt.
Das Token, bei dem dies am praktischsten relevant ist, ist return. Betrachten Sie zum Beispiel den folgenden Code
return
{
first: 'jane'
};Dieser Code wird geparst als
return;
{
first: 'jane';
}
;Das heißt
return;{'jane'; mit Label first:};Warum macht JavaScript das? Es schützt davor, versehentlich einen Wert in einer Zeile nach einem return zurückzugeben.
In einigen Fällen wird ASI nicht ausgelöst, wenn Sie denken, dass es ausgelöst werden sollte. Das macht das Leben komplizierter für Leute, die Semikolons nicht mögen, weil sie sich dieser Fälle bewusst sein müssen. Die folgenden drei Beispiele sind dafür gedacht. Es gibt mehr.
Beispiel 1: Unbeabsichtigter Funktionsaufruf.
a = b + c
(d + e).print()Geparst als
a = b + c(d + e).print();Beispiel 2: Unbeabsichtigte Division.
a = b
/hi/g.exec(c).map(d)Geparst als
a = b / hi / g.exec(c).map(d);Beispiel 3: Unbeabsichtigter Eigenschaftszugriff.
someFunction()
['ul', 'ol'].map(x => x + x)Ausgeführt als
const propKey = ('ul','ol'); // comma operator
assert.equal(propKey, 'ol');
someFunction()[propKey].map(x => x + x);Ich empfehle, immer Semikolons zu schreiben
Es gibt jedoch auch viele Leute, die die zusätzliche visuelle Unordnung von Semikolons nicht mögen. Wenn Sie einer von ihnen sind: Code ohne sie ist legal. Ich empfehle, Werkzeuge zu verwenden, die Ihnen helfen, Fehler zu vermeiden. Die folgenden zwei Beispiele sind dafür gedacht
Ab ECMAScript 5 hat JavaScript zwei Modi, in denen JavaScript ausgeführt werden kann
Sie werden Sloppy Mode in modernem JavaScript-Code selten antreffen, der fast immer in Modulen steht. In diesem Buch gehe ich davon aus, dass Strict Mode immer eingeschaltet ist.
In Skriptdateien und CommonJS-Modulen schalten Sie Strict Mode für eine vollständige Datei ein, indem Sie den folgenden Code in die erste Zeile setzen
'use strict';Das Tolle an dieser „Direktive“ ist, dass ECMAScript-Versionen vor 5 sie einfach ignorieren: Sie ist eine Ausdrucksanweisung, die nichts tut.
Sie können Strict Mode auch nur für eine einzelne Funktion einschalten
function functionInStrictMode() {
'use strict';
}Betrachten wir drei Dinge, die Strict Mode besser macht als Sloppy Mode. Alle Codefragmente in diesem Abschnitt werden im Sloppy Mode ausgeführt.
Im Nicht-Strict-Modus erstellt das Ändern einer nicht deklarierten Variablen eine globale Variable.
function sloppyFunc() {
undeclaredVar1 = 123;
}
sloppyFunc();
// Created global variable `undeclaredVar1`:
assert.equal(undeclaredVar1, 123);Strict Mode macht es besser und löst einen ReferenceError aus. Das erleichtert die Erkennung von Tippfehlern.
function strictFunc() {
'use strict';
undeclaredVar2 = 123;
}
assert.throws(
() => strictFunc(),
{
name: 'ReferenceError',
message: 'undeclaredVar2 is not defined',
});assert.throws() besagt, dass sein erstes Argument, eine Funktion, einen ReferenceError auslöst, wenn sie aufgerufen wird.
Im Strict Mode existiert eine Variable, die über eine Funktionsdeklaration erstellt wurde, nur innerhalb des innersten umschließenden Blocks
function strictFunc() {
'use strict';
{
function foo() { return 123 }
}
return foo(); // ReferenceError
}
assert.throws(
() => strictFunc(),
{
name: 'ReferenceError',
message: 'foo is not defined',
});Im Sloppy Mode sind Funktionsdeklarationen funktionsbezogen
function sloppyFunc() {
{
function foo() { return 123 }
}
return foo(); // works
}
assert.equal(sloppyFunc(), 123);Im Strict Mode erhalten Sie eine Ausnahme, wenn Sie versuchen, unveränderliche Daten zu ändern
function strictFunc() {
'use strict';
true.prop = 1; // TypeError
}
assert.throws(
() => strictFunc(),
{
name: 'TypeError',
message: "Cannot create property 'prop' on boolean 'true'",
});Im Sloppy Mode schlägt die Zuweisung stillschweigend fehl
function sloppyFunc() {
true.prop = 1; // fails silently
return true.prop;
}
assert.equal(sloppyFunc(), undefined); Weitere Lektüre: Sloppy Mode
Weitere Informationen darüber, wie sich Sloppy Mode von Strict Mode unterscheidet, finden Sie unter MDN.
Quiz: Fortgeschritten
Siehe Quiz-App.