Déclarations

Références

L'actualité

Librairie

L'information

Déclarations de variables

let et const sont deux types relativement nouveaux de déclarations de variables en JavaScript. let est semblable à var à certains égards, mais permet aux utilisateurs d'éviter certains des "pièges" courants rencontrés par les utilisateurs en JavaScript. const est une augmentation de let, ce qu'il empêche la réaffectation à une variable.

TypeScript étant un sur-ensemble de JavaScript, le langage prend naturellement en charge let et const.

Si vous avez utilisé JavaScript avec désinvolture, la section suivante peut être un bon moyen de vous rafraîchir la mémoire. Si vous connaissez très bien toutes les bizarreries des déclarations var en JavaScript, vous aurez peut-être plus de facilité à sauter.

Déclarations var

Déclarer une variable en JavaScript a toujours été fait avec le mot-clé var.

Comme vous l'avez peut-être compris, nous venons de déclarer une variable nommée a avec la valeur 10.

Nous pouvons également déclarer une variable à l'intérieur d'une fonction:

Nous pouvons également accéder à ces mêmes variables au sein d'autres fonctions:

Dans l'exemple ci-dessus, g capture la variable a déclarée dans f. À tout moment la valeur de a sera liée à la valeur de a dans f. Même si g est appelé une fois f exécuté, il pourra accéder et modifier a.

Règles de cadrage

Les déclarations var ont des règles de portée étranges pour càutilisées dans d'autres languages. Prenons l'exemple suivant :

La variable x a été déclarée à l'intérieur du bloc if et pourtant nous avons pu y accéder de l'extérieur. En effet, les déclarations var sont accessibles n'importe où dans la fonction, le module, l'espace de nom ou l'étendue globale qui les contient, quel que soit le bloc qui le contient. Certaines personnes appellent cela var-scoping ou fonction-scope. Les paramètres sont également fonctionnels.

Ces règles de portée peuvent entraîner plusieurs types d'erreurs. Un problème qu'ils exacerbent est le fait que déclarer la même variable plusieurs fois n'est pas une erreur :

C'était peut-être facile à repérer pour certains, mais la boucle interne for écrasera accidentellement la variable i, car i fait référence à la même variable étendue de fonction.

Variables de capture variables

Prenez une seconde pour deviner quelle est la sortie de l'extrait suivant:

Pour ceux qui ne sont pas familiers, setTimeout essaiera d'exécuter une fonction après un certain nombre de millisecondes (en attendant que tout le reste cesse de fonctionner).

De nombreux développeurs JavaScript connaissent très bien ce comportement, mais si vous êtes surpris, vous n'êtes certainement pas seul. La plupart des gens s'attendent à ce que la sortie soit :

Rappelez-vous ce que nous avons mentionné plus tôt à propos de la capture de variables ? Chaque expression de fonction à laquelle nous passons setTimeout fait référence à la même variable i de la même portée.

Prenons une minute pour réfléchir à ce que cela signifie. setTimeout exécutera une fonction après un certain nombre de millisecondes, mais uniquement après que la boucle for est cessé de s'exécuter; Au moment où la boucle for a cessé de s'exécuter, la valeur de i est 10. Ainsi, chaque fois que la fonction donnée sera appelée, elle sera imprimée 10 !

Une solution courante consiste à utiliser IIFE (Immediately Invoked Function Expression) pour capturer i à chaque itération :

Ce motif étrange est en fait assez commun. En fait, le i dans la liste des paramètres ombrage le i déclaré dans la boucle for, mais puisque nous leur avons donné le même nom, nous n'avons pas eu à modifier trop le corps de la boucle.

Déclarations let

Vous avez maintenant compris que var posaient certains problèmes, et c'est précisément la raison pour laquelle des déclarations let ont été introduites. Hormis le mot clé let utilisé, les instructions sont écrites de la même manière var.

La principale différence ne réside pas dans la syntaxe, mais dans la sémantique.

Portée de bloc

Quand une variable est déclarée en utilisant let, elle utilise ce que certains appellent le lexical-scoping ou block-scoping. Contrairement aux variables déclarées avec var dont les portées sortent de la fonction qui les contient, les variables à portée de bloc ne sont pas visibles en dehors de leur bloc contenant ou de la boucle for la plus proche.
Ici, nous avons deux variables locales a et b. La portée de a est limitée au corps de f tandis que la portée de b est limitée au bloc if de la déclaration qui la contient.

Les variables déclarées dans une clause catch ont également des règles de portée similaires.

