Der folgende Beitrag ist ein Gastbeitrag von Ryan Christiani. Ryan ist Lehrer an der HackerYou und hat an einer Videoserie namens Let’s Learn ES6 gearbeitet. Er bot an, einige davon in einem Tutorial-Format zusammenzustellen, was ich für perfekt hielt, da wir ES2015 hier auf CSS-Tricks noch nicht oft behandelt haben.
Was steckt hinter dem Namen?
Im Juni 2015 wurde die größte Ergänzung der JavaScript-Sprache fertiggestellt. Der offizielle Name ist ECMAScript 2015, manchmal auch als „ES6“ bezeichnet, oder heute gebräuchlicher als „ES2105“. Es ist der Höhepunkt jahrelanger Arbeit und Features.
Zukünftig wird es ECMAScript 2016 geben, das wahrscheinlich als „ES7“ oder „ES2016“ bezeichnet wird. Der Plan ist, jährliche inkrementelle Veröffentlichungen zu haben.
Die meisten Browser haben begonnen, die ES2015-Features zu implementieren, aber die Unterstützung variiert zwischen ihnen. Sie können die aktuelle Browserkompatibilität für diese Implementierung anhand dieser Tabelle einsehen.
Tools wie Babel ermöglichen es uns, heute neuen ES2015-Code zu schreiben und eine Aufgabe namens Transpilieren (ähnlich wie Preprocessing) durchzuführen, um den Code in eine frühere Version von JavaScript zu konvertieren, die eine größere Browserunterstützung hat. Dies ähnelt der Funktionsweise von Sass; Sie schreiben Ihren Code zunächst in Sass-Syntax, und dann kompiliert ein Präprozessor ihn in Standard-CSS.
Überblick
In diesem Artikel werden wir uns einige Features ansehen, die für Entwickler jetzt verfügbar sind.
Wir werden uns neue Schlüsselwörter wie let und const ansehen, wie man Template-Literale erstellt, um die Verkettung zu erleichtern, die neue Arrow-Function-Syntax, den Spread-Operator und Rest-Parameter! Hier ist ein Inhaltsverzeichnis
Diese Ergänzungen können das Schreiben von JavaScript zu einer wahren Freude machen!
let und const
let und const sind zwei neue Schlüsselwörter, die seit ES2015 verfügbar sind. Sie werden verwendet, um Variablen zu deklarieren, aber sie haben ein wichtiges Merkmal gemeinsam, das sie von var unterscheidet: sie erstellen blockbezogene Variablen.
Wenn Sie das Schlüsselwort var verwenden, um eine Variable zu erstellen, ist diese funktionsbezogen und nur lokal auf diese Funktion beschränkt. Das bedeutet, sie ist innerhalb der Funktion, in der sie erstellt wurde, und jeder darin verschachtelten Funktion verfügbar. Aber sie ist NICHT außerhalb davon verfügbar. Wenn Sie var verwendet haben, um eine Variable außerhalb einer Funktion zu definieren, wäre sie global verfügbar.
Ein häufiges Problem, das bei funktionsbezogenen Variablen auftritt, ist die for-Schleife.
for (var i = 0; i < 10; i++) {
console.log(i);
}
console.log(i); // Will print out 10;
Es ist üblich, eine Variable innerhalb der for-Schleife zu deklarieren, mit der Absicht, dass sie nur an diese for-Schleife gebunden ist, aber das ist nicht der Fall. Wenn Sie den obigen Code ausführen, werden Sie sehen, dass die Variable i außerhalb der for-Schleife verfügbar ist.
Wenn Sie let oder const verwenden möchten, müssen Sie zuerst den Strict Mode für Ihre JavaScript-Datei aktivieren. Indem Sie 'use strict' am Anfang Ihres Dokuments hinzufügen, aktivieren Sie eine eingeschränkte Variante von JavaScript.
'use strict';
Der Strict Mode ist eine Möglichkeit, sich für eine Version von JavaScript zu entscheiden, die einige Fehler in der Sprache behebt und sie in Fehler umwandelt. Er verbietet auch Syntax, die wahrscheinlich in Zukunft definiert wird! Zum Beispiel können Sie im Strict Mode keine Variable mit dem Namen let erstellen. Weitere Informationen zum Strict Mode finden Sie auf der MDN-Seite zu diesem Thema.
(Anmerkung des Herausgebers: Wenn wir Babel verwenden, müssen wir uns keine Gedanken über „use strict“ machen, da es automatisch zu unserem Code hinzugefügt wird, aber es ist sicherlich erwähnenswert, dass es passiert.)
Ein „Block“ in JavaScript ist alles zwischen { }. Wenn wir also von Block-Scope sprechen, bedeutet das, dass jede Variable, die in geschweiften Klammern definiert ist, nur in diesem Block existiert!
var ist funktionsbezogen, daher macht die Erstellung einer Variable innerhalb eines Blocks mit var sie auch außerhalb des Blocks verfügbar.
{
var user = "Ryan";
}
console.log(user); // Ryan
Wenn Sie eine Variable mit dem Schlüsselwort let definieren, wird eine neue Variable nur innerhalb der { } oder des Blocks erstellt.
{
let user = "Ryan";
}
console.log(user); // Uncaught ReferenceError: user is not defined
Dies definiert und bindet eine Variable nur an den Block, in dem sie sich befindet! Wenn wir uns das for-Schleifen-Beispiel erneut ansehen und var durch let ersetzen
for (let i = 0; i < 10; i++) {
console.log(i);
}
console.log(i); // Uncaught ReferenceError: i is not defined
Jetzt funktioniert es wie beabsichtigt. Das Schlüsselwort const verhält sich exakt gleich, mit einer Ausnahme. Sobald der Basiswert definiert ist, kann er nie wieder neu definiert werden. Es ist ein schreibgeschützter Wert.
const person = 'Ryan';
person = 'Kristen'; // Uncaught TypeError: Assignment to constant variable.
console.log(person);
Der Browser wirft einen Fehler, wenn Sie versuchen, einer mit const definierten Variable einen neuen Wert zuzuweisen. Das gesagt, Sie können etwas wie dieses tun.
const person = {
name: 'Ryan'
};
person.name = 'Kristen';
console.log(person); // {name: 'Kristen'}
Die Verwendung von const erstellt keinen unveränderlichen Wert, der Wert, der in der Variable person gespeichert ist, ist immer noch ein Objekt, aber wir haben gerade eine Eigenschaft darin geändert. Wenn Sie ein Objekt sperren möchten, schauen Sie sich Object.freeze() an.
Wann let und wann const verwenden
Es gibt gerade eine kleine Debatte darüber, wann man let vs. const verwenden sollte. Die Faustregel ist, dass Sie sich für const entscheiden sollten, wenn Sie wissen, dass der Wert während Ihres gesamten Programms nicht neu definiert wird. Wenn Sie einen Wert benötigen, der sich ändern kann, entscheiden Sie sich für let. Wenn Sie dem Browser mitteilen, dass eine Variable während des gesamten Programms konstant ist, kann dies zu bestimmten Anpassungen führen und die Leistung steigern!
Template Literals
In ES2015 gibt es eine neue Art, einen String zu definieren, und sie bringt zusätzliche Vorteile mit sich. Derzeit können Sie, wenn Sie einen String definieren möchten, '' oder "" verwenden.
let name = "Ryan";
let job = 'Instructor';
Wenn Sie Strings miteinander verketten möchten, können Sie den Operator + verwenden.
let name = "Ryan";
let job = "Instructor";
let sentence = name + " works at HackerYou as an " + job;
console.log(sentence); // "Ryan works at HackerYou as an Instructor"
Mit zunehmender Menge, die verkettet werden muss, wird dieses Muster ziemlich mühsam und unübersichtlich. Hier kommen Template Literals ins Spiel!
Um einen Template-Literal-String zu erstellen, verwenden wir das Backtick ` anstelle der Anführungszeichen.
let name = `Ryan`;
let job = `Instructor`;
Sie verhalten sich exakt wie ein regulärer String-Literal, aber es gibt einen Unterschied. Mit einem Template-Literal wird die Verkettung viel einfacher.
let name = `Ryan`;
let job = `Instructor`;
let sentence = `${name} works at HackerYou as an ${job}`;
console.log(sentence); // "Ryan works at HackerYou as an Instructor"
Beachten Sie die Syntax ${} im String? Dies ist ein Template-Platzhalter. Er ermöglicht es uns, unsere Strings zu schablonieren, und der Browser ersetzt den ${}-Ausdruck zur Laufzeit durch den korrekten Wert. Das macht das Verketten von langen Strings viel angenehmer.
Diese neuen Platzhalter erlauben es Ihnen auch, Ausdrücke darin auszuführen!
const price = 9.99;
const shipping = 3.99;
const message = `Your total with shipping will be ${price + shipping}.`;
console.log(message); // Your total with shipping will be 13.98.
Mehrzeilig
Eine letzte Sache, die wir bei Template Literals betrachten sollten, ist, wie sie mehrzeilige Strings handhaben können. Bei einem regulären String müssten Sie, wenn Sie möchten, dass er sich über mehr als eine Zeile erstreckt, etwas wie folgt tun.
const multi = "This is a \n multiline string";
console.log(multi);
Die Einbeziehung von \n oder des Zeilenumbruchzeichens erzwingt, dass der Text in eine neue Zeile geht. Wenn Sie versuchen würden, den Text einfach auf zwei Zeilen zu setzen, so
const multi = "This is a
multiline string";
console.log(multi);
würde dies zu einem Fehler führen Uncaught SyntaxError: Unexpected token ILLEGAL. Mit Template Literals können wir das jedoch tun und dort Zeilenumbrüche einfügen, wo wir möchten!
const multi = `This is a
multiline string`;
console.log(multi);
Dies ermöglicht es uns, unser Markup auf eine deutlich sauberere Weise zu organisieren!
const person = {
name: 'Ryan',
job: 'Developer/Instructor'
};
const markup = `
<div>
<h2>${person.name}</h2>
<h3>${person.job}</h3>
</div>
`;
Arrow Functions
Arrow Functions sind eine neue Syntax zum Erstellen von Funktionen in ES2015. Dies ersetzt nicht die function() {}-Syntax, die wir kennen und lieben, aber wir werden sie immer häufiger als Standard-Funktionssyntax sehen.
const add = (a, b) => {
return a + b;
};
Der Kern der Syntax ist das Fehlen des Schlüsselworts function beim Definieren einer neuen Funktion. Stattdessen haben wir den => oder fetten Pfeil. Sie können die Funktion genauso aufrufen wie jede andere.
add(2, 3); // 5
Es gibt tatsächlich ein paar Möglichkeiten, die Arrow Function zu definieren. Wenn die Funktion zum Beispiel einfach einen Wert zurückgibt und nichts anderes im Funktionskörper steht, können wir die {} und das Schlüsselwort return weglassen.
const add = (a, b) => a + b;
Das return ist hier implizit, das heißt, es wird angenommen, anstatt dass wir return explizit zu unserem Block hinzufügen müssen. Wenn die Funktion nur einen Parameter hatte, können Sie tatsächlich die () von der Funktionsdefinition weglassen.
const add5 = a => a + 5;
Wenn keine Parameter für die Funktion verwendet werden, werden leere Klammern als Platzhalter verwendet.
const eight = () => 3 + 5;
Oder es entsteht ein neues Muster, bei dem Leute einen _ als Platzhalter anstelle der leeren Klammern verwenden.
const eight = _ => 3 + 5;
Arrow Functions und funktionale Programmierung
Da die Syntax für die Arrow Function so klein ist und die meisten Operationen in der funktionalen Programmierung nur sehr wenige Operationen im Funktionskörper erfordern. Diese Syntax passt perfekt zu diesem Programmierstil!
// Without Arrow functions
const numbers = [3,4,5,6,7,8];
const doubleNumbers = numbers.map(function(n) {
return n * 2;
});
// With arrow functions
const numbers = [3,4,5,6,7,8];
const doubleNumbers = numbers.map( n => n * 2 );
Die Syntax ermöglicht es Ihnen, diese schöne und einfache Operation in eine einzige Zeile zu bringen!
Das Schlüsselwort this
Ein Punkt, auf den man bei der Arbeit mit Arrow Functions achten sollte, ist, wie sie mit dem Schlüsselwort this umgehen. Betrachten Sie eine Methode eines Objekts.
const person = {
firstName: "Ryan",
sayName: function() {
return this.firstName;
}
}
console.log(person.sayName()); // "Ryan"
Innerhalb der Methode sayName ist das Schlüsselwort this an das Objekt person gebunden. Das Ausführen der Methode ergibt also Ryan. Bei einer Arrow Function ist das Schlüsselwort this lexikalisch gebunden. Das bedeutet, dass der Scope der Funktion basierend darauf gebunden wird, wo sie definiert wurde. Der Wert von this bezieht sich dann auf den übergeordneten Scope.
const person = {
firstName: "Ryan",
sayName: () => {
return this.firstName;
}
}
console.log(person.sayName()); // undefined
In diesem Beispiel würde unsere Methode sayName, wenn wir sie von einer anonymen Funktion in eine Arrow Function ändern würden, undefined zurückgeben! Das this wird lexikalisch gebunden, und in diesem Fall ist es das window-Objekt, auf dem keine Eigenschaft firstName vorhanden ist. Es wird Fälle geben, in denen Sie möchten, dass dies das richtige Ergebnis ist! Sehen Sie sich dieses Beispiel an.
const person = {
firstName: 'Ryan',
hobbies: ['Robots', 'Games', 'Internet'],
showHobbies: function() {
this.hobbies.forEach(function(hobby) {
console.log(`${this.firstName} likes ${hobby}`);
});
}
};
person.showHobbies();
Das Ausführen dieses Codes führt zu Uncaught TypeError: Cannot read property 'firstName' of undefined. Das this in der Callback-Funktion für unsere Methode .forEach() ist an nichts gebunden (im Strict Mode, im Non-Strict Mode ist es das window). Aber wenn wir den Callback in eine Arrow Function ändern, können wir das lexikalisch gebundene this verwenden, um den gewünschten Wert zu erhalten!
const person = {
firstName: 'Ryan',
hobbies: ['Robots', 'Games', 'Internet'],
showHobbies: function() {
this.hobbies.forEach(hobby => {
console.log(`${this.firstName} likes ${hobby}`);
});
}
};
person.showHobbies();
Das this innerhalb unseres forEach wird an das Objekt person gebunden!
Spread Operatoren
Manchmal möchten wir etwas mit einem Array tun, das wir nicht können! Nehmen wir zum Beispiel an, wir haben ein Array von Zahlen, von denen wir das Maximum finden möchten. Math.max scheint die richtige Methode dafür zu sein.
const numbers = [39, 25, 90, 123];
const max = Math.max(numbers);
console.log(max); // NaN
Math.max ist eine Methode, die eine kommaseparierte Liste von Werten entgegennimmt und die höchste zurückgibt! Leider können wir ihr kein Array übergeben. Es gibt jedoch einen Weg, dies zu umgehen: Wir können eine Methode namens .apply verwenden, die ein Array entgegennimmt und eine Funktion aufruft, als hätten wir die Werte als Liste übergeben.
const numbers = [39, 25, 90, 123];
const max = Math.max.apply(null, numbers);
console.log(max); // 123
Das erste Argument in .apply ist der Wert, den wir für den this-Wert festlegen möchten, wenn wir Math.max aufrufen. In diesem Beispiel übergeben wir null. Das zweite Argument ist das Array, das wir auf die Funktion anwenden möchten. Das könnte etwas verwirrend sein, gäbe es nicht einen einfacheren Weg, dies zu tun?
Der Spread-Operator kommt ins Spiel
In ES2015 gibt es den Spread-Operator. Die Syntax sieht so aus
...numbers
Was dieses Werkzeug tut, ist, die Elemente aus dem Array zu verteilen oder zu zerstreuen! Es dehnt sie an Ort und Stelle aus. Wir können den obigen .apply-Methodenaufruf jetzt so ändern.
const numbers = [39, 25, 90, 123];
const max = Math.max(...numbers);
console.log(max); // 123
Spread dehnt das Array an Ort und Stelle aus und übergibt die Elemente, als wäre es eine kommaseparierte Liste.
Den Spread-Operator zum Verketten verwenden
Sie können den Spread-Operator auch verwenden, um Arrays miteinander zu verketten! Da Spread Arrays erweitert, können wir Arrays in Arrays erweitern!
const numbersArray1 = [3, 4, 5, 7, 8];
const numbersArray2 = [9, 6, 10, 11];
const concatArray = [...numbersArray1, ...numbersArray2];
console.log(concatArray); // [3, 4, 5, 7, 8, 9, 6, 10, 11]
Rest Parameter
Der Spread-Operator ermöglicht es uns, ein Array von Argumenten an eine Funktion zu übergeben. Auf der anderen Seite ermöglicht uns der Rest-Parameter, die an unsere Funktionen übergebenen Parameter zu sammeln! Genau wie beim Spread-Operator beinhaltet die Syntax des Rest-Parameters ebenfalls ... am Anfang eines Variablennamens.
Betrachten wir ein Beispiel. Stellen wir uns eine Funktion vor, die eine beliebige Anzahl von Argumenten entgegennimmt und deren Summe zurückgibt. add(2, 3, 4, 5, 6, 7) würde 27 zurückgeben.
const add = function() {
const numbers = Array.prototype.slice.call(arguments);
return numbers.reduce((a,b) => a + b);
};
add(2, 3, 4, 5, 6, 7);
Ohne Rest-Parameter müssten wir das Schlüsselwort arguments verwenden und Array.prototype.slice.call(arguments) aufrufen. Was in aller Welt bedeutet Array.prototype.slice.call(arguments)?! arguments ist ein Array-ähnliches Objekt, das heißt, es ist kein echtes Array, sondern eine Sammlung der an eine Funktion übergebenen Argumente. Wenn wir jedoch eine Array-Methode wie .reduce() auf arguments anwenden möchten, müssten wir etwas basteln.
JavaScript ist aus einer Vielzahl von Objekten aufgebaut. Alle diese Objekte haben ein übergeordnetes Objekt, von dem sie ihre Methoden und Eigenschaften erben. Dies tun sie über die Eigenschaft .prototype. Arrays haben die Methode .slice, die wir verwenden können, um ein echtes Array aus unserem arguments-Wert zu erstellen. Mit .call können wir die Methode .slice vom Prototyp aufrufen, wobei arguments als Kontext dient, um ein Array zu erstellen... wow, das ist viel.
Rest Parameter kommen ins Spiel!
const add = function(...numbers) {
return numbers.reduce((a, b) => a + b);
};
add(2, 3, 4, 5, 6, 7);
WOW! Das war viel einfacher. Rest-Parameter erstellen ein echtes Array aus den an eine Funktion übergebenen Argumenten, sodass wir Methoden wie .reduce darauf anwenden können. Dies gibt uns die Freiheit, ähnliche Aufgaben viel einfacher zu erledigen!
Es ist wichtig darauf hinzuweisen, dass Sie Rest-Parameter und den Spread-Operator mischen und anpassen können. Betrachten Sie eine Funktion, die einen Multiplikator als erstes Argument entgegennimmt und dann jeden Wert danach mit dieser Zahl multipliziert.
const multi = (multiplier, ...numbers) => {
return numbers.map(n => n * multiplier);
}
Wir definieren die Funktion mit einem Parameter für den Multiplikator und verwenden Rest-Parameter, um so viele Argumente zu sammeln, wie dieser Funktion übergeben werden!
JavaScript in Bewegung
Es gibt viele Features in ES2015, die wir hier nicht behandelt haben, aber hoffentlich gibt Ihnen dies eine gute Grundlage für einige nützliche neue Syntax und Ergänzungen der Sprache! Wenn Sie mehr erfahren möchten, schauen Sie sich meine Videoserie Let’s Learn ES6 auf YouTube an, sowie letslearnes6.com, wo Sie mehr über ein Buch erfahren können, das ich über ES6 schreibe.
Ich verwende ES6 seit einiger Zeit in der Addon-Entwicklung. Die größte Frage ist natürlich, wann es „sicher“ für die Verwendung im Web ist? Müssen wir warten, bis alle wichtigen Browser mitmachen? Gibt es bereits nutzbare Shim-Bibliotheken (insbesondere für task.jsm) für Nicht-Mozilla-Browser?
Rückwärtskompatibilität ist wie immer die größte Sorge bei einer neuen Technologie wie dieser. Ich habe das gleiche Problem, wenn ich Addons veröffentliche, die auch in Postbox laufen sollen, welches eine veraltete Version der Gecko-Plattform verwendet. Für diese Plattform können einige modernere Features einfach nicht unterstützt werden.
Größtenteils sind Sie jetzt gut aufgestellt, wenn Sie transpilieren! Wenn wir warten würden, bis jede Umgebung die Features unterstützt, würde es viele Jahre dauern, bis wir sie nutzen könnten.
Das gesagt, stellen Sie sicher, dass die Umgebung, in der Sie Ihren transpilierte Code ausführen möchten, diesen Code unterstützt. Babel nimmt Ihren ES2015 (ES6)-Code und konvertiert ihn in ES5, aber wenn der Browser, den Sie benötigen, diesen Code nicht unterstützt. Dann müssen Sie nach anderen Lösungen suchen.
Persönlich ist mein Lieblings-ES6-Feature Default-Parameter. Im Moment muss man if-Anweisungen verwenden, um das Verhalten zu emulieren, z. B.
Aber in ES6 kann man Folgendes verwenden
Viel einfacher :P
Sie können hier mehr über Default-Parameter lesen: https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Functions/default_parameters
Ja! Default-Parameter sind großartig. Ich arbeite bald an einem Video dazu.
Sie haben einen kleinen Tippfehler im Abschnitt über Template-Literale
peron.jobsollte seinperson.jobVielen Dank für Ihren Artikel!
Danke, dass Sie das bemerkt haben, ich werde versuchen, das zu beheben!
Vielen Dank für den großartigen Artikel.
Habe ich das richtig verstanden, dass wir ab jetzt aufhören können, var und die „alte“ Funktionssyntax zu verwenden, oder gibt es Fälle, in denen wir eine dieser beiden verwenden müssten?
Danke Manuel
PS: Sie haben noch einen kleinen Tippfehler. „ES2105“ im allerersten Absatz.
Sie sollten die alte Syntax immer noch hier und da verwenden. In der neuen Arrow-Syntax wird das Schlüsselwort `this` anders gebunden, daher sollten Sie vorsichtig sein, wie Sie damit arbeiten. Mein Video zu Arrow Functions kann dies etwas besser erklären!
Ein Chris-Screencast darüber, wie man Tools wie Babel und den Rest für jemanden wie mich einrichtet, der immer noch nur altes JS in einer script.js-Datei verwendet, wäre großartig.
Das wäre nett!
Ich habe ein Video über die ersten Schritte mit Gulp und Babel, das Ihnen in der Zwischenzeit helfen könnte.
Ich hoffe, das hilft Ihnen weiter!
Gute Zusammenfassung. Ein Vorschlag: Fügen Sie ein einfaches Beispiel für die neue Schleifenkonstruktion for...of für jedes iterierbare Objekt, wie z.B. Arrays, hinzu.