
Wenn ich einen anderen Artikel über wenig bekannte Funktionen der JavaScript- Sprache lese und leise einige verrückte Lösungen in die Browserkonsole pisse, sage ich oft in meinem Kopf, dass es auf dem Produkt sicherlich nicht so ist! Immerhin hat die Sprache seit langem eine große Gemeinschaft und eine überraschend breite Abdeckung der industriellen Entwicklung. Wenn ja, warum vergessen wir dann oft seine Fähigkeit, von jedem verstanden zu werden und fördern buchstäblich all diese spezifischen und „denkwürdigen“ Konstruktionen? Mach es einfach offensichtlich!
Argumentation zum Thema
Sie können diese Graphomanie überspringen.
Wenn wir über industrielle Entwicklung sprechen, ist in den allermeisten Fällen die Anforderung, dass ein Code unterstützt wird, noch wichtiger als die Lösung einer von einem Unternehmen gestellten Aufgabe. Für viele ist dies offensichtlich, für einige - zum Teil (natürlich gibt es auch seltene D'Artagnans). Je klarer unser Code ist, desto geringer ist das Risiko, in das staubige Regal zu gelangen und für uns und unsere Nachfolger Probleme mit dem Nervensystem zu bekommen.
Es ist kein Geheimnis, dass JavaScript in seiner Flexibilität erstaunlich ist, was sowohl seine größte Tugend als auch ein nerviger Fluch ist. Der Weg des JavaScript-Entwicklers ist lang und äußerst interessant: Wir nehmen Buch für Buch, Artikel für Artikel auf und sammeln einzigartige Erfahrungen, aber manchmal ist er wirklich sprachspezifisch. Die breiteste Verbreitung der Sprache und gleichzeitig eine reiche Anzahl von angesammelten und gefütterten Nicht-Offensichtlichkeiten tragen zur Bildung von zwei Fronten bei: diejenigen, die diese Sprache fast vergöttern, und diejenigen, die sie als ungeschickte und schwankende Ente betrachten.
Und alles wäre in Ordnung, aber oft arbeiten Vertreter beider Fronten an demselben Projekt. Und die übliche, alles akzeptierte Praxis ist das Missverständnis (Unwillen, den Code des anderen zu verstehen und sogar zu ignorieren). Und in der Tat: "Ich habe einen Java-Entwickler, und nicht dieser gehört dir!" . Javascript-Anhänger selbst geben dem Feuer Treibstoff und sagen: "Niemand kennt JavaScript wirklich!" ja "Ich kann es in einer Zeile auf js schreiben!" . Ich gebe zu, dass ich selbst abnormale Programmierung in meiner Freizeit missbrauche ...
Sie spüren dieses Problem, wenn Sie an die Stelle eines Randes treten und Erfahrungen mit Menschen und deren Code auf beiden Seiten der Barrikaden sammeln. Planung und andere Besprechungen sind produktiver, wenn sich alle Entwickler nicht nur auf der Ebene der Geschäftsbereiche, sondern zumindest ein wenig auf der Ebene ihrer Implementierung verstehen. Der berüchtigte Bassfaktor hat einen geringeren Einfluss auf das Projekt, wenn der Rest des Teams im Falle einer einzelnen Front-Ender-Krankheit nicht zögert, eine Zeile der .js- Datei zu korrigieren. Der Prozess des Wissensaustauschs innerhalb des Teams und darüber hinaus wird für alle transparenter, wenn alle ein detaillierteres Bild haben. Nun und alles in der gleichen Richtung.
Ich fordere niemanden auf, "Vollglas" oder "T-Form" zu verwenden (wie soll ich das jetzt sagen?). Aber warum ziehen wir diesen Vorhang nicht auch von der JavaScript-Community ein wenig auf? Um dies zu tun, reicht es aus, nur ein wenig Klarheit in unseren Code zu bringen und die Flexibilität der Sprache zu nutzen, um nicht anzugeben, sondern uns zu verstehen.
Aufwachsen und Verantwortung übernehmen
JavaScript seinerseits hat seine Rolle seit langem erkannt, nicht als Sprache für die Interaktivität von Internetseiten und das "Kleben" ihrer Ressourcen, sondern als leistungsstarkes und ausreichendes Werkzeug für die Erstellung vollständiger plattformübergreifender und häufig sehr skalierbarer Anwendungen.
Ursprünglich für Webdesigner entwickelt, tritt diese "am meisten missverstandene Programmiersprache" trotz der schnell wachsenden Popularität und Relevanz seit langem auf dem Wasser. In den 13 bis 14 Jahren vor der Ausgabe von ECMAScript 5.1 ist es schwierig, sich an wichtige Änderungen des Standards zu erinnern oder den Vektor seiner Entwicklung zu verstehen. Zu dieser Zeit leistete seine Community einen großen Beitrag zur Bildung des Ökosystems der Sprache: Prototyp, jQuery, MooTools usw. Nachdem JavaScript dieses Feedback von den Entwicklern erhalten hatte, hat JavaScript erhebliche Arbeit an den Fehlern geleistet: die laute 6-Jahres-Version von ES6 im Jahr 2015 und jetzt die jährliche Veröffentlichung von ECMAScript dank des vom TC39-Komitee neu gestalteten Prozesses zur Einführung neuer Funktionen in die Spezifikation.
Nun, als unsere Anwendungen groß genug wurden, war der Prototyp des OOP-Modells zur Beschreibung von Benutzertypen aufgrund eines ungewöhnlichen Ansatzes nicht mehr gerechtfertigt. Nun im Ernst, was ist das?
function Animal() { } function Rabbit() {} Rabbit.prototype = Object.create(Animal.prototype); Rabbit.prototype.constructor = Rabbit;
Klassen wurden nicht in der Sprache angezeigt, aber ihre Syntax wurde angezeigt. Und der Code ist für Anhänger des traditionellen klassenorientierten Paradigmas verfügbar geworden:
class Animal { constructor() { } } class Rabbit extends Animal {}
Jetzt in der Phase des Kandidaten für die Freigabe sind die privaten Felder der Klasse. Es ist kaum zu glauben, dass wir früher oder später aufhören werden, uns gegenseitig mit einer Vereinbarung über die Benennung von Privateigentum durch Unterstriche auszulachen.
Gleichzeitig ist es in einer Sprache, in der eine Funktion ein Objekt erster Ordnung ist und ein konstantes Ereignis stattfindet, durchaus üblich:
let that = this; setTimeout(function() { that.n += 1; }, 1000);
Und dann beginnen Erklärungen zu diesen Kontexten und zum Schließen in JavaScript, was jeden zweiten externen Entwickler abschreckt. In vielen Fällen vermeidet die Sprache jedoch unnötige Überraschungen, indem sie Function.prototype.bind explizit verwendet oder sogar:
setTimeout(() => this.n += 1, 1000);
Wir haben auch Pfeilfunktionen, und das sind wirklich Funktionen, keine funktionalen Schnittstellen (ja, Java?). Zusammen mit einer erweiterten Reihe von Methoden zum Arbeiten mit einem Array helfen sie auch dabei, die übliche deklarative Gewinnlinie von Berechnungen zu schreiben:
[-1, 2, -3, 4] .filter(x => x > 0) .map(x => Math.pow(2, x)) .reduce((s, x) => s + x, 0);
Die Sprache versteht sich zu Recht als multi-paradigmatisch. Aber hier ist ein einfaches Beispiel für die Signatur einer Funktion:
function ping(host, count) { count = count || 5; }
Zuerst wird eine vorbeikommende Person eine Frage stellen, die besagt, dass eine Funktion wahrscheinlich nur das erste Argument annehmen kann, und dann sagen, was zum Teufel in diesem Fall zählt, wird Boolean !? In der Tat hat die Funktion zwei Verwendungszwecke: mit und ohne. Dies ist jedoch völlig offensichtlich: Sie müssen sich die Implementierung ansehen und verstehen. Die Verwendung von JSDoc kann helfen, dies ist jedoch keine gängige Praxis. Und hier ging JavaScript voran und fügte Unterstützung nicht für Überladung, sondern zumindest für Standardparameter hinzu:
function ping(host, count = 5) { }
Zusammenfassend lässt sich sagen, dass JavaScript eine Vielzahl vertrauter Dinge enthält: Generatoren, Iteratoren, Set- Sammlungen und Map- Wörterbücher, typisierte Arrays und sogar reguläre Ausdrücke, die mit Unterstützung von Lookbehind zufrieden waren ! Die Sprache tut alles, um für viele Dinge geeignet zu sein und für alle freundlich zu werden.
Günstiger Weg zum Offensichtlichen
Die Sprache selbst ist sicherlich gut gemacht, und es ist schwer, damit zu streiten! Aber was ist los mit uns? Warum erinnern wir die ganze Welt ständig daran, dass JavaScript irgendwie anders ist? Schauen wir uns Beispiele einiger weit verbreiteter Techniken an und fragen Sie nach ihrer Angemessenheit.
Typ Casting
Ja, JavaScript hat ein dynamisches und schwaches Typsystem und ermöglicht es Ihnen, Operationen an allem durchzuführen und implizit Transformationen für uns durchzuführen. Oft ist für uns jedoch noch ein explizites Casting erforderlich, und Folgendes kann beobachtet werden:
let bool = !!(expr); let numb = +(expr); let str = ''+(expr);
Diese Tricks sind jedem JavaScript-Entwickler bekannt und sie sind durch die Tatsache motiviert, dass man "schnell" etwas in etwas verwandeln kann: Mit Geschwindigkeit ist hier eine kurze Aufzeichnung gemeint. Kann es auch sofort falsch schreiben als ! 1 ? Wenn der Entwickler über die druckbaren Zeichen so besorgt ist, können Sie in seiner bevorzugten IDE problemlos die erforderliche Live-Vorlage konfigurieren oder automatisch vervollständigen. Und wenn - für die Größe des veröffentlichten Codes - wir ihn immer durch den Verschleierer laufen lassen, der besser als wir weiß, wie man all dies entmenschlicht. Warum nicht:
let bool = Boolean(expr); let numb = Number(expr); let str = String(expr);
Das Ergebnis ist das gleiche, nur für alle klar.
Für String-Konvertierungen haben wir toString , aber für numerische gibt es einen interessanten valueOf , der auch überschrieben werden kann. Ein klassisches Beispiel, das das "Uneingeweihte" in einen Stupor einführt:
let timestamp = +new Date;
Aber Date hat eine bekannte getTime- Methode. Verwenden wir sie:
let timestamp = (new Date()).getTime();
oder fertige Funktion:
let timestamp = Date.now();
Es ist absolut nicht erforderlich, die implizite Typkonvertierung auszunutzen.
Logische Operatoren
Besonderes Augenmerk wird auf die logischen Operatoren AND (&&) und OR (||) gelegt, die in JavaScript nicht ganz logisch sind: Sie akzeptieren und geben Werte aller Art zurück. Wir werden nicht auf die Funktionsweise des Rechners für logische Ausdrücke eingehen, sondern Beispiele betrachten. Die zuvor vorgestellte Option mit der Funktion:
function ping(host, count) { count = count || 5; }
Es könnte so aussehen:
function ping(host, count) {
Eine solche Überprüfung ist vertrauter und kann in einigen Fällen dazu beitragen, Fehler zu vermeiden.
Vielmehr erscheint es dem Entwickler, der ursprünglich den JavaScript-Pfad gewählt hat, wild. Aber für die meisten anderen ist dieser Code wirklich wild:
var root = (typeof self == 'object' && self.self === self && self) || (typeof global == 'object' && global.global === global && global);
Ja, es ist kompakt und beliebte Bibliotheken können es sich leisten. Aber bitte, lasst es uns nicht missbrauchen, denn unser Code wird nicht von Mitwirkenden in JavaScript gelesen, sondern von Entwicklern, die geschäftliche Probleme innerhalb des Zeitrahmens lösen.
Ein solches Muster kann überhaupt auftreten:
let count = typeof opts == 'object' && opts.count || 5;
Dies ist definitiv kürzer als der übliche ternäre Operator, aber wenn Sie einen solchen Code lesen, erinnern Sie sich zuerst an die Prioritäten der verwendeten Operationen.
Wenn wir eine Prädikatfunktion schreiben, die wir an denselben Array.prototype.filter übergeben , ist es ein guter Ton, den Rückgabewert in Boolean zu verpacken. Der Zweck dieser Funktion wird sofort ersichtlich und es gibt keine Dissonanz zwischen Entwicklern, deren Sprachen die "richtigen" logischen Operatoren haben.
Bitweise Operationen
Ein häufiges Beispiel für die Überprüfung des Vorhandenseins eines Elements in einem Array oder einer Teilzeichenfolge in einer Zeichenfolge mithilfe von bitweisem NOT (NOT), das selbst in einigen Tutorials angeboten wird:
if (~[1, 2, 3].indexOf(1)) { console.log('yes'); }
Welches Problem löst das? Wir müssen nicht prüfen ! == -1 , da indexOf den Index des Elements oder -1 erhält und die Tilde 1 hinzufügt und das Vorzeichen ändert. Somit wird der Ausdruck im Fall von Index -1 zu einem "falschen".
Eine Duplizierung von Code kann jedoch auf andere Weise vermieden werden: Sie können wie jeder andere auch eine Prüfung in eine separate Funktion eines Utils-Objekts einfügen, anstatt bitweise Operationen für andere Zwecke zu verwenden. Dafür gibt es in lodash eine Includes- Funktion, die nicht funktioniert Arschfick Tilde. Sie können sich freuen, denn in ECMAScript 2016 wurde die Methode Array.prototype.includes behoben (die Zeilen haben auch eine).
Aber da war es! Eine andere Tilde (zusammen mit XOR) wird verwendet, um Zahlen zu runden und den Dezimalteil zu verwerfen:
console.log(~~3.14);
Für diese Zwecke gibt es jedoch parseInt oder Math.floor . Bitweise Operationen sind hier praktisch, um schnell Code in die Konsole einzugeben, da sie auch eine niedrige Priorität gegenüber dem Rest der Arithmetik haben. Bei einer Codeüberprüfung ist es jedoch besser, sie nicht zu verpassen.
Syntax- und Sprachkonstrukte
Einige seltsame Praktiken lassen sich nur schwer einem bestimmten Abschnitt zuordnen. Sie sagen beispielsweise, dass Klammern beim Aufrufen des Konstruktors optional sind und die folgenden zwei Ausdrücke identisch sind:
let rabbit = new Rabbit(); let rabbit = new Rabbit;
Und das ist es wirklich! aber warum eine Frage von Grund auf neu erstellen? Nicht jede Sprache kann sich einer solchen "Funktion" rühmen. Und wenn Sie es trotzdem wollen, dann lassen Sie es eine Vereinbarung über das gesamte Projekt sein. Ansonsten gibt es ein falsches Gefühl, dass es einen Unterschied gibt.
Eine ähnliche Situation mit der Deklaration einer Reihe von Variablen. Mit der Syntax der Direktiven var und let können Sie mehrere Variablen gleichzeitig deklarieren (und definieren), getrennt durch Kommas:
let count = 5, host, retry = true;
Jemand verwendet Zeilenvorschübe zur besseren Lesbarkeit, aber in jedem Fall ist diese Syntax in gängigen Sprachen nicht üblich. Niemand wird helfen und fragen, ob Sie so schreiben:
let count = 5; let retry = true; let host;
Auch hier gibt es keine Fragen, wenn auf Projekt- / Unternehmensebene eine Einigung über guten Stil erzielt wird. Es ist nur so, dass Sie nicht zu viel Syntax für Ihre Stimmung kombinieren müssen.
In der Sprache gibt es bestimmte Konstruktionen, z. B. IIFE, mit denen Sie eine Funktion sofort an der Stelle ihrer Definition aufrufen können. Der Trick besteht darin, dass der Parser einen Funktionsausdruck erkennt, keine Funktionsdeklaration. Und dies kann auf viele verschiedene Arten geschehen: klassisch in Klammern einwickeln, durch void oder einen anderen unären Operator. Und daran ist nichts Wunderbares! Es ist notwendig, die einzige Option zu wählen und sie nicht ohne die Notwendigkeit zu lassen:
(function() { }());
Sie müssen keine Operatoren verwenden, um den Parser zu hacken. Wenn ein Neuling zu dem Projekt kommt, möchte ich es in die Geschäftslogik der Anwendung eintauchen und es nicht mit Erklärungen versehen, von denen all diese Ausrufezeichen und Lücken ausspioniert wurden. Es gibt auch einen zweiten klassischen Eintrag in Klammern und einen interessanten Kommentar von Crockford zu diesem Thema.
Das Auftreten der Klassensyntax in ES6 wurde nicht von den üblichen Zugriffsmodifikatoren begleitet. Und manchmal möchte der Entwickler in die Klassen pinkeln und die Privatsphäre beachten. Was zu diesem Frankenstein-Code führt:
class Person { constructor(name) { let _name = name; this.getName = function() { return _name; } } toString() { return `Hello, ${this.getName()}`; } }
Das heißt, Accessoren werden für die Instanz im Konstruktor erstellt, und die Privatsphäre wird durch ihren Zugriff auf lokale Eigenschaftsvariablen durch Schließen erreicht. Dieses Beispiel sieht sogar nach Lacconcino aus, aber dies ist ein völlig unskalierbarer Ansatz, es sei denn, Sie erstellen eine dokumentierte Framework-Lösung. Meine Herren, verwenden wir entweder die verfügbaren Klassen (und warten auf die Standardisierung privater Felder) oder das beliebte Mustermodul. Hier eine Art Zwischenmischungslösung zu erstellen, ist so etwas für Sie, da Klassen keine Klassen mehr sind und der Code verständlich ist.
Zusammenfassend wird er seinen gesunden Menschenverstand mit dem im Projekt verwendeten Styleguide, der Konfiguration für den Linter oder einfach mit Codefragmenten mit Kollegen teilen, die seine Nicht-JavaScript-Komponente zum Projekt beitragen. Die Sprache bietet buchstäblich für jede typische Aufgabe mehrere Optionen, sodass es nicht schwierig (oder fast) ist, das gegenseitige Verständnis zu verbessern und unter einen gemeinsamen Nenner zu fallen.
Missgeschick
Dieses Thema ist sicherlich ganzheitlich und es gibt viel mehr Beispiele, aber die Hauptbotschaft des Artikels ist, dass Sie die Nicht-Offensichtlichkeit in JavaScript nicht missbrauchen sollten, wenn dies vermieden werden kann. Die Art der Sprache ist einzigartig: Sie ermöglicht es Ihnen, sowohl elegante als auch ausdrucksstarke (mäßig "spitze") Lösungen zu schreiben, die verständlich und für jedermann zugänglich sind. Ich bin grundsätzlich nicht mit der herkömmlichen Weisheit einverstanden, dass JavaScript "sich selbst bestraft" oder "unter einem Haufen guter Absichten und Fehler begraben" ist. Denn jetzt zeigt sich der größte Teil der Fremdheit nicht in der Sprache, sondern in der Kultur der Entwickler und (nicht) gleichgültig um sie herum.