Une autre propriété des variables à portée de bloc est qu'elles ne peuvent pas être lues ou écrites avant d'être déclarées. Bien que ces variables soient "présentes" dans tout leur périmètre, tous les points jusqu'à leur déclaration font partie de leur zone morte temporelle. C'est une façon sophistiquée de dire que vous ne pouvez pas y accéder avant l'instruction let, et heureusement, TypeScript vous le fera savoir.

Il convient de noter que vous pouvez toujours capturer une variable à la portée d'un bloc avant qu'elle ne soit déclarée. Le seul problème est qu'il est illégal d'appeler cette fonction avant la déclaration. Si vous ciblez ES2015, une exécution moderne générera une erreur. Cependant, pour l'instant, TypeScript est facultatif et ne le signale pas comme une erreur.


Re-déclarations et observation

Avec les déclarations var, nous avons mentionné que peu importe le nombre de fois que vous avez déclaré vos variables; tu viens d'en avoir un.
Dans l'exemple ci-dessus, toutes les déclarations de x font en réalité référence au même x, ce qui est parfaitement valide. Cela finit souvent par être une source de bugs. Heureusement, les déclarations ne sont pas aussi clémentes.
Les variables n'ont pas nécessairement besoin d'être toutes les deux couvertes par des blocs pour que TypeScript nous indique qu'il y a un problème.


Cela ne veut pas dire que la variable à portée de bloc ne peut jamais être déclarée avec une variable à portée de fonction. La variable de portée de bloc doit simplement être déclarée dans un bloc distinctement différent.

L'introduction d'un nouveau nom dans une portée plus imbriquée est appelée observation. C'est un peu un couteau à double tranchant, car il peut introduire certains bugs seul en cas d'ombrage accidentel, tout en empêchant certains bugs. Par exemple, imaginons que nous ayons écrit une fonction sumMatrix en utilisant des variables let.
En fait, cette version de la boucle effectuera la somme correctement car les ombres de la boucle interne sont issues de la boucle externe.

On devrait généralement éviter l'observation pour pouvoir écrire du code plus clair. Bien que dans certains cas, il soit opportun de tirer parti de cette situation, vous devez faire preuve de jugement.

Capture de variables à portée de bloc

Lorsque nous avons abordé pour la première fois l'idée de la capture de variables avec une déclaration var, nous avons brièvement abordé le mode d'action des variables une fois capturées. Pour en donner une meilleure idée, chaque fois qu'une étendue est exécutée, cela crée un "environnement" de variables. Cet environnement et ses variables capturées peuvent exister même après que tout ce qui est dans son étendue a fini de s'exécuter.
Comme nous avons capturé une ville dans son environnement, nous pouvons toujours y accéder malgré le fait que le bloc if ait été exécuté.

Rappelez-vous qu'avec notre précédent exemple setTimeout, nous avions fini par avoir besoin d'utiliser un IIFE pour capturer l'état d'une variable pour chaque itération de la boucle for. En réalité, nous étions en train de créer un nouvel environnement de variables pour nos variables capturées. C'était un peu pénible, mais heureusement, vous n'aurez plus jamais à refaire cela avec TypeScript.

Laissez les déclarations avoir un comportement radicalement différent lorsqu'elles sont déclarées dans le cadre d'une boucle. Plutôt que de simplement introduire un nouvel environnement dans la boucle elle-même, ces déclarations créent en quelque sorte une nouvelle portée par itération. Comme c'est ce que nous faisions de toute façon avec notre IIFE, nous pouvons changer notre ancien exemple setTimeout pour utiliser simplement une déclaration let.

et comme prévu, cela imprimera


Déclarations const

Les déclarations const sont une autre façon de déclarer des variables.
Elles ressemblent à des déclarations mais, comme leur nom l'indique, elles ne peuvent pas être modifiées. En d'autres termes, elles sont identiques à celles qui ont été définies, mais vous ne pouvez pas les renvoyer. Cela ne doit pas être confondu avec l'idée que les valeurs importées sont référence immuables.
A moins que vous ne preniez les mesures spécifiques pour l'éviter, l'état interne d'une variable reste modifiable. Heureusement, TypeScript vous permet de déterminer les membres d'un objet en lecture seule.

let vs const

étant donné que nous avons deux types de déclarations avec une sémantique de portée similaire, il est naturel de se demander laquelle utiliser. Comme pour la plupart des questions générales, la réponse est cela dépend.

En appliquant le principe de moindre privilège, toutes les déclarations autres que celles que vous envisagez de modifier doivent utiliser const. La raison en est que si une variable n'a pas besoin d'être écrite, les autres personnes travaillant sur la même base de code ne devraient pas automatiquement être en mesure d'écrire sur l'objet et doivent donc déterminer si elles doivent réellement être réaffectées à la variable. L'utilisation de const rend également le code plus prévisible lors du raisonnement sur le flux de données.

