JavaScript und TypeScript: 11 Kompakte Konstrukte, die Sie kennen sollten

Es gibt eine sehr feine Linie zwischen sauberem, effizientem Code und Code, die nur der Autor verstehen kann. Und das Schlimmste ist, dass es unmöglich ist, diese Linie klar zu definieren. Einige Programmierer, die danach suchen, sind bereit, viel weiter zu gehen als andere. Wenn Sie einen bestimmten Code so erstellen müssen, dass er garantiert von jedem verstanden wird, versuchen sie in diesem Code normalerweise, nicht alle Arten von kompakten Konstruktionen wie ternäre Operatoren und einzeilige Pfeilfunktionen zu verwenden.



Aber die Wahrheit, die unangenehme Wahrheit ist, dass diese kompakten Designs oft sehr nützlich sind. Und sie sind gleichzeitig ganz einfach. Dies bedeutet, dass jeder, der an dem Code interessiert ist, in dem er verwendet wird, ihn beherrschen und diesen Code verstehen kann.







In diesem Beitrag werde ich einige sehr nützliche (und manchmal kryptische) kompakte Konstrukte betrachten, die Sie möglicherweise in JavaScript und TypeScript finden. Nachdem Sie sie studiert haben, können Sie sie selbst verwenden oder zumindest den Code der Programmierer verstehen, die sie verwenden.



1. Betreiber?



Der Operator zum Überprüfen der Werte für nullund undefined(nullish coalescing operator) sieht aus wie zwei Fragezeichen ( ??). Es ist kaum zu glauben, dass dies mit diesem und jenem Namen der beliebteste Betreiber ist. Wahr?



Die Bedeutung dieses Operators ist, dass er den Wert des rechten Operanden zurückgibt, wenn der Wert des linken gleich nulloder ist undefined. Dies spiegelt sich nicht ganz klar in seinem Namen wider, aber na ja, das heißt - das heißt. So verwenden Sie es:



function myFn(variable1, variable2) {
  let var2 = variable2 ?? "default value"
  return variable1 + var2
}

myFn("this has ", "no default value") // "this has no default value"
myFn("this has no ") // "this has no default value"
myFn("this has no ", 0) // "this has no 0"


Hierbei handelt es sich um Mechanismen, die denen zur Organisation der Arbeit des Bedieners sehr ähnlich sind ||. Wenn die linke Seite des Ausdrucks gleich nulloder ist undefined, wird die rechte Seite des Ausdrucks zurückgegeben. Andernfalls wird die linke Seite zurückgegeben. Infolgedessen eignet sich der Operator ??hervorragend für Situationen, in denen einer Variablen alles zugewiesen werden kann. Sie müssen jedoch Maßnahmen ergreifen, falls diese Variable in diese Variable fällt nulloder in diese fällt undefined.



2. Betreiber ?? =



Ein Operator, der verwendet wird, um einer Variablen nur dann einen Wert zuzuweisen, wenn er einen Wert hat nulloder undefined(logischer Null-Zuweisungsoperator) wie zwei Fragezeichen gefolgt von einem Gleichheitszeichen ( ??=) aussieht . Stellen Sie sich das als Erweiterung des obigen Operators vor ??.



Werfen wir einen Blick auf das vorherige Code-Snippet, das mit neu geschrieben wurde ??=.



function myFn(variable1, variable2) {
  variable2 ??= "default value"
  return variable1 + variable2
}

myFn("this has ", "no default value") // "this has no default value"
myFn("this has no ") // "this has no default value"
myFn("this has no ", 0) // "this has no 0"


Mit dem Operator ??=können Sie den Wert eines Funktionsparameters überprüfen variable2. Wenn es gleich nulloder ist undefined, wird ein neuer Wert darauf geschrieben. Andernfalls ändert sich der Parameterwert nicht.



