Mais la vérité, la vérité désagréable, c'est que ces conceptions compactes sont souvent très utiles. Et ils sont, en même temps, assez simples. Cela signifie que toute personne intéressée par le code dans lequel ils sont utilisés peut les maîtriser et comprendre ce code.
Dans cet article, je vais jeter un oeil à quelques constructions compactes très utiles (et parfois cryptiques) que vous pourriez trouver dans JavaScript et TypeScript. Après les avoir étudiés, vous pouvez les utiliser vous-même, ou du moins comprendre le code de ces programmeurs qui les utilisent.
1. Opérateur ??
L'opérateur de vérification des valeurs pour
null
et undefined
(opérateur de fusion nul) ressemble à deux points d'interrogation ( ??
). Il est difficile de croire que celui-ci, avec tel ou tel nom, soit l'opérateur le plus populaire. Vrai?
La signification de cet opérateur est qu'il renvoie la valeur de l'opérande de droite si la valeur de l'opérande de gauche est égale à
null
ou undefined
. Ce n'est pas clairement reflété dans son nom, mais bon, qu'est-ce que c'est. Voici comment l'utiliser:
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"
Il s'agit ici de mécanismes très similaires à ceux utilisés pour organiser le travail de l'opérateur
||
. Si le côté gauche de l'expression est égal à null
ou undefined
, le côté droit de l'expression sera renvoyé. Sinon, le côté gauche sera retourné. En conséquence, l'opérateur est ??
idéal pour une utilisation dans des situations où tout peut être affecté à une variable, mais vous devez prendre des mesures dans le cas où null
ou tomberait dans cette variable undefined
.
2. Opérateur ?? =
Un opérateur utilisé pour attribuer une valeur à une variable uniquement si elle a une valeur
null
ou undefined
(opérateur d'assignation logique nulle) ressemble à deux points d'interrogation suivis d'un signe égal ( ??=
). Considérez-le comme une extension de l'opérateur ci-dessus ??
.
Jetons un coup d'œil à l'extrait de code précédent, réécrit à l'aide de
??=
.
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"
L'opérateur
??=
vous permet de vérifier la valeur d'un paramètre de fonction variable2
. S'il est égal à null
ou undefined
, il y écrira une nouvelle valeur. Sinon, la valeur du paramètre ne changera pas.
Gardez à l'esprit que le design
??=
peut sembler incompréhensible à ceux qui ne le connaissent pas. Par conséquent, si vous l'utilisez, vous voudrez peut-être ajouter un bref commentaire avec des explications à l'endroit approprié dans le code.
3. Déclaration abrégée des constructeurs TypeScript
Cette fonctionnalité est spécifique à TypeScript. Par conséquent, si vous êtes un champion de la pureté JavaScript, il vous manque beaucoup. (Je plaisante, bien sûr, mais cela ne s'applique vraiment pas aux JS classiques).
Comme vous le savez, lors de la déclaration d'une classe, ils répertorient généralement toutes ses propriétés avec des modificateurs d'accès, puis, dans le constructeur de classe, attribuent des valeurs à ces propriétés. Mais dans les cas où le constructeur est très simple et où les valeurs des paramètres passés au constructeur sont simplement écrites dans les propriétés, vous pouvez utiliser une construction plus compacte que la construction habituelle.
Voici à quoi ça ressemble:
// ...
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){}
}
L'utilisation de l'approche ci-dessus lors de la création de constructeurs permet certainement de gagner du temps. Surtout quand il s'agit d'une classe qui a de nombreuses propriétés.
L'essentiel ici est de ne pas oublier d'ajouter
{}
immédiatement après la description du constructeur, puisqu'il s'agit d'une représentation du corps de la fonction. Une fois que le compilateur a rencontré une telle description, il comprendra tout et fera le reste lui-même. En fait, nous parlons du fait que les premier et deuxième fragments du code TS seront éventuellement convertis en le même code JavaScript.
4. Opérateur ternaire
L'opérateur ternaire est une construction facile à lire. Cet opérateur est souvent utilisé à la place d'instructions courtes
if…else
, car il vous permet de vous débarrasser des caractères supplémentaires et de transformer une construction multiligne en une construction sur une ligne.
// if…else
let isEven = ""
if(variable % 2 == 0) {
isEven = "yes"
} else {
isEven = "no"
}
//
let isEven = (variable % 2 == 0) ? "yes" : "no"
Dans la structure d'un opérateur ternaire, le premier est une expression logique, le second est quelque chose comme une commande
return
qui renvoie une valeur si l'expression logique est vraie, et le troisième est aussi quelque chose comme une commande return
qui renvoie une valeur si l'expression logique est fausse. Bien que l'opérateur ternaire soit mieux utilisé sur le côté droit des affectations de valeurs (comme dans l'exemple), il peut également être utilisé de manière autonome, comme mécanisme pour appeler des fonctions quand quelle fonction sera appelée, ou avec quels arguments un et le même sera appelé la même fonction est déterminée par la valeur de l'expression logique. Voici à quoi ça ressemble:
let variable = true;
(variable) ? console.log("It's TRUE") : console.log("It's FALSE")
Notez que la structure de l'instruction ressemble à celle de l'exemple précédent. L'inconvénient d'utiliser l'opérateur ternaire est que si à l'avenir vous devez développer l'une de ses parties (soit celle qui fait référence à la vraie valeur de l'expression logique, soit celle qui fait référence à sa fausse valeur), cela signifie que vous devez passer à l'instruction normale
if…else
.
5. Utilisation d'un cycle de calcul court utilisé par l'opérateur ||
En JavaScript (et en TypeScript aussi), l'opérateur logique OR (
||
) implémente un modèle de calcul abrégé. Autrement dit, il renvoie la première expression évaluée comme true
et ne vérifie pas les expressions restantes.
Cela signifie que s'il y a l'instruction suivante
if
, où l'expression expression1
contient une valeur fausse (réductible à false
), et expression2
- true (réductible à true
), alors seulement expression1
et sera calculé expression2
. Expressions espression3
et expression4
ne seront pas évaluées.
if( expression1 || expression2 || expression3 || expression4)
Nous pouvons profiter de cette opportunité en dehors de l'instruction
if
, où nous attribuons des valeurs aux variables. Cela permettra, en particulier, d'écrire une valeur par défaut dans une variable dans le cas où une valeur, par exemple, représentée par un paramètre de fonction, s'avère être fausse (par exemple, égale 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"
Cet exemple montre comment utiliser un opérateur
||
pour écrire dans une variable soit la valeur du deuxième paramètre d'une fonction, soit une valeur par défaut. Cependant, si vous regardez attentivement cet exemple, vous pouvez y voir un petit problème. Le fait est que s'il y a variable2
une valeur dans 0
ou une chaîne vide, la var2
valeur par défaut sera écrite dans, car les deux 0
et la chaîne vide sont convertis en false
.
Par conséquent, si dans votre cas vous n'avez pas besoin de remplacer toutes les fausses valeurs par la valeur par défaut, vous pouvez recourir à un opérateur moins connu
??
.
6. Opérateur à double bit ~
Les développeurs JavaScript ne sont généralement pas particulièrement enclins à utiliser des opérateurs binaires. Qui se soucie des représentations binaires des nombres de nos jours? Mais le fait est que, comme ces opérateurs travaillent au niveau du bit, ils exécutent les actions correspondantes beaucoup plus rapidement que, par exemple, certaines méthodes.
Si nous parlons de l'opérateur bit à bit NOT (
~
), alors il prend un nombre, le convertit en un entier de 32 bits (en supprimant les bits "supplémentaires") et inverse les bits de ce nombre. Cela entraîne la x
conversion de la valeur en valeur -(x+1)
. Pourquoi sommes-nous intéressés par une telle conversion de nombres? Et le fait que si vous l'utilisez deux fois, cela nous donnera le même résultat qu'un appel de méthode Math.floor
.
let x = 3.8
let y = ~x // x -(3 + 1), ,
let z = ~y // y ( -4) -(-4 + 1) - 3
// :
let flooredX = ~~x //
Remarquez les deux icônes
~
sur la dernière ligne de l'exemple. Cela peut paraître étrange, mais si vous devez convertir beaucoup de nombres à virgule flottante en nombres entiers, cette technique peut être très utile.
7. Attribution de valeurs aux propriétés d'objet
Les capacités de la norme ES6 simplifient le processus de création d'objets et, en particulier, le processus d'attribution de valeurs à leurs propriétés. Si les valeurs de propriété sont attribuées en fonction de variables qui ont les mêmes noms que ces propriétés, il n'est pas nécessaire de répéter ces noms. Auparavant, c'était nécessaire.
Voici un exemple écrit en 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
}
Comme vous pouvez le voir, la nouvelle approche d'attribution de valeurs aux propriétés d'objet vous permet d'écrire du code plus compact et plus simple. Et un tel code n'est pas plus difficile à lire que le code écrit selon les anciennes règles (ce qui ne peut pas être dit à propos du code écrit à l'aide d'autres constructions compactes décrites dans cet article).
8. Renvoi implicite de valeurs à partir des fonctions fléchées
Saviez-vous que les fonctions de flèche sur une seule ligne renvoient les résultats des calculs effectués sur leur seule ligne?
L'utilisation de ce mécanisme vous permet de vous débarrasser d'une expression inutile
return
. Cette technique est souvent utilisée dans les fonctions fléchées passées aux méthodes de tableau telles que filter
ou map
. Voici un exemple de TypeScript:
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 )
Appliquer cette technique ne signifie pas nécessairement rendre le code plus complexe. Des constructions comme celles-ci sont un bon moyen de nettoyer un peu votre code et de vous débarrasser des espaces inutiles et des lignes supplémentaires. Bien sûr, l'inconvénient de cette approche est que si les corps de ces fonctions courtes doivent être étendus, vous devrez recommencer à utiliser des accolades.
La seule particularité qui devra être prise en compte ici est que ce qui est contenu dans la seule ligne des fonctions de flèche courte considérées ici doit être une expression (c'est-à-dire qu'elle doit produire un résultat qui peut être retourné par la fonction). Sinon, une telle conception sera inopérante. Par exemple, les fonctions sur une ligne ci-dessus ne peuvent pas être écrites comme ceci:
const m = _ => if(2) console.log("true") else console.log("false")
Dans la section suivante, nous continuerons à parler des fonctions fléchées sur une seule ligne, mais nous allons maintenant parler des fonctions qui ne peuvent pas être créées sans accolades.
9. Paramètres de fonction, qui peuvent avoir des valeurs par défaut
ES6 a introduit la possibilité de spécifier des valeurs affectées aux paramètres de fonction par défaut. Auparavant, JavaScript n'avait pas de telles capacités. Par conséquent, dans les situations où il était nécessaire d'attribuer des valeurs similaires aux paramètres, il était nécessaire de recourir à quelque chose comme un modèle de calculs d'opérateurs réduits
||
.
Mais maintenant, le même problème peut être résolu très simplement:
// 2
// ,
function myFunc(a, b, c = 2, d = "") {
// ...
}
Mécanisme simple, non? Mais, en fait, tout est encore plus intéressant qu'il n'y paraît à première vue. Le fait est que la valeur par défaut peut être n'importe quoi, y compris un appel de fonction. Cette fonction sera appelée si le paramètre correspondant ne lui est pas passé lors de l'appel de la fonction. Cela facilite la mise en œuvre du modèle de paramètre de fonction requis:
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)
Ici, en fait, est la même fonction de flèche sur une ligne, lors de la création que vous ne pouvez pas faire sans accolades. Le fait est que la fonction
mandatory
utilise une instruction throw
. Faites attention - "instruction", pas "expression". Mais, je suppose, ce n'est pas le prix le plus élevé pour la possibilité d'équiper des fonctions avec les paramètres requis.
10. Conversion de valeurs en type booléen en utilisant !!
Ce mécanisme fonctionne sur le même principe que la construction ci-dessus
~~
. À savoir, nous parlons du fait que pour convertir une valeur en un type logique, vous pouvez utiliser deux opérateurs logiques NOT ( !!
):
!!23 // TRUE
!!"" // FALSE
!!0 // FALSE
!!{} // TRUE
Un opérateur
!
résout déjà la plupart de ce problème, c'est-à-dire qu'il convertit la valeur en un type booléen, puis renvoie la valeur opposée. Et le deuxième opérateur !
prend ce qui s'est passé et renvoie simplement le contraire. En conséquence, nous obtenons la valeur d'origine convertie en un type booléen.
Cette construction courte peut être utile dans diverses situations. Premièrement, lorsque vous devez vous assurer qu'une variable est affectée à une valeur booléenne réelle (par exemple, si nous parlons d'une variable TypeScript de type
boolean
). Deuxièmement, lorsque vous devez effectuer une comparaison stricte (en utilisant ===
) quelque chose avec true
ou false
.
11. Syntaxe de destruction et de diffusion
Les mécanismes mentionnés dans le titre de cette section peuvent être discutés et discutés. Le fait est que, s'ils sont utilisés correctement, ils peuvent conduire à des résultats très intéressants. Mais ici je n'irai pas trop loin. Je vais vous dire comment les utiliser pour simplifier la solution de certains problèmes.
▍Destructuration d'objets
Avez-vous déjà été confronté à la tâche d'écrire plusieurs valeurs de propriétés d'objet dans des variables ordinaires? Cette tâche est assez courante. Par exemple, lorsque vous devez travailler avec ces valeurs (en les modifiant, par exemple) et en même temps ne pas affecter ce qui est stocké dans l'objet d'origine.
L'utilisation de la déstructuration d'objets vous permet de résoudre des problèmes similaires en utilisant le minimum de code:
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;
Quiconque a utilisé TypeScript a vu cette syntaxe dans les instructions
import
. Il vous permet d'importer des méthodes de bibliothèque individuelles sans contaminer l'espace de noms du projet avec de nombreuses fonctions inutiles:
import { get } from 'lodash'
Par exemple, cette instruction vous permet d'importer
lodash
uniquement une méthode de la bibliothèque get
. En même temps, les autres méthodes de cette bibliothèque ne tombent pas dans l'espace de noms du projet. Et il y en a beaucoup dedans.
▍Étendre la syntaxe et créer de nouveaux objets et tableaux basés sur ceux existants
L'utilisation de la syntaxe spread (
…
) vous permet de simplifier la tâche de création de nouveaux tableaux et objets basés sur ceux existants. Maintenant, cette tâche peut être résolue en écrivant littéralement une ligne de code et sans recourir à aucune méthode spéciale. Voici un exemple:
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}
Notez que l'utilisation de cette approche pour combiner des objets écrasera leurs propriétés avec le même nom. Cela ne s'applique pas aux tableaux. En particulier, si les tableaux à fusionner ont les mêmes valeurs, ils finiront tous dans le tableau résultant. Si vous devez vous débarrasser des répétitions, vous pouvez recourir à une structure de données
Set
.
▍Combinaison de la déstructuration et de la syntaxe de diffusion
La déstructuration peut être utilisée en conjonction avec la syntaxe de diffusion. Cela vous permet d'obtenir un effet intéressant. Par exemple, supprimez le premier élément du tableau et laissez le reste intact (comme dans l'exemple courant avec le premier et le dernier élément de la liste, dont l'implémentation peut être trouvée en Python et dans d'autres langages). Et aussi, par exemple, vous pouvez même extraire certaines propriétés d'un objet et laisser le reste intact. Prenons un exemple:
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"}
Notez que les trois points utilisés sur le côté gauche de l'instruction d'affectation doivent s'appliquer au tout dernier élément. Vous ne pouvez pas d'abord utiliser la syntaxe de propagation, puis décrire des variables individuelles:
const [...values, lastItem] = [1,2,3,4]
Ce code ne fonctionnera pas.
Résultat
Il existe de nombreux autres modèles similaires à ceux dont nous avons parlé aujourd'hui. Mais, en les utilisant, il convient de se rappeler que plus le code est compact, plus il est difficile de le lire pour quelqu'un qui n'est pas habitué aux constructions abrégées qui y sont utilisées. Et le but de l'utilisation de telles constructions n'est pas de minimiser le code ou de l'accélérer. Cet objectif est uniquement de supprimer les structures inutiles du code et de faciliter la vie de la personne qui lira ce code.
Par conséquent, pour que tout le monde soit heureux, il est recommandé de maintenir un équilibre sain entre les constructions compactes et le code lisible normal. N'oubliez pas que vous n'êtes pas la seule personne à lire votre code.
Quelles constructions compactes utilisez-vous dans le code JavaScript et TypeScript?