Déstructuration

Une autre fonctionnalité d'ECMAScript 2015 et de TypeScript est la déstructuration.

Déstructuration des tableaux

La forme la plus simple de déstructuration est l'affectation de déstructuration de tableau :

Cela crée deux nouvelles variables nommées first et second. Ceci équivaut à utiliser l'indexation, mais est beaucoup plus pratique :

La restructuration fonctionne également avec des variables déjà déclarées :

Et avec les paramètres d'une fonction :

Vous pouvez créer une variable pour les éléments restants d'une liste en utilisant la syntaxe suivante ... :

Bien sûr, puisqu'il s'agit de JavaScript, vous pouvez simplement ignorer les éléments de fin qui vous importent peu :

Ou d'autres éléments :


Déstructuration d'objet

Vous pouvez également déstructurer des objets :

Cela crée de nouvelles variables a et b de o.a et o.b. Notez que vous pouvez sauter c si vous n'en avez pas besoin.

Comme pour la déstructuration de tableau, vous pouvez avoir une affectation sans déclaration :

Notez que nous avons dû entourer cette déclaration de parenthèses. JavaScript analyse normalement a comme le début du bloc.

Vous pouvez créer une variable pour les éléments restants d'un objet en utilisant la syntaxe suivante ...:


Renommage de propriété

Vous pouvez également donner différents noms aux propriétés :

Ici, la syntaxe commence à devenir confuse. Vous pouvez lire a: newName1 comme " a comme newName1". La direction est de gauche à droite, comme si vous aviez écrit :

De manière confuse, les deux points ici n'indiquent pas le type. Le type, si vous le spécifiez, doit encore être écrit après la déstructuration complète :


Les valeurs par défaut

Les valeurs par défaut vous permettent de spécifier une valeur par défaut dans le cas où une propriété n'est pas définie :

keepWholeObject a maintenant une variable pour wholeObject ainsi que les propriétés a et b, même si b est indéfini.

Déclarations de fonction

La destruction fonctionne également dans les déclarations de fonction. Pour les cas simples, ceci est simple :

Toutefois, la définition de paramètres par défaut est plus courante pour les paramètres et il peut être délicat de définir correctement les paramètres par défaut avec la déstructuration. Tout d'abord, vous devez vous rappeler de mettre le motif avant la valeur par défaut.


L'extrait ci-dessus est un exemple d'inférence de type.

Ensuite, vous devez vous rappeler de donner une valeur par défaut pour les propriétés facultatives sur la propriété déstructurée au lieu de l'initialiseur principal. Rappelez-vous que C a été défini avec b optionnel :

Utilisez la déstructuration avec soin. Comme le montre l'exemple précédent, toute expression de déstructuration la plus simple confond. Cela est particulièrement vrai avec la déstructuration profondément imbriquée, qui devient très difficile à comprendre même sans empiler sur le renommage, les valeurs par défaut et les annotations de type. Essayez de garder les expressions de déstructuration petites et simples. Vous pouvez toujours écrire les affectations que la déstructuration générerait vous-même.

Propager

L'opérateur d'étalement est l'opposé de la déstructuration. Il vous permet de répartir un tableau dans un autre tableau ou un objet dans un autre objet. Par exemple :

Cela donne à bothPlus la valeur [0, 1, 2, 3, 4, 5]. La propagation crée une copie superficielle des premier et deuxième. Ils ne sont pas modifiés par la propagation.

Vous pouvez également écarter des objets :

Maintenant, la recherche est {food: "rich", prix: "$$", ambiance: "noisy"}. La propagation d'objets est plus complexe que la diffusion de tableaux. Comme la propagation de tableau, elle se fait de gauche à droite, mais le résultat est toujours un objet. Cela signifie que les propriétés apparaissant plus tard dans l'objet répandu écrasent les propriétés apparaissant plus tôt. Donc, si nous modifions l'exemple précédent pour qu'il s'étende à la fin :

Ensuite, la propriété food par défaut écrase la food: "rich", ce qui n'est pas ce que nous voulons dans ce cas.

La propagation d'un objet a également quelques autres limites surprenantes. Tout d'abord, il n'inclut que les propriétés énumérables propres à un objet. En gros, cela signifie que vous perdez des méthodes lorsque vous répartissez les instances d'un objet :

Deuxièmement, le compilateur Typescript n'autorise pas les paramètres de type spreads à partir de fonctions génériques. Cette fonctionnalité est attendue dans les futures versions du langage.