Beachten Sie, dass das Design ??=für diejenigen, die es nicht kennen, möglicherweise unverständlich erscheint. Wenn Sie es verwenden, möchten Sie daher möglicherweise an der entsprechenden Stelle im Code einen kurzen Kommentar mit Erläuterungen hinzufügen.



3. Abgekürzte Deklaration von TypeScript-Konstruktoren



Diese Funktion ist spezifisch für TypeScript. Wenn Sie sich für JavaScript-Reinheit einsetzen, fehlt Ihnen daher viel. (Natürlich nur ein Scherz, aber das gilt wirklich nicht für reguläres JS).



Wie Sie wissen, listen sie beim Deklarieren einer Klasse normalerweise alle ihre Eigenschaften mit Zugriffsmodifikatoren auf und weisen diesen Eigenschaften dann im Klassenkonstruktor Werte zu. In Fällen, in denen der Konstruktor sehr einfach ist und die Werte der an den Konstruktor übergebenen Parameter einfach in die Eigenschaften geschrieben werden, können Sie eine Konstruktion verwenden, die kompakter als die übliche ist.



So sieht es aus:



// ...
class Person {
  
  private first_name: string;
  private last_name: string;
  private age: number;
  private is_married: boolean;
  
  constructor(fname:string, lname:string, age:number, married:boolean) {
    this.first_name = fname;
    this.last_name = lname;
    this.age = age;
    this.is_married = married;
  }
}

// ,   ...
class Person {

  constructor( private first_name: string,
               private last_name: string,
               private age: number,
               private is_married: boolean){}
}


Die Verwendung des obigen Ansatzes beim Erstellen von Konstruktoren hilft definitiv, Zeit zu sparen. Besonders wenn es um eine Klasse geht, die viele Eigenschaften hat.



Die Hauptsache hier ist nicht zu vergessen, {}unmittelbar nach der Beschreibung des Konstruktors hinzuzufügen , da dies eine Darstellung des Funktionskörpers ist. Nachdem der Compiler auf eine solche Beschreibung gestoßen ist, versteht er alles und erledigt den Rest selbst. Tatsächlich sprechen wir über die Tatsache, dass sowohl das erste als auch das zweite Fragment des TS-Codes schließlich in denselben JavaScript-Code konvertiert werden.



4. Ternärer Operator



Der ternäre Operator ist ein Konstrukt, das leicht zu lesen ist. Dieser Operator wird häufig anstelle von kurzen Anweisungen verwendet if…else, da Sie damit zusätzliche Zeichen entfernen und ein mehrzeiliges Konstrukt in ein einzeiliges Konstrukt verwandeln können.



//   if…else
let isEven = ""
if(variable % 2 == 0) {
  isEven = "yes"
} else {
  isEven = "no"
}

//  
let isEven = (variable % 2 == 0) ? "yes" : "no"


In der Struktur eines ternären Operators ist der erste ein logischer Ausdruck, der zweite ist so etwas wie ein Befehl return, der einen Wert zurückgibt, wenn der logische Ausdruck wahr ist, und der dritte ist auch so etwas wie ein Befehl return, der einen Wert zurückgibt, wenn der logische Ausdruck falsch ist. Obwohl der ternäre Operator am besten auf der rechten Seite von Wertzuweisungen verwendet wird (wie im Beispiel), kann er auch autonom als Mechanismus zum Aufrufen von Funktionen verwendet werden, wenn welche Funktion aufgerufen wird oder mit welchen Argumenten ein und dieselbe aufgerufen wird Die gleiche Funktion wird durch den Wert des logischen Ausdrucks bestimmt. So sieht es aus:



let variable = true;

(variable) ? console.log("It's TRUE") : console.log("It's FALSE")


Beachten Sie, dass die Struktur der Anweisung genauso aussieht wie im vorherigen Beispiel. Der Nachteil der Verwendung des ternären Operators besteht darin, dass, wenn in Zukunft einer seiner Teile erweitert werden muss (entweder derjenige, der sich auf den wahren Wert des logischen Ausdrucks bezieht, oder derjenige, der sich auf seinen falschen Wert bezieht), die Notwendigkeit besteht, zur üblichen Anweisung zu wechseln if…else.



5. Verwenden eines vom Bediener verwendeten kurzen Berechnungszyklus ||



In JavaScript (und auch in TypeScript ||) implementiert der logische ODER-Operator ( ) ein Shorthand-Computing-Modell. Das heißt, es gibt den ersten Ausdruck zurück, der als ausgewertet wird true, und überprüft die verbleibenden Ausdrücke nicht.



Dies bedeutet, dass wenn es die folgende Anweisung gibt if, in der der Ausdruck expression1einen falschen Wert (reduzierbar auf false) und expression2- wahr (reduzierbar auf true) enthält, nur expression1und berechnet wird expression2. Ausdrücke espression3und expression4werden nicht ausgewertet.



if( expression1 || expression2 || expression3 || expression4)


Wir können diese Gelegenheit außerhalb der Anweisung nutzen if, wo wir Variablen Werte zuweisen. Dies ermöglicht es insbesondere, einen Standardwert in eine Variable zu schreiben, falls sich herausstellt, dass ein Wert, der beispielsweise durch einen Funktionsparameter dargestellt wird, falsch ist (z. B. gleich undefined):



function myFn(variable1, variable2) {
  let var2 = variable2 || "default value"
  return variable1 + var2
}

myFn("this has ", " no default value") // "this has no default value"
myFn("this has no ") // "this has no default value"


Dieses Beispiel zeigt, wie Sie mit einem Operator ||entweder den Wert des zweiten Parameters einer Funktion oder einen Standardwert in eine Variable schreiben können. Wenn Sie sich dieses Beispiel jedoch genau ansehen, sehen Sie ein kleines Problem darin. Tatsache ist, dass, wenn variable2ein Wert in 0oder eine leere Zeichenfolge vorhanden ist, der var2Standardwert beschrieben wird, da sowohl und 0als auch die leere Zeichenfolge in konvertiert werden false.



Wenn Sie in Ihrem Fall nicht alle falschen Werte durch den Standardwert ersetzen müssen, können Sie daher auf einen weniger bekannten Operator zurückgreifen ??.



6. Doppelter bitweiser Operator ~



JavaScript-Entwickler sind normalerweise nicht besonders daran interessiert, bitweise Operatoren zu verwenden. Wen interessieren heutzutage binäre Darstellungen von Zahlen? Tatsache ist jedoch, dass diese Operatoren, da sie auf Bitebene arbeiten, die entsprechenden Aktionen viel schneller ausführen als beispielsweise einige Methoden.



Wenn wir über den bitweisen Operator NOT ( ~) sprechen , nimmt er eine Zahl, konvertiert sie in eine 32-Bit-Ganzzahl (verwirft die "zusätzlichen" Bits) und invertiert die Bits dieser Zahl. Dadurch wird der Wert xin einen Wert umgewandelt -(x+1). Warum interessieren wir uns für eine solche Umrechnung von Zahlen? Und die Tatsache, dass bei zweimaliger Verwendung das gleiche Ergebnis wie bei einem Methodenaufruf erzielt wird Math.floor.



let x = 3.8
let y = ~x // x   -(3 + 1),    ,    
let z = ~y //  y ( -4)  -(-4 + 1)   -  3

//   :

let flooredX = ~~x //      


Beachten Sie die beiden Symbole ~in der letzten Zeile des Beispiels. Es mag seltsam aussehen, aber wenn Sie viele Gleitkommazahlen in Ganzzahlen konvertieren müssen, kann diese Technik sehr nützlich sein.



7. Zuweisen von Werten zu Objekteigenschaften



Die Funktionen des ES6-Standards vereinfachen das Erstellen von Objekten und insbesondere das Zuweisen von Werten zu ihren Eigenschaften. Wenn Eigenschaftswerte basierend auf Variablen zugewiesen werden, die dieselben Namen wie diese Eigenschaften haben, müssen diese Namen nicht wiederholt werden. Zuvor war es notwendig.



Hier ist ein Beispiel in TypeScript.



let name:string = "Fernando";
let age:number = 36;
let id:number = 1;

type User = {
  name: string,
  age: number,
  id: number
}

// 
let myUser: User = {
  name: name,
  age: age,
  id: id
}

// 
let myNewUser: User = {
  name,
  age,
  id
}


Wie Sie sehen, können Sie mit dem neuen Ansatz zum Zuweisen von Werten zu Objekteigenschaften kompakteren und einfacheren Code schreiben. Und ein solcher Code ist nicht schwieriger zu lesen als Code, der nach den alten Regeln geschrieben wurde (was nicht über den Code gesagt werden kann, der mit anderen in diesem Artikel beschriebenen kompakten Konstrukten geschrieben wurde).



8. Implizite Rückgabe von Werten aus Pfeilfunktionen



Wussten Sie, dass einzeilige Pfeilfunktionen die Ergebnisse von Berechnungen zurückgeben, die in ihrer einzelnen Zeile ausgeführt wurden?



Mit diesem Mechanismus können Sie unnötige Ausdrücke entfernen return. Diese Technik wird häufig in Pfeilfunktionen verwendet, die an Array-Methoden wie filteroder übergeben werden map. Hier ist ein TypeScript-Beispiel:



let myArr:number[] = [1,2,3,4,5,6,7,8,9,10]

//  :
let oddNumbers:number[] = myArr.filter( (n:number) => {
  return n % 2 == 0
})

let double:number[] = myArr.map( (n:number) => {
  return n * 2;
})

//  :
let oddNumbers2:number[] = myArr.filter( (n:number) => n % 2 == 0 )

let double2:number[] = myArr.map( (n:number) =>  n * 2 )


Die Anwendung dieser Technik bedeutet nicht unbedingt, den Code komplexer zu gestalten. Konstrukte wie diese sind eine gute Möglichkeit, Ihren Code ein wenig zu bereinigen und unnötige Leerzeichen und zusätzliche Zeilen zu entfernen. Der Nachteil dieses Ansatzes ist natürlich, dass Sie, wenn die Körper mit solch kurzen Funktionen erweitert werden müssen, wieder geschweifte Klammern verwenden müssen.



Die einzige Besonderheit, die hier berücksichtigt werden muss, ist, dass das, was in der einzigen Zeile der hier betrachteten Kurzpfeilfunktionen enthalten ist, ein Ausdruck sein muss (das heißt, es muss ein Ergebnis erzeugt werden, das von der Funktion zurückgegeben werden kann). Andernfalls ist ein solches Design nicht funktionsfähig. Zum Beispiel können die obigen einzeiligen Funktionen nicht so geschrieben werden:



const m = _ => if(2) console.log("true"else console.log("false")


Im nächsten Abschnitt werden wir weiter über einzeilige Pfeilfunktionen sprechen, aber jetzt werden wir über Funktionen sprechen, die ohne geschweifte Klammern nicht erstellt werden können.



9. Funktionsparameter, die Standardwerte haben können



Mit ES6 wurde die Möglichkeit eingeführt, Werte anzugeben, die Standardfunktionsparametern zugewiesen sind. Bisher verfügte JavaScript nicht über solche Funktionen. In Situationen, in denen es notwendig war, Parametern ähnliche Werte zuzuweisen, musste daher auf so etwas wie ein Modell reduzierter Operatorberechnungen zurückgegriffen werden ||.



Aber jetzt kann das gleiche Problem ganz einfach gelöst werden:



//    2  
//     ,   
function myFunc(a, b, c = 2, d = "") {
  //   ...
}


Einfacher Mechanismus, richtig? Tatsächlich ist aber alles noch interessanter, als es auf den ersten Blick scheint. Der Punkt ist, dass der Standardwert alles sein kann - einschließlich eines Funktionsaufrufs. Diese Funktion wird aufgerufen, wenn der entsprechende Parameter beim Aufruf der Funktion nicht an sie übergeben wird. Dies erleichtert die Implementierung des erforderlichen Funktionsparameter-Musters:



const mandatory = _ => {
  throw new Error("This parameter is mandatory, don't ignore it!")
}

function myFunc(a, b, c = 2, d = mandatory()) {
  //    ...
}

// !
myFunc(1,2,3,4)

// 
myFunc(1,2,3)


Hier ist in der Tat die einzeilige Pfeilfunktion, auf die Sie beim Erstellen nicht ohne geschweifte Klammern verzichten können. Der Punkt ist, dass die Funktion mandatoryeine Anweisung verwendet throw. Achten Sie darauf - "Anweisung", nicht "Ausdruck". Aber ich nehme an, dies ist nicht der höchste Preis für die Fähigkeit, Funktionen mit den erforderlichen Parametern auszustatten.



10. Umwandeln von Werten in einen booleschen Typ mit !!



Dieser Mechanismus arbeitet nach dem gleichen Prinzip wie die obige Konstruktion ~~. Wir sprechen nämlich über die Tatsache, dass Sie zwei logische Operatoren NOT ( !!) verwenden können, um einen Wert in einen logischen Typ umzuwandeln :



!!23 // TRUE
!!"" // FALSE
!!0 // FALSE
!!{} // TRUE


Ein Operator !löst bereits den größten Teil dieses Problems, dh er konvertiert den Wert in einen booleschen Typ und gibt dann den entgegengesetzten Wert zurück. Und der zweite Operator !nimmt, was passiert ist, und gibt nur das Gegenteil davon zurück. Als Ergebnis erhalten wir den ursprünglichen Wert, der in einen booleschen Typ konvertiert wurde.



Diese kurze Konstruktion kann in einer Vielzahl von Situationen nützlich sein. Erstens, wenn Sie sicherstellen müssen, dass einer Variablen ein realer boolescher Wert zugewiesen wird (z. B. wenn es sich um eine TypeScript-Variable vom Typ handelt boolean). Zweitens, wenn Sie einen strengen Vergleich (mit ===) von etwas mit trueoder durchführen müssen false.



11. Syntax zerstören und verbreiten



Über die im Titel dieses Abschnitts genannten Mechanismen kann gesprochen und gesprochen werden. Der Punkt ist, dass es bei richtiger Anwendung zu sehr interessanten Ergebnissen führen kann. Aber hier werde ich nicht zu tief gehen. Ich werde Ihnen erklären, wie Sie sie verwenden können, um die Lösung einiger Probleme zu vereinfachen.



▍Destrukturierung von Objekten



Haben Sie jemals die Aufgabe gehabt, mehrere Werte von Objekteigenschaften in gewöhnliche Variablen zu schreiben? Diese Aufgabe ist ziemlich häufig. Zum Beispiel, wenn es notwendig ist, mit diesen Werten zu arbeiten (indem Sie sie beispielsweise ändern) und gleichzeitig nicht zu beeinflussen, was im ursprünglichen Objekt gespeichert ist.



Die Verwendung der Objektzerstörung ermöglicht es Ihnen, ähnliche Probleme mit der minimalen Menge an Code zu lösen:



const myObj = {
  name: "Fernando",
  age: 37,
  country: "Spain"
}

// :
const name = myObj.name;
const age = myObj.age;
const country = myObj.country;

// 
const {name, age, country} = myObj;


Jeder, der TypeScript verwendet hat, hat diese Syntax in der Anleitung gesehen import. Sie können einzelne Bibliotheksmethoden importieren, ohne den Projektnamespace mit vielen unnötigen Funktionen zu kontaminieren:



import { get } from 'lodash'


Mit dieser Anweisung können Sie beispielsweise lodashnur eine Methode aus der Bibliothek importieren get. Gleichzeitig fallen andere Methoden dieser Bibliothek nicht in den Namespace des Projekts. Und es gibt viele davon.



▍ Verbreitete Syntax und Erstellen neuer Objekte und Arrays basierend auf vorhandenen



Die Verwendung der Spread ( ) -Syntax vereinfacht das Erstellen neuer Arrays und Objekte basierend auf vorhandenen. Jetzt kann diese Aufgabe gelöst werden, indem buchstäblich eine Codezeile geschrieben wird und keine speziellen Methoden angewendet werden. Hier ist ein Beispiel:



const arr1 = [1,2,3,4]
const arr2 = [5,6,7]

const finalArr = [...arr1, ...arr2] // [1,2,3,4,5,6,7]

const partialObj1 = {
  name: "fernando"
}
const partialObj2 = {
  age:37
}

const fullObj = { ...partialObj1, ...partialObj2 } // {name: "fernando", age: 37}


Beachten Sie, dass bei Verwendung dieses Ansatzes zum Kombinieren von Objekten deren Eigenschaften mit demselben Namen überschrieben werden. Dies gilt nicht für Arrays. Insbesondere wenn die zusammenzuführenden Arrays dieselben Werte haben, landen sie alle im resultierenden Array. Wenn Sie Wiederholungen entfernen müssen, können Sie auf die Verwendung einer Datenstruktur zurückgreifen Set.



▍Kombinieren von Destrukturierung und Spread-Syntax



Die Destrukturierung kann in Verbindung mit der Spread-Syntax verwendet werden. So erzielen Sie einen interessanten Effekt. Entfernen Sie beispielsweise das erste Element des Arrays und lassen Sie den Rest unberührt (wie im allgemeinen Beispiel mit dem ersten und letzten Element der Liste, dessen Implementierung in Python und anderen Sprachen zu finden ist). Außerdem können Sie beispielsweise einige Eigenschaften aus einem Objekt extrahieren und den Rest unberührt lassen. Betrachten wir ein Beispiel:



const myList = [1,2,3,4,5,6,7]
const myObj = {
  name: "Fernando",
  age: 37,
  country: "Spain",
  gender: "M"
}

const [head, ...tail] = myList

const {name, age, ...others} = myObj

console.log(head) //1
console.log(tail) //[2,3,4,5,6,7]
console.log(name) //Fernando
console.log(age) //37
console.log(others) //{country: "Spain", gender: "M"}


Beachten Sie, dass die drei Punkte auf der linken Seite der Zuweisungsanweisung für das allerletzte Element gelten müssen. Sie können nicht zuerst die Spread-Syntax verwenden und dann einzelne Variablen beschreiben:



const [...values, lastItem] = [1,2,3,4]


Dieser Code funktioniert nicht.



Ergebnis



Es gibt viel mehr Designs, die denen ähneln, über die wir heute gesprochen haben. Wenn Sie sie verwenden, sollten Sie jedoch bedenken, dass es für jemanden, der nicht an die darin verwendeten abgekürzten Konstruktionen gewöhnt ist, umso schwieriger ist, sie zu lesen, je kompakter der Code ist. Der Zweck der Verwendung solcher Konstrukte besteht nicht darin, den Code zu minimieren oder zu beschleunigen. Dieses Ziel besteht nur darin, unnötige Strukturen aus dem Code zu entfernen und denjenigen, die diesen Code lesen, das Leben zu erleichtern.



Um alle zufrieden zu stellen, wird daher empfohlen, ein ausgewogenes Verhältnis zwischen kompakten Konstrukten und regelmäßig lesbarem Code aufrechtzuerhalten. Denken Sie immer daran, dass Sie nicht die einzige Person sind, die Ihren Code liest.



Welche kompakten Konstrukte verwenden Sie in JavaScript- und TypeScript-Code?










All Articles