trouver un expert Python
Si vous êtes sérieux au sujet de développement web, alors vous devrez en savoir plus sur Javascript à un moment donné. Année après année, de nombreuses enquêtes ont montré que JavaScript est l'un des langages de programmation les plus populaires au monde, avec une communauté de développeurs importante et croissante. Tout comme Python, le JavaScript moderne peut être utilisé presque partout, y compris le frontal, le back-end, le bureau, le mobile et l'Internet des objets (IoT). Parfois, ce n'est pas un choix évident entre Python et JavaScript.
Si vous n'avez jamais utilisé JavaScript auparavant ou que vous vous êtes senti dépassé par le rythme rapide de son évolution ces dernières années, cet article vous mettra sur la bonne voie. Vous devez déjà connaître les bases de Python pour profiter pleinement des comparaisons faites entre les deux langages.
JavaScript en un coup d'œil
Si vous connaissez déjà les origines de JavaScript ou si vous voulez simplement voir le code en action, n'hésitez pas à passer à la section suivante. Sinon, préparez-vous à une brève leçon d'histoire qui vous guidera à travers l'évolution de JavaScript.
Ce n'est pas Java!
Beaucoup de gens, notamment certains recruteurs informatiques, pensent que JavaScript et Java sont la même langue. Il est difficile de les blâmer, car inventer un nom aussi familier était une astuce marketing.
JavaScript s'appelait à l'origine Moka avant qu'il ne soit renommé LiveScript et enfin rebaptisé Javascript peu de temps avant sa sortie. À l'époque, Java était une technologie Web prometteuse, mais elle était trop difficile pour les webmasters non techniques. JavaScript a été conçu comme un langage quelque peu similaire mais adapté aux débutants pour compléter les applets Java dans les navigateurs Web.
Fait amusant: Java et JavaScript ont été publiés en 1995. Python avait déjà cinq ans.
Pour ajouter à la confusion, Microsoft a développé sa propre version du langage, qu'il a appelé JScript en raison d'un manque de droits de licence, pour une utilisation avec Internet Explorer 3.0. Aujourd'hui, les gens appellent souvent JavaScript JS.
Bien que Java et JavaScript partagent quelques similitudes dans leurs Syntaxe de type C ainsi que dans leurs bibliothèques standard, ils sont utilisés à des fins différentes. Java a divergé du côté client en un langage plus général. JavaScript, malgré sa simplicité, était suffisant pour valider les formulaires HTML et ajouter de petites animations.
C'est ECMAScript
JavaScript a été développé au début du Web par une entreprise relativement petite connue sous le nom de Netscape. Pour gagner le marché contre Microsoft et atténuer les différences entre les navigateurs Web, Netscape devait normaliser leur langue. Après avoir été refusée par l'international Consortium World Wide Web (W3C), ils ont demandé à un organisme européen de normalisation ECMA (aujourd'hui Ecma International) pour aider.
L'ECMA a défini une spécification formelle pour le langage appelé ECMAScript parce que le nom Javascript avait été déposé par Sun Microsystems. JavaScript est devenu l'une des implémentations de la spécification qu'il a initialement inspirée.
Remarque: En d'autres termes, JavaScript est conforme à la spécification ECMAScript. Un autre membre notable de la famille ECMAScript est ActionScript, qui est utilisé sur la plate-forme Flash.
Alors que les implémentations individuelles de la spécification étaient conformes à ECMAScript dans une certaine mesure, elles étaient également livrées avec des API propriétaires supplémentaires. Cela a conduit à des pages Web ne s'affichant pas correctement sur différents navigateurs et à l'avènement de bibliothèques telles que jQuery.
Y a-t-il d'autres scripts?
À ce jour, JavaScript reste le seul langage de programmation pris en charge nativement par les navigateurs Web. C'est le lingua franca du Web. Certaines personnes l'adorent, d'autres non.
Il y a eu et continue d'exister de nombreuses tentatives pour remplacer ou supplanter JavaScript par d'autres technologies, notamment:
- Applications Internet riches: Flash, Silverlight, JavaFX
- Transpilers: Haxe, Google Web Toolkit, pyjs
- Dialectes JavaScript: CoffeeScript, TypeScript
Ces tentatives ont été motivées non seulement par des préférences personnelles, mais aussi par les limitations des navigateurs Web avant HTML5 est entré en scène. À cette époque, vous ne pouviez pas utiliser JavaScript pour des tâches gourmandes en calcul telles que le dessin de graphiques vectoriels ou le traitement audio.
Applications Internet riches (RIA), d'autre part, offrait une expérience de type bureau immersive dans le navigateur via des plugins. Ils étaient parfaits pour les jeux et le traitement des médias. Malheureusement, la plupart d'entre eux étaient des sources fermées. Certains avaient des failles de sécurité ou des problèmes de performances sur certaines plateformes. Pour couronner le tout, ils ont tous considérablement limité la capacité des moteurs de recherche Web à indexer les pages créées avec ces plugins.
À peu près au même moment transpilers, qui a permis une traduction automatisée d'autres langues en JavaScript. Cela a rendu la barrière d'entrée au développement frontal beaucoup plus faible, car soudainement, les ingénieurs back-end pouvaient tirer parti de leurs compétences dans un nouveau domaine. Cependant, les inconvénients étaient un temps de développement plus lent, une prise en charge limitée des normes Web et un débogage encombrant du code JavaScript transposé. Pour le lier au code d'origine, vous avez besoin d'une carte source.
Remarque: Alors qu'un compilateur traduit le code lisible par l'homme écrit dans un langage de programmation de haut niveau directement en code machine, un transpilateur traduit un langage de haut niveau en un autre. C’est pourquoi les transpilers sont également appelés de source en source compilateurs. Cependant, ils ne sont pas les mêmes que les compilateurs croisés, qui produisent du code machine pour les plates-formes matérielles étrangères.
Pour écrire du code Python pour le navigateur, vous pouvez utiliser l'un des transpilateurs disponibles, tels que Transcrypt ou pyjs. Ce dernier est un port de Google Web Toolkit (GWT), qui était un transpileur Java vers JavaScript très populaire. Une autre option consiste à utiliser un outil comme Brython, qui exécute une version simplifiée de l'interpréteur Python en JavaScript pur. Cependant, les avantages peuvent être compensés par de mauvaises performances et un manque de compatibilité.
Le transpiling a permis l'émergence d'une tonne de nouveaux langages dans le but de remplacer JavaScript et de combler ses lacunes. Certaines de ces langues étaient étroitement liées dialectes de JavaScript. Le premier était peut-être CoffeeScript, qui a été créé il y a une dizaine d'années. L'un des derniers en date était Google's Dart, qui était la langue à la croissance la plus rapide en 2019 selon GitHub. De nombreuses autres langues ont suivi, mais la plupart d'entre elles sont désormais obsolètes en raison des récentes avancées de JavaScript.
Une exception flagrante est le TypeScript de Microsoft, qui a gagné en popularité ces dernières années. Il s'agit d'un surensemble de JavaScript entièrement compatible qui ajoute des options vérification de type statique. Si cela vous semble familier, c'est parce que l'indication de type de Python a été inspirée par TypeScript.
Bien que le JavaScript moderne soit mature et activement développé, le transpilage est toujours une approche courante pour assurer la rétrocompatibilité avec les navigateurs plus anciens. Même si vous n'utilisez pas TypeScript, qui semble être le langage de choix pour de nombreux nouveaux projets, vous devrez toujours transposer votre nouveau JavaScript brillant dans une ancienne version du langage. Sinon, vous courez le risque d’obtenir un erreur d'exécution.
Certains transpilers synthétisent également des API Web de pointe, qui peuvent ne pas être disponibles sur certains navigateurs, avec ce que l'on appelle polyfill.
Aujourd'hui, JavaScript peut être considéré comme le langage d'assemblage du Web. De nombreux ingénieurs frontaux professionnels ont tendance ne pas pour l'écrire à la main plus. Dans un tel cas, il est généré à partir de zéro grâce au transpiling.
Cependant, même le code manuscrit est souvent traité d'une manière ou d'une autre. Par exemple, la minification supprime les espaces et renomme les variables pour réduire la quantité de données à transférer et pour obscurcir le code afin qu'il soit plus difficile de procéder à une rétro-ingénierie. Ceci est analogue à la compilation du code source d'un langage de programmation de haut niveau en code machine natif.
En plus de cela, il convient de mentionner que les navigateurs contemporains prennent en charge la norme WebAssembly, qui est une technologie assez nouvelle. Il définit un format binaire pour le code qui peut s'exécuter avec des performances presque natives dans le navigateur. Il est rapide, portable, sécurisé et permet une compilation croisée de code écrit dans des langages comme C ++ ou Rust. Avec lui, par exemple, vous pouvez prendre le code vieux de plusieurs décennies de votre jeu vidéo préféré et l'exécuter dans le navigateur.
Pour le moment, WebAssembly vous aide à optimiser les performances des parties critiques du calcul de votre code, mais il est livré avec un prix. Pour commencer, vous devez connaître l'un des langages de programmation actuellement pris en charge. Vous devez vous familiariser avec les concepts de bas niveau tels que la gestion de la mémoire car il n'y a pas encore de garbage collector. L'intégration avec du code JavaScript est difficile et coûteuse. De plus, il n'y a aucun moyen facile d'appeler des API Web à partir de celui-ci.
Il semble qu'après toutes ces années, JavaScript ne disparaîtra pas de si tôt.
Kit de démarrage JavaScript
L'une des premières similitudes que vous remarquerez en comparant Python vs JavaScript est que les barrières à l'entrée pour les deux sont assez faibles, ce qui rend les deux langues très attrayantes pour les débutants qui souhaitent apprendre à coder. Pour JavaScript, la seule exigence de départ est d'avoir un navigateur Web. Si vous lisez ceci, alors vous avez déjà couvert cela. Cette accessibilité contribue à la popularité de la langue.
La barre d'adresse
Pour avoir un aperçu de ce que c'est que d'écrire du code JavaScript, vous pouvez arrêter de lire maintenant et taper le texte suivant dans la barre d'adresse avant d'y accéder:
Le texte littéral est javascript: alert ('bonjour le monde')
, mais ne vous contentez pas de le copier-coller!
Cette partie après la javascript:
le préfixe est un morceau de code JavaScript. Une fois confirmé, votre navigateur devrait afficher une boîte de dialogue avec le Bonjour le monde
message en elle. Chaque navigateur affiche cette boîte de dialogue légèrement différemment. Par exemple, Google Chrome l'affiche comme ceci:
Copier et coller un tel extrait dans la barre d'adresse échouera dans la plupart des navigateurs, qui filtrent le javascript:
préfixe comme mesure de sécurité contre l'injection de code malveillant.
Certains navigateurs, comme Mozilla Firefox, vont encore plus loin en bloquant complètement ce type d'exécution de code. Dans tous les cas, ce n'est pas la façon la plus pratique de travailler avec JavaScript, car vous êtes limité à une seule ligne et limité à un certain nombre de caractères. Il y a une meilleure façon.
Outils de développement Web
Si vous affichez cette page sur un ordinateur de bureau ou un ordinateur portable, vous pouvez profiter de la outils de développement web, qui offrent une expérience comparable sur les navigateurs Web concurrents.
Remarque: Les exemples qui suivent utilisent la version de Google Chrome 80,0
. Les raccourcis clavier peuvent varier pour d'autres navigateurs, mais l'interface doit être sensiblement la même.
Pour basculer entre ces outils, reportez-vous à la documentation de votre navigateur ou essayez l'un de ces raccourcis clavier courants:
-
F12
-
Ctrl+Décalage+je
-
Cmd+Option+je
Cette fonctionnalité peut être désactivée par défaut si vous utilisez Apple Safari ou Microsoft Edge, par exemple. Une fois les outils de développement Web activés, vous verrez une myriade d'onglets et de barres d'outils avec un contenu similaire à celui-ci:
Collectivement, il s'agit d'un puissant environnement de développement équipé d'un débogueur JavaScript, d'un profileur de performances et de mémoire, d'un gestionnaire de trafic réseau et bien plus encore. Il existe même un débogueur à distance pour les appareils physiques connectés via un câble USB!
Pour le moment, cependant, concentrez-vous console, auquel vous pouvez accéder en cliquant sur un onglet situé en haut. Alternativement, vous pouvez rapidement l'amener à l'avant en appuyant sur Esc à tout moment lors de l'utilisation des outils de développement Web.
La console est principalement utilisée pour inspecter messages de journal émis par la page Web actuelle, mais il peut également être une excellente aide à l'apprentissage JavaScript. Tout comme avec l'interpréteur Python interactif, vous pouvez taper du code JavaScript directement dans la console pour l'avoir réalisé à la volée:
Il a tout ce que vous attendez d'un REPL outil et plus. En particulier, la console est livrée avec une coloration syntaxique, une saisie semi-automatique contextuelle, un historique des commandes, une modification de ligne similaire à GNU Readline et la possibilité de rendre des éléments interactifs. Ses capacités de rendu peuvent être particulièrement utiles pour introspecter des objets et des données tabulaires, passer au code source à partir d'une trace de pile ou afficher des éléments HTML.
Vous pouvez enregistrer des messages personnalisés sur la console à l'aide d'un prédéfini console
objet. JavaScript console.log ()
est l'équivalent de Python impression()
:
console.Journal('Bonjour le monde');
Cela fera apparaître le message dans l'onglet console des outils de développement Web. En dehors de cela, il existe quelques méthodes plus utiles disponibles dans le console
objet.
Document HTML
L'endroit le plus naturel pour le code JavaScript est de loin quelque part près d'un document HTML, qu'il manipule généralement. Vous en apprendrez plus à ce sujet plus tard. Vous pouvez référencer JavaScript à partir de HTML de trois manières différentes:
Méthode | Exemple de code |
---|---|
Attribut de l'élément HTML |
|
HTML Tag |
|
Fichier externe |
|
Vous pouvez en avoir autant que vous le souhaitez. Les première et deuxième méthodes incorporent en ligne JavaScript directement dans un document HTML. Bien que cela soit pratique, vous devriez essayer de garder le JavaScript impératif séparé du HTML déclaratif pour favoriser la lisibilité.
Il est plus courant d'en trouver un ou plusieurs tags referencing external files with JavaScript code. These files can be served by either a local or a remote web server.
The La balise peut apparaître n'importe où dans le document tant qu'elle est imbriquée dans
ou la
étiquette:
<html>
<tête>
<méta charset="UTF-8">
<Titre>Page d'accueil</Titre>
<scénario src="https://server.com/library.js"> </scénario>
<scénario src="local / assets / app.js"> </scénario>
<scénario>
une fonction ajouter(une, b)
revenir une + b;
</scénario>
</tête>
<corps>
<p>Lorem ipsum dolor sit amet (...)</p>
<scénario>
console.Journal(ajouter(2, 3));
</scénario>
</corps>
</html>
Ce qui est important, c'est la façon dont les navigateurs Web traitent les documents HTML. Un document est lu de haut en bas. Chaque fois qu'un tag is found, it gets immediately executed even before the page has been fully loaded. If your script tries to find HTML elements that haven’t been rendered yet, then you’ll get an error.
To be safe, always put the balises en bas du corps de votre document:
<html>
<tête>
<méta charset="UTF-8">
<Titre>Page d'accueil</Titre>
</tête>
<corps>
<p>Lorem ipsum dolor sit amet (...)</p>
<scénario src="https://server.com/library.js"> </scénario>
<scénario src="local / assets / app.js"> </scénario>
<scénario>
une fonction ajouter(une, b)
revenir une + b;
</scénario>
<scénario>
console.Journal(ajouter(2, 3));
</scénario>
</corps>
</html>
Non seulement cela vous protégera contre ladite erreur, mais cela améliorera également l'expérience globale de l'utilisateur. En déplaçant ces balises vers le bas, vous autorisez l'utilisateur à voir la page entièrement rendue avant le téléchargement des fichiers JavaScript. Vous pourriez aussi reporter
le téléchargement de fichiers JavaScript externes jusqu'au chargement de la page:
<scénario src="https://server.com/library.js" reporter> </scénario>
Si vous souhaitez en savoir plus sur le mélange de JavaScript avec HTML, jetez un œil à un didacticiel JavaScript de W3Schools.
Node.js
Vous n'avez plus besoin d'un navigateur Web pour exécuter le code JavaScript. Il existe un outil appelé Node.js qui fournit un environnement d'exécution pour JavaScript côté serveur.
Un environnement d'exécution comprend le Moteur JavaScript, qui est l'interpréteur ou le compilateur de langue, ainsi qu'un API pour interagir avec le monde. Il existe plusieurs moteurs alternatifs fournis avec différents navigateurs Web:
Navigateur Web | Moteur JavaScript |
---|---|
Apple Safari | JavaScriptCore |
Microsoft Edge | V8 |
Microsoft IE | Chakra |
Mozilla Firefox | SpiderMonkey |
Google Chrome | V8 |
Chacun d'eux est mis en œuvre et maintenu par son fournisseur. Pour l'utilisateur final, cependant, il n'y a pas de différence notable, sauf pour les performances des moteurs individuels. Node.js utilise le même moteur V8 développé par Google pour son navigateur Chrome.
Lorsque vous exécutez JavaScript dans un navigateur Web, vous souhaitez généralement pouvoir répondre aux clics de souris, ajouter dynamiquement des éléments HTML ou peut-être obtenir une image de la webcam. Mais cela n'a aucun sens dans une application Node.js, qui s'exécute en dehors du navigateur.
Après avoir installé Node.js pour votre plate-forme, vous pouvez exécuter du code JavaScript comme avec l'interpréteur Python. Pour démarrer une session interactive, accédez à votre terminal et tapez nœud
:
Ceci est similaire à la console de développeur Web que vous avez vue précédemment. Cependant, dès que vous essayez de faire référence à un élément lié au navigateur, vous obtenez une erreur:
> alerte('Bonjour le monde');
Jeté:
ReferenceError: alerte est ne pas défini
C'est parce que votre environnement d'exécution manque l'autre composant, qui est l'API du navigateur. Dans le même temps, Node.js fournit un ensemble d'API utiles dans une application principale, comme l'API du système de fichiers:
> const fs = exiger(«fs»);
> fs.existSync('/ chemin / vers / fichier');
faux
Pour des raisons de sécurité, vous ne trouverez pas ces API dans le navigateur. Imaginez permettre à un site Web aléatoire de contrôler les fichiers de votre ordinateur!
Si la bibliothèque standard ne répond pas à vos besoins, vous pouvez toujours installer un package tiers avec le Gestionnaire de packages de nœuds (npm
) fourni avec l'environnement Node.js. Pour parcourir ou rechercher des packages, accédez au npm
registre public, qui est comme le Python Package Index (PyPI).
Semblable à la python
commande, vous pouvez exécuter scripts avec Node.js:
$ écho "console.log ('hello world');" > bonjour.js
$ noeud hello.js
Bonjour le monde
En fournissant un chemin d'accès à un fichier texte avec le code JavaScript à l'intérieur, vous demandez à Node.js d'exécuter ce fichier au lieu de démarrer une nouvelle session interactive.
Sur les systèmes de type Unix, vous pouvez même indiquer avec quel programme exécuter le fichier à l'aide d'un case commentaire dans la toute première ligne du fichier:
Noeud!! / usr / bin / env
console.log('Bonjour le monde');
Le commentaire doit être un chemin vers l'exécutable Node.js. Cependant, pour éviter de coder en dur un chemin absolu, qui peut différer selon les installations, il est préférable de laisser l'outil env déterminer où Node.js est installé sur votre machine.
Ensuite, vous devez rendre le fichier exécutable avant de pouvoir l'exécuter comme s'il s'agissait d'un script Python:
$ chmod + x hello.js
$ ./hello.js
Bonjour le monde
La route vers la construction à part entière des applications Web avec Node.js est long et sinueux, mais il en va de même pour l'écriture d'applications Django ou Flask en Python.
Une langue étrangère
Parfois, l'environnement d'exécution de JavaScript peut être un autre langage de programmation. Ceci est typique des langages de script en général. Python, par exemple, est largement utilisé dans brancher développement. Vous le trouverez dans l'éditeur de texte sublime, GIMP et Blender.
Pour vous donner un exemple, vous pouvez évaluer le code JavaScript dans un programme Java à l'aide de l'API de script:
paquet org.example;
importation javax.script.ScriptEngine;
importation javax.script.ScriptEngineManager;
importation javax.script.ScriptException;
Publique classe App
Publique statique néant principale(Chaîne[] args) jette ScriptException
final ScriptEngineManager directeur = Nouveau ScriptEngineManager();
final ScriptEngine moteur = directeur.getEngineByName("javascript");
Système.en dehors.println(moteur.eval("2 + 2"));
Il s'agit d'une extension Java, bien qu'elle ne soit pas disponible sur votre machine virtuelle Java particulière. Les générations Java suivantes regroupent des moteurs de script alternatifs, tels que Rhino, Nashorn et GraalVM.
Pourquoi est-ce utile?
Tant que les performances ne sont pas trop mauvaises, vous pourriez réutiliser le code d'une bibliothèque JavaScript existante au lieu de la réécrire dans une autre langue. Peut-être que résoudre un problème, tel que l'évaluation de l'expression mathématique, serait plus pratique avec JavaScript que votre langue maternelle. Enfin, en utilisant un langage de script pour personnalisation du comportement lors de l'exécution, comme le filtrage ou la validation des données, pourrait être le seul moyen d'aller dans un langage compilé.
JavaScript contre Python
Dans cette section, vous allez comparer Python vs JavaScript du point de vue de Pythonista. Il y aura de nouveaux concepts à venir, mais vous découvrirez également quelques similitudes entre les deux langues.
Cas d'utilisation
Python est un à usage général, multi-paradigme, haut niveau, multiplateforme, langage de programmation interprété avec une bibliothèque standard riche et une syntaxe accessible.
En tant que tel, il est utilisé dans un large éventail de disciplines, y compris l'enseignement de l'informatique, les scripts et l'automatisation, le prototypage, les tests de logiciels, le développement Web, la programmation de périphériques intégrés et l'informatique scientifique. Bien que cela soit faisable, vous ne choisiriez probablement pas Python comme technologie principale pour le développement de jeux vidéo ou d'applications mobiles.
JavaScript, en revanche, est uniquement né d'un côté client langage de script pour rendre les documents HTML un peu plus interactifs. C'est intentionnellement simple et a un objectif singulier: ajouter un comportement aux interfaces utilisateur. C'est encore vrai aujourd'hui malgré ses capacités améliorées. Avec Javascript, vous pouvez créer non seulement des applications Web, mais aussi des programmes de bureau et des applications mobiles. Les environnements d'exécution sur mesure vous permettent d'exécuter JavaScript sur le serveur ou même sur les appareils IoT.
Philosophie
Python met l'accent sur le code lisibilité et maintenabilité au prix de son expressivité. Après tout, vous ne pouvez même pas trop formater votre code sans le casser. Vous ne trouverez pas non plus d'opérateurs ésotériques comme vous le feriez en C ++ ou Perl car la plupart des opérateurs Python sont des mots anglais. Certaines personnes plaisantent que Python est un pseudocode exécutable grâce à sa syntaxe simple.
Comme vous le découvrirez plus tard, JavaScript offre bien plus souplesse mais aussi plus de façons de causer des problèmes. Par exemple, il n'y a pas une seule bonne façon de créer des types de données personnalisés en JavaScript. En outre, la langue doit rester rétrocompatible avec les navigateurs plus anciens, même lorsque la nouvelle syntaxe résout un problème.
Versions
Jusqu'à récemment, vous trouverez deux versions largement incompatibles de Python disponibles en téléchargement sur son site officiel. Cette fracture entre Python 2.7 et Python 3.x était déroutant pour les débutants et a été un facteur majeur dans le ralentissement de l'adoption de la dernière branche de développement.
En janvier 2020, après des années de report de l'échéance, le support de Python 2.7 a finalement été abandonné. Cependant, malgré le manque imminent de mises à jour de sécurité et d'avertissements émis par certaines agences gouvernementales, de nombreux projets n'ont pas encore migré:
Brendan Eich a créé JavaScript en 1995, mais l'ECMAScript que nous connaissons aujourd'hui a été normalisé deux ans plus tard. Depuis lors, il n'y a eu qu'une poignée de versions, ce qui semble stagner par rapport aux multiples nouvelles versions de Python publiées chaque année au cours de la même période.
Remarquez l'écart entre ES3 et ES5, qui a duré une décennie entière! En raison de conflits politiques et de désaccords au sein du comité technique, ES4 n'a jamais fait son chemin vers les navigateurs Web, mais il a été utilisé par Macromedia (plus tard Adobe) comme base pour ActionScript.
La première refonte majeure de JavaScript est intervenue en 2015 avec l'introduction d'ES6, également connu sous le nom ES2015 ou ECMAScript Harmony. Cela a apporté beaucoup de nouvelles constructions syntaxiques, ce qui a rendu le langage plus mature, sûr et pratique pour le programmeur. Cela a également marqué un tournant dans le calendrier de sortie d'ECMAScript, qui promet désormais une nouvelle version chaque année.
Un rythme aussi rapide signifie que vous ne pouvez pas supposer que la dernière version linguistique a été adoptée par tous les principaux navigateurs Web, car le déploiement des mises à jour prend du temps. Voilà pourquoi transpiler et polyfills prévaloir. Aujourd'hui, à peu près n'importe quel navigateur Web moderne peut prendre en charge ES5, qui est la cible par défaut des transpilateurs.
Durée
Pour exécuter un programme Python, vous devez d'abord télécharger, installer et éventuellement configurer son interprète pour votre plateforme. Certains systèmes d'exploitation fournissent un interpréteur prêt à l'emploi, mais ce n'est peut-être pas la version que vous cherchez à utiliser. Il existe d'autres implémentations Python, notamment CPython, PyPy, Jython, IronPython ou Stackless Python. Vous pouvez également choisir parmi plusieurs Python distributions, comme Anaconda, fourni avec des packages tiers préinstallés.
JavaScript est différent. Il n'y a pas de programme autonome à télécharger. Au lieu de cela, tous les principaux navigateurs Web sont livrés avec une sorte de Moteur JavaScript et une API, qui forment ensemble l'environnement d'exécution. Dans la section précédente, vous avez découvert Node.js, qui permet d'exécuter du code JavaScript en dehors du navigateur. Vous connaissez également la possibilité d'incorporer JavaScript dans d'autres langages de programmation.
Écosystème
Un écosystème linguistique comprend son environnement d'exécution, ses frameworks, ses bibliothèques, ses outils et ses dialectes ainsi que ses meilleures pratiques et règles non écrites. La combinaison que vous choisirez dépendra de votre cas d'utilisation particulier.
Autrefois, vous n'aviez pas besoin de beaucoup plus qu'un bon éditeur de code pour écrire JavaScript. Vous devriez télécharger quelques bibliothèques comme jQuery, Underscore.js ou Backbone.js, ou compter sur un réseau de distribution de contenu (CDN) pour les fournir à vos clients. Aujourd'hui, le nombre de questions auxquelles vous devez répondre et les outils dont vous avez besoin pour commencer à créer même le site Web le plus simple peuvent être intimidants.
Le processus de construction d'une application frontale est aussi compliqué que pour une application principale, sinon plus. Votre projet Web passe par le peluchage, la transpilation, le polyfilling, le regroupement, la minification, etc. Heck, même les feuilles de style CSS ne sont plus suffisantes et doivent être compilées à partir d'un langage d'extension par un préprocesseur tel que Sass ou Less.
Pour y remédier, certains frameworks proposent des utilitaires qui configurent la structure de projet par défaut, génèrent des fichiers de configuration et téléchargent des dépendances pour vous. Par exemple, vous pouvez créer une nouvelle application React avec cette courte commande, à condition que vous disposiez déjà du dernier Node.js sur votre ordinateur:
$ npx create-react-app todo
Au moment d'écrire ces lignes, cette commande a pris plusieurs minutes pour terminer et installé un énorme 166 Mo dans 1815 packages! Comparez cela au démarrage d'un projet Django en Python, qui est instantané:
$ blog django-admin startproject
L'écosystème JavaScript moderne est énorme et continue d'évoluer, ce qui rend impossible de donner un aperçu complet de ses éléments. Vous rencontrerez de nombreux outils étrangers pendant que vous apprenez JavaScript. Cependant, les concepts derrière certains d'entre eux vous sembleront familiers. Voici comment les réassocier à Python:
Python | Javascript | |
---|---|---|
Éditeur de code / IDE | PyCharm, VS Code | Atom, VS Code, WebStorm |
Formateur de code | noir |
Plus jolie |
Gestionnaire de dépendances | Pipenv , poésie |
tonnelle , npm , fil |
Outil de documentation | Sphinx |
JSDoc , sphinx-js |
Interprète | bpython , ipython , python |
nœud |
Bibliothèque | demandes , dateutil |
axios , moment |
Linter | flake8 , pyflakes , pylint |
eslint , tslint |
Directeur chargé d'emballage | pépin , ficelle |
tonnelle , npm , fil |
Registre des packages | PyPI | npm |
Package Runner | pipx |
npx |
Gestionnaire d'exécution | pyenv |
nvm |
Outil d'échafaudage | cuisine |
cuisine , Yeoman |
Cadre de test | docteur , nez , pytest |
Jasmin , Plaisanter , Moka |
Cadre Web | Django, flacon, tornade | Angulaire, React, Vue.js |
Cette liste n'est pas exhaustive. En outre, certains des outils mentionnés ci-dessus ont des capacités qui se chevauchent, il est donc difficile de faire une comparaison de pommes à pommes dans chaque catégorie.
Parfois, il n'y a pas d'analogie directe entre Python et JavaScript. Par exemple, bien que vous ayez l'habitude de créer des environnements virtuels isolés pour vos projets Python, Node.js gère cela dès le départ en installant des dépendances dans un dossier local.
Inversement, les projets JavaScript peuvent nécessiter des outils supplémentaires propres au développement frontal. Un tel outil est Babel, qui transpile votre code selon différents plugins regroupés en presets. Il peut gérer des fonctionnalités ECMAScript expérimentales ainsi que TypeScript et même la syntaxe d'extension JSX de React.
Une autre catégorie d’outils est le groupeur de modules, dont le rôle est de consolider plusieurs fichiers sources indépendants en un seul qui peut être facilement consommé par un navigateur Web.
Pendant le développement, vous souhaitez décomposer votre code en modules réutilisables, testables et autonomes. C'est raisonnable pour un programmeur Python expérimenté. Malheureusement, JavaScript n'était pas initialement compatible avec la modularité. Vous devez toujours utiliser un outil distinct pour cela, bien que cette exigence change. Les choix populaires pour les regroupeurs de modules sont webpack, Parcel et Browserify, qui peuvent également gérer des actifs statiques.
Alors vous avez automatisation de la construction des outils tels que Grunt et gulp. Ils sont vaguement similaires à Fabric et Ansible en Python, bien qu'ils soient utilisés localement. Ces outils automatisent les tâches ennuyeuses telles que la copie de fichiers ou l'exécution du transpilateur.
Dans une application monopage à grande échelle (SPA) avec beaucoup d'éléments d'interface utilisateur interactifs, vous pouvez avoir besoin d'une bibliothèque spécialisée telle que Redux ou MobX pour gestion de l'état. Ces bibliothèques ne sont liées à aucune infrastructure frontale particulière mais peuvent être rapidement connectées.
Comme vous pouvez le voir, l'apprentissage de l'écosystème JavaScript est un voyage sans fin.
Modèle de mémoire
Les deux langues tirent parti de la gestion automatique de la mémoire en tas pour éliminer les erreurs humaines et réduire la charge cognitive. Néanmoins, cela ne vous libère pas complètement du risque d’obtenir un fuite de mémoire, et cela ajoute une surcharge de performances.
Remarque: Une fuite de mémoire se produit lorsqu'un morceau de mémoire qui n'est plus nécessaire reste inutilement occupé et qu'il n'y a aucun moyen de le désallouer car il n'est plus accessible à partir de votre code. Une source courante de fuites de mémoire en JavaScript sont les variables globales et les fermetures qui contiennent des références fortes aux objets disparus.
L'implémentation orthodoxe de CPython utilise comptage de références ainsi que non déterministe collecte des ordures (GC) pour gérer les cycles de référence. Parfois, vous pouvez être obligé d'allouer et de récupérer manuellement la mémoire lorsque vous vous lancez dans l'écriture d'un module d'extension C personnalisé.
En JavaScript, l'implémentation réelle de la gestion de la mémoire est également laissée à votre moteur et à votre version particuliers, car ils ne font pas partie des spécifications de langue. La stratégie de base pour la collecte des ordures est généralement la marquer et balayer algorithme, mais diverses techniques d'optimisation existent.
Par exemple, le tas peut être organisé en générations qui séparent les objets éphémères des objets éphémères. Le garbage collection peut s'exécuter simultanément pour décharger le thread principal d'exécution. Une approche incrémentielle peut éviter d’arrêter complètement le programme pendant le nettoyage de la mémoire.
Système de type JavaScript
Vous devez avoir hâte de vous familiariser avec la syntaxe JavaScript, mais examinons d'abord rapidement son système de types. C'est l'un des composants les plus importants qui définissent tout langage de programmation.
Vérification de type
Python et JavaScript sont tous deux typé dynamiquement car ils vérifient les types au moment de l'exécution, lorsque l'application est en cours d'exécution, plutôt qu'au moment de la compilation. C'est pratique car vous n'êtes pas obligé de déclarer le type d'une variable comme int
ou str
:
>>> Les données = 42
>>> Les données = «Ceci est une chaîne»
Ici, vous réutilisez le même nom de variable pour deux types différents d'entités qui ont des représentations distinctes dans la mémoire de l'ordinateur. C'est d'abord un nombre entier, puis c'est un morceau de texte.
Remarque: Il convient de noter que certaines langues typées statiquement, telles que Scala, ne nécessitent pas non plus de déclaration de type explicite tant qu'elles peuvent l'être. déduit du contexte.
La dactylographie dynamique est souvent mal interprétée comme n'ayant aucun type. Cela vient de langues dans lesquelles une variable fonctionne comme une boîte qui ne peut s'adapter qu'à un certain type d'objet. En Python et en JavaScript, les informations de type ne sont pas liées à la variable mais à l'objet vers lequel elle pointe. Une telle variable est simplement un alias, une étiquette ou un pointeur vers un objet en mémoire.
Un manque de déclarations de type est idéal pour le prototypage, mais dans les grands projets, il devient rapidement un goulot d'étranglement du point de vue de la maintenance. La frappe dynamique est moins sécurisée en raison d'un risque plus élevé de bogues non détectés à l'intérieur des chemins d'exécution de code rarement exercés.
De plus, cela rend le raisonnement sur le code beaucoup plus difficile à la fois pour les humains et pour les éditeurs de code. Python a résolu ce problème en introduisant une indication de type, avec laquelle vous pouvez saupoudrer des variables:
Les données: str = «Ceci est une chaîne»
Par défaut, les indications de type ne fournissent qu'une valeur informative, car l'interpréteur Python ne s'en soucie pas lors de l'exécution. However, you can add a separate utility, such as a static type checker, to your tool chain to get an early warning about mismatched types. The type hints are completely optional, which makes it possible to combine dynamically typed code with statically typed code. This approach is known as gradual typing.
The idea of gradual typing was borrowed from TypeScript, which is essentially JavaScript with types that you can transpile back to plain old JavaScript.
Another common feature of both languages is the use of duck typing for testing type compatibility. However, an area where Python vs JavaScript are significantly different is the strength of their type-checking mechanisms.
Python demonstrates strong typing by refusing to act upon objects with incompatible types. For example, you can use the plus (+
) operator to add numbers or to concatenate strings, but you can’t mix the two:
>>> '3' + 2
Traceback (most recent call last):
File "" , line 1, in
TypeError: can only concatenate str (not "int") to str
The interpreter won’t implicitly promote one type to another. You have to decide for yourself and make a suitable type casting manually. If you wanted an algebraic sum, then you’d do this:
To join the two strings together, you’d cast the second operand accordingly:
>>> '3' + str(2)
>>> '32'
JavaScript, on the other hand, uses weak typing, which automatically coerces types according to a set of rules. Unfortunately, these rules are inconsistent and hard to remember as they depend on operator precedence.
Taking the same example as before, JavaScript will implicitly convert numbers to strings when you use the plus (+
) operator:
That’s great as long as it’s the desired behavior. Otherwise, you’ll be pulling your hair out trying to find the root cause of a logical error. But it gets even more funky than that. Let’s see what happens if you change the operator to something else:
Now it’s the other operand that gets converted to a number so the end result isn’t a string. As you can see, weak typing can be quite surprising.
The strength of type checking isn’t just black and white. Python lies somewhere in the middle of this spectrum. For instance, it’ll happily add an integer to a floating-point number, whereas the Swift programming language would raise an error in such a situation.
Remarque: Strong vs weak typing is independent from static vs dynamic typing. For instance, the C programming language is statically and weakly typed at the same time.
To recap, JavaScript is dynamically as well as weakly typed and supports duck typing.
JavaScript Types
In Python, everything is an object, whereas JavaScript makes a distinction between primitive et reference types. They differ in a couple of ways.
First, there are only a few predefined primitive types that you need to care about because you can’t make your own. The majority of built-in data types that come with JavaScript are reference types.
These are the only primitive types available in JavaScript:
booléen
nul
nombre
chaîne
symbol
(since ES6)undefined
On the other hand, here are a handful of reference types that come with JavaScript off the shelf:
Array
Boolean
Date
Map
Number
Object
RegExp
Set
String
Symbol
- (…)
There’s also a proposal to include a new BigInt
numeric type, which some browsers already support, in ES11. Other than that, any custom data types that you might define are going to be reference types.
Variables of primitive types are stored in a special memory area called the stack, which is fast but has a limited size and is short-lived. Conversely, objects with reference types are allocated on the heap, which is only restricted by the amount of physical memory available on your computer. Such objects have a much longer life cycle but are slightly slower to access.
Primitive types are bare values without any attributes or methods to call. However, as soon as you try to access one using dot notation, the JavaScript engine will instantly wrap a primitive value in the corresponding wrapper object:
> 'Lorem ipsum'.longueur
11
Even though a string literal in JavaScript is a primitive data type, you can check its .length
attribute. What happens under the hood is that your code is replaced with a call to the String
object’s constructor:
> Nouveau String('Lorem ipsum').longueur
11
A constructor is a special function that creates a new instance of a given type. You can see that the .length
attribute is defined by the String
object. This wrapping mechanism is known as autoboxing and was copied directly from the Java programming language.
The other and more tangible difference between primitive and reference types is how they’re passed around. Specifically, whenever you assign or pass a value of a primitive type, you actually create a copy of that value in memory. Voici un exemple:
> X = 42
> y = X
> X++ // This is short for x += 1
> console.log(X, y)
43 42
The assignment y = x
creates a new value in memory. Now you have two distinct copies of the number 42
referenced by X
et y
, so incrementing one doesn’t affect the other.
However, when you pass a reference to an object literal, then both variables point to the same entity in memory:
> X = Nom: 'Person1'
> y = X
> X.Nom = 'Person2'
> console.log(y)
Nom: 'Person2'
Object
is a reference type in JavaScript. Here, you’ve got two variables, X
et y
, referring to the same instance of a Person
object. The change made to one of the variables is reflected in the other variable.
Last but not least, primitive types are immutable, which means that you can’t change their state once they are initialized. Every modification, such as incrementing a number or making text uppercase, results in a brand-new copy of the original value. While this is a bit wasteful, there are plenty of good reasons to use immutable values, including thread safety, simpler design, and consistent state management.
Remarque: To be fair, this is almost identical to how Python deals with passing objects despite its lack of primitive types. Mutable types such as liste
et dict
don’t create copies, whereas immutable types such as int
et str
do.
To check if a variable is a primitive type or a reference type in JavaScript, you can use the built-in typeof
operator:
> typeof 'Lorem ipsum'
'string'
> typeof Nouveau String('Lorem ipsum')
'object'
For reference types, the typeof
operator always returns a generic "object"
string.
Remarque: Always use the typeof
operator to check if a variable is undefined
. Otherwise, you may find yourself in trouble:
> typeof noSuchVariable === 'undefined'
vrai
> noSuchVariable === undefined
ReferenceError: noSuchVariable est ne pas defined
Comparing a non-existing variable to any value will throw an exception!
If you want to obtain a more detailed information about a particular type, then you have a couple of options:
> aujourd'hui = Nouveau Date()
> aujourd'hui.constructor.Nom
'Date'
> aujourd'hui instanceof Date
vrai
> Date.prototype.isPrototypeOf(aujourd'hui)
vrai
You can try checking an object’s constructor name using the instanceof
operator, or you can test if it’s derived from a particular parent type with the .prototype
propriété.
Type Hierarchy
Python and JavaScript are object-oriented programming languages. They both allow you to express code in terms of objects that encapsulate identity, state, and behavior. While most programming languages, including Python, use class-based inheritance, JavaScript is one of a few that don’t.
Remarque: A class is a template for objects. You can think of classes like cookie cutters or object factories.
To create hierarchies of custom types in JavaScript, you need to become familiar with prototypal inheritance. That is often one the most challenging concepts to understand when you make a switch from a more classical inheritance model. If you have twenty minutes, then you can watch a great video on prototypes that clearly explains the concept.
Remarque: Contrary to Python, multiple inheritance isn’t possible in JavaScript because any given object can have only one prototype. That said, you can use proxy objects, which were introduced in ES6, to mitigate that.
The gist of the story is that there are no classes in JavaScript. Well, technically, you can use the classe
keyword that was introduced in ES6, but it’s purely a syntactic sugar to make things easier for newcomers. Prototypes are still used behind the scenes, so it’s worthwhile to get a closer look at them, which you’ll have a chance to do later on.
Function Type
Enfin, les fonctions are an interesting part of the JavaScript and Python type systems. In both languages, they’re often referred to as first-class citizens or first-class objects because the interpreter doesn’t treat them any differently than other data types. You can pass a function as an argument, return it from another function, or store it in a variable just like a regular value.
This is a very powerful feature that allows you to define higher-order functions and to take full advantage of the functional paradigm. For languages in which functions are special entities, you can work around this with the help of design patterns such as the strategy pattern.
JavaScript is even more flexible than Python in regard to functions. You can define an anonymous function expression full of statements with side effects, whereas Python’s lambda function must contain exactly one expression and no statements:
let countdown = 5;
const id = setInterval(function()
si (countdown > 0)
console.log("$ countdown-...`);
else si (countdown === 0)
console.log('Go!');
clearInterval(id);
, 1000);
The built-in setInterval()
lets you execute a given function periodically in time intervals expressed in milliseconds until you call clearInterval()
with the corresponding ID. Notice the use of a conditional statement and the mutation of a variable from the outer scope of the function expression.
JavaScript Syntax
JavaScript and Python are both high-level scripting languages that share a fair bit of syntactical similarities. This is especially true of their latest versions. That said, JavaScript was designed to resemble Java, whereas Python was modeled after the ABC and Modula-3 languages.
Code Blocks
One of the hallmarks of Python is the use of mandatory indentation to denote a block of code, which is quite unusual and frowned upon by new Python converts. Many popular programming languages, including JavaScript, use curly brackets or special keywords instead:
function fib(n)
si (n > 1)
revenir fib(n-2) + fib(n-1);
revenir 1;
In JavaScript, every block of code consisting of more than one line needs an opening and a closing
, which gives you the freedom to format your code however you like. You can mix tabs with spaces and don’t need to pay attention to your bracket placement.
Unfortunately, this can result in messy code and sectarian conflicts between developers with different style preferences. This makes code reviews problematic. Therefore, you should always establish coding standards for your team and use them consistently, preferably in an automated way.
Remarque: You could simplify the function body above by taking advantage of the ternary if (?:
), which is sometimes called the Elvis operator because it looks like the hairstyle of the famous singer:
revenir (n > 1) ? fib(n-2) + fib(n-1) : 1;
This is equivalent to a conditional expression in Python.
Speaking of indentation, it’s customary for JavaScript code to be formatted using two spaces per indentation level instead of the recommended four in Python.
Déclarations
To reduce friction for those making a switch from Java or another C-family programming language, JavaScript terminates statements with a familiar semicolon (;
). If you’ve ever programmed in one of those languages, then you’ll know that putting a semicolon after an instruction becomes muscle memory:
Semicolons aren’t required in JavaScript, though, because the interpreter will take a guess and insert one for you automatically. In most cases, it’ll be right, but sometimes it may lead to peculiar results.
Remarque: You can use semicolons in Python, too! Although they’re not very popular, they help isolate multiple statements on a single line:
importation pdb; pdb.set_trace()
People have strong opinions on whether to use the semicolon explicitly or not. While there are a few corner cases in which it matters, it’s largely just a convention.
Identifiers
Identifiers, such as variable or function names, must be alphanumeric in JavaScript and Python. In other words, they can only contain letters, digits, and a few special characters. At the same time, they can’t start with a digit. While non-Latin characters are allowed, you should generally avoid them:
- Legal:
foo
,foo42
,_foo
,$foo
,fößar
- Illegal:
42foo
Names in both languages are case sensitive, so variables like foo
et Foo
are distinct. Nonetheless, the naming conventions in JavaScript are slightly different than in Python:
Python | JavaScript | |
---|---|---|
Type | ProjectMember |
ProjectMember |
Variable, Attribute, or Function | first_name |
firstName |
In general, Python recommends using lower_case_with_underscores, also known as snake_case, for compound names, so that individual words get separated with an underscore character (_
). The only exception to that rule is classes, whose names should follow the CapitalizedWords, or Pascal case, style. JavaScript also uses CapitalizedWords for types but mixedCase, or lower camelCase, for everything else.
String Literals
To define string literals in JavaScript, you can use a pair of single quotes ("
) or double quotes ("
) interchangeably, just like in Python. However, for a long time, there was no way to define multiline strings in JavaScript. Only ES6 in 2015 brought template literals, which look like a hybrid of f-strings and multiline strings borrowed from Python:
var Nom = 'John Doe';
var message = `Hi $ Nom.split(' ')[[[[0],
We're writing to you regarding...
Kind regards,
Xyz
";
A template starts with a backtick (`), also known as the grave accent, instead of regular quotes. À interpolate a variable or any legal expression, you have to use the dollar sign followed by a pair of matching curly brackets: $...
. This is different from Python’s f-strings, which don’t require the dollar sign.
Variable Scopes
When you define a variable in JavaScript the same way that you would normally do in Python, you’re implicitly creating a global variable. Since global variables break encapsulation, you should rarely need them! The correct way to declare variables in JavaScript has always been through the var
keyword:
X = 42; // This is a global variable. Did you really mean that?
var y = 15; // This is global only when declared in a global context.
Unfortunately, this doesn’t declare a truly local variable, and it has its own problems that you’ll find out about in the upcoming section. Since ES6, there’s been a better way to declare les variables et constants avec le let
et const
keywords, respectively:
> let Nom = 'John Doe';
> const PI = 3.14;
> PI = 3.1415;
TypeError: Assignment à constant variable.
Unlike constants, variables in JavaScript don’t need an initial value. You can provide one later:
let Nom;
Nom = 'John Doe';
When you leave off the initial value, you create what’s called a variable declaration rather than a variable définition. Such variables automatically receive a special value of undefined
, which is one of the primitive types in JavaScript. This is different in Python, where you always define variables except for variable annotations. But even then, these variables aren’t technically declared:
Nom: str
Nom = 'John Doe'
Such an annotation doesn’t affect the variable life cycle. If you referred to Nom
before the assignment, then you’d receive a NameError
exception.
Switch Statements
If you’ve been complaining about Python not having a proper switch statement, then you’ll be happy to learn that JavaScript does:
// As with C, clauses will fall through unless you break out of them.
commutateur (expression)
Cas 'kilo':
valeur = bytes / 2**dix;
break;
Cas 'mega':
valeur = bytes / 2**20;
break;
Cas 'giga':
valeur = bytes / 2**30;
break;
default:
console.log(`Unknown unit: "$ expression"`);
The expression can evaluate to any type, including a string, which wasn’t always the case in the older Java versions that influenced JavaScript. By the way, did you notice the familiar exponentiation operator (**
) in the code snippet above? It wasn’t available in JavaScript until ES7 in 2016.
Enumerations
There’s no native enumeration type in pure JavaScript, but you can use the enum
type in TypeScript or emulate one with something similar to this:
const Sauce = Object.freeze(
BBQ: Symbol('bbq'),
CHILI: Symbol('chili'),
GARLIC: Symbol('garlic'),
KETCHUP: Symbol('ketchup'),
MUSTARD: Symbol('mustard')
);
Freezing an object prevents you from adding or removing its attributes. This is different from a constant, which can be mutable! A constant will always point to the same object, but the object itself might change its value:
> const des fruits = [[[['apple', 'banana'];
> des fruits.push('orange'); // ['apple', 'banana', 'orange']
> des fruits = [];
TypeError: Assignment à constant variable.
You can add an orange
to the array, which is mutable, but you can’t modify the constant that is pointing to it.
Arrow Functions
Until ES6, you could only define a function or an anonymous function expression using the function
keyword:
function ajouter(une, b)
revenir une + b;
let ajouter = function(une, b)
revenir une + b;
;
However, to reduce the boilerplate code and to fix a slight problem with binding functions to objects, you can now use the arrow function in addition to the regular syntax:
let ajouter = (une, b) => une + b;
Notice that there’s no function
keyword anymore, and the return statement is implicit. The arrow symbol (=>
) separates the function’s arguments from its body. People sometimes call it the graisse arrow function because it was originally borrowed from CoffeeScript, which also has a mince arrow (->
) counterpart.
Arrow functions are most suitable for small, anonymous expressions like lambdas in Python, but they can contain multiple statements with side effects if needed:
let ajouter = (une, b) =>
const résultat = une + b;
revenir résultat;
When you want to return an object literal from an arrow function, you need to wrap it in parentheses to avoid ambiguity with a block of code:
let ajouter = (une, b) => (
résultat: une + b
);
Otherwise, the function body would be confused for a block of code without any revenir
statements, and the colon would create a labeled statement rather than a key-value pair.
Default Arguments
Starting with ES6, function arguments can have default values like in Python:
> function greet(Nom = 'John')
… console.log('Hello', Nom);
…
> greet();
Hello John
Unlike Python, however, the default values are resolved every time the function is called instead of only when it’s defined. This makes it possible to safely use mutable types as well as to dynamically refer to other arguments passed at runtime:
> function foo(une, b=une+1, c=[])
… c.push(une);
… c.push(b);
… console.log(c);
…
> foo(1);
[[[[1, 2]
> foo(5);
[[[[5, 6]
Every time you call foo()
, its default arguments are derived from the actual values passed to the function.
Variadic Functions
When you want to declare a function with variable number of parameters in Python, you take advantage of the special *args
syntax. The JavaScript equivalent would be the rest parameter defined with the spread (...
) operator:
> function moyenne(...Nombres)
… si (Nombres.longueur > 0)
… const sum = Nombres.reduce((une, X) => une + X);
… revenir sum / Nombres.longueur;
…
… revenir 0;
…
> moyenne();
0
> moyenne(1);
1
> moyenne(1, 2);
1,5
> moyenne(1, 2, 3);
2
The spread operator can also be used to combine iterable sequences. For example, you can extract the elements of one array into another:
const redFruits = [[[['apple', 'cherry'];
const des fruits = [[[['banana', ...redFruits];
Depending on where you place the spread operator in the target list, you may prepend or append elements or insert them somewhere in the middle.
Destructuring Assignments
To unpack an iterable into individual variables or constants, you can use the destructuring assignment:
> const des fruits = [[[['apple', 'banana', 'orange'];
> const [[[[une, b, c] = des fruits;
> console.log(b);
banana
Similarly, you can destructure and even rename object attributes:
const person = Nom: 'John Doe', âge: 42, marié: vrai;
const Nom: fullName, âge = person;
console.log("$ fullName est $ âge years old.`);
This helps avoid name collisions for variables defined within one scope.
avec
Déclarations
There’s an alternative way to drill down to an object’s attributes using the slightly old avec
statement:
const person = Nom: 'John Doe', âge: 42, marié: vrai;
avec (person)
console.log("$ Nom est $ âge years old.`);
It works like a construct in Object Pascal, in which a local scope gets temporarily augmented with attributes of the given object.
Remarque: le avec
statements in Python vs JavaScript are false friends. In Python, you use a avec
statement to manage resources through context managers.
Since this might be obscure, the avec
statement is generally discouraged and is even unavailable in strict mode.
Iterables, Iterators, and Generators
Since ES6, JavaScript has had the iterable et iterator protocols as well as generator functions, which look almost identical to Python’s iterables, iterators, and generators. To turn a regular function into a generator function, you need to add an asterisk (*
) after the function
keyword:
function* makeGenerator()
You can’t make generator functions out of arrow functions, though.
When you call a generator function, it won’t execute the body of that function. Instead, it returns a suspended generator object that conforms to the iterator protocol. To advance your generator, you can call .next()
, which is similar to Python’s built-in next()
:
> const generator = makeGenerator();
> const valeur, terminé = generator.next();
> console.log(valeur);
undefined
> console.log(terminé);
vrai
As a result, you’ll always get a status object with two attributes: the subsequent value and a flag that indicates if the generator has been exhausted. Python throws the StopIteration
exception when there are no more values in the generator.
To return some value from your generator function, you can use either the yield
keyword or the revenir
keyword. The generator will keep feeding values until there are no more yield
statements, or until you revenir
prematurely:
let shouldStopImmediately = false;
function* randomNumberGenerator(maxTries=3)
let tries = 0;
tandis que (tries++ < maxTries)
si (shouldStopImmediately)
revenir 42; // The value is optional
yield Math.random();
The above generator will keep yielding random numbers until it reaches the maximum number of tries or you set a flag to make it terminate early.
The equivalent of the yield from
expression in Python, which delegates the iteration to another iterator or an iterable object, is the yield*
expression:
> function* makeGenerator()
… yield 1;
… yield* [[[[2, 3, 4];
… yield 5;
…
> const generator = makeGenerator()
> generator.next();
valeur: 1, terminé: false
> generator.next();
valeur: 2, terminé: false
> generator.next();
valeur: 3, terminé: false
> generator.next();
valeur: 4, terminé: false
> generator.next();
valeur: 5, terminé: false
> generator.next();
valeur: undefined, terminé: vrai
Interestingly, it’s legal to revenir
et yield
at the same time:
function* makeGenerator()
revenir yield 42;
However, due to a grammar limitation, you’d have to use parentheses to achieve the same effect in Python:
def make_generator():
revenir (yield 42)
To explain what’s going on, you can rewrite that example by introducing a helper constant:
function* makeGenerator()
const message = yield 42;
revenir message;
If you know coroutines in Python, then you’ll remember that generator objects can be both les producteurs et consumers. You can send arbitrary values into a suspended generator by providing an optional argument to .next()
:
> function* makeGenerator()
… const message = yield 'ping';
… revenir message;
…
> const generator = makeGenerator();
> generator.next();
valeur: "ping", terminé: false
> generator.next('pong');
valeur: "pong", terminé: vrai
The first call to .next()
runs the generator until the first yield
expression, which happens to return "ping"
. The second call passes a "pong"
that is stored in the constant and immediately returned from the generator.
Asynchronous Functions
The nifty mechanism explored above was the basis for asynchronous programming and the adoption of the async
et await
keywords in Python. JavaScript followed the same path by bringing in asynchronous functions with ES8 in 2017.
While a generator function returns a special kind of iterator, the generator object, asynchronous functions always return a promettre, which was first introduced in ES6. A promise represents the future result of an asynchronous call such as fetch()
from the Fetch API.
When you return any value from an asynchronous function, it’s automatically wrapped in a promise object that can be awaited in another asynchronous function:
async function greet(Nom)
revenir `Hello $ Nom";
async function main()
const promettre = greet('John');
const salutation = await promettre;
console.log(salutation); // "Hello John"
main();
Typically, you’d await
and assign the result in one go:
const salutation = await greet('John');
Although you can’t completely get rid of promises with asynchronous functions, they significantly improve your code readability. It starts to look like synchronous code even though your functions can be paused and resumed multiple times.
One notable difference from the asynchronous code in Python is that, in JavaScript, you don’t need to manually set up the event loop, which runs in the background implicitly. JavaScript is inherently asynchronous.
Objects and Constructors
You know from an earlier part of this article that JavaScript doesn’t have a concept of classes. Instead, it knows about objects. You can create new objects using object literals, which look like Python dictionaries:
let person =
Nom: 'John Doe',
âge: 42,
marié: vrai
;
It behaves like a dictionary in that you can access individual attributes using dot syntax or square brackets:
> person.âge++;
> person[[[['age'];
43
Object attributes don’t need to be enclosed in quotes unless they contain spaces, but that isn’t a common practice:
> let person =
… 'full name': 'John Doe'
… ;
> person[[[['full name'];
'John Doe'
> person.plein Nom;
SyntaxError: Unexpected identifiant
Just like a dictionary and some objects in Python, objects in JavaScript have dynamic attributes. That means you can add new attributes or delete existing ones from an object:
> let person = Nom: 'John Doe';
> person.âge = 42;
> console.log(person);
Nom: "John Doe", âge: 42
> supprimer person.Nom;
vrai
> console.log(person);
âge: 42
Starting from ES6, objects can have attributes with computed names:
> let person =
… [[[['full' + 'Name']: 'John Doe'
… ;
> person.fullName;
'John Doe'
Python dictionaries and JavaScript objects are allowed to contain functions as their keys and attributes. There are ways to bind such functions to their owner so that they behave like class methods. For example, you can use a circular reference:
> let person =
… Nom: 'John Doe',
… sayHi: function()
… console.log(`Hi, my name is $ person.Nom.`);
…
… ;
> person.sayHi();
salut, mon Nom est John Doe.
sayHi()
is tightly coupled to the object it belongs to because it refers to the person
variable by name. If you were to rename that variable at some point, then you’d have to go through the whole object and make sure to update all occurrences of that variable name.
A slightly better approach takes advantage of the implicit cette
variable that is exposed to functions. The value of cette
can be different depending on who’s calling the function:
> let jdoe =
… Nom: 'John Doe',
… sayHi: function()
… console.log(`Hi, my name is $ cette.Nom.`);
…
… ;
> jdoe.sayHi();
salut, mon Nom est John Doe.
After replacing a hard-coded person
avec cette
, which is similar to Python’s self
, it won’t matter what the variable name is, and the result will be the same as before.
Remarque: The example above won’t work if you replace the function expression with an arrow function, because the latter has different scoping rules for the cette
variable.
That’s great, but as soon as you decide to introduce more objects of the same Person
kind, you’ll have to repeat all attributes and redefine all functions in each object. What you’d rather have is a template for Person
objects.
The canonical way of creating custom data types in JavaScript is to define a constructor, which is an ordinary function:
function Person()
console.log('Calling the constructor');
As a convention, to denote that such a function has a special meaning, you’d capitalize the first letter to follow CapitalizedWords instead of the usual mixedCase.
On the syntactical level, however, it’s just a function that you can call normally:
> Person();
Calling le constructor
undefined
What makes it special is Comment you call it:
> Nouveau Person();
Calling le constructor
Person
When you add the Nouveau
keyword in front of the function call, it’ll implicitly return a brand-new instance of a JavaScript object. That means your constructor shouldn’t contain the revenir
déclaration.
While the interpreter is responsible for allocating memory for and scaffolding a new object, the role of a constructor is to give the object an initial state. You can use the previously mentioned cette
keyword to refer to a new instance under construction:
function Person(Nom)
cette.Nom = Nom;
cette.sayHi = function()
console.log(`Hi, my name is $ cette.Nom.`);
Now you can create multiple distinct Person
entities:
const jdoe = Nouveau Person('John Doe');
const jsmith = Nouveau Person('John Smith');
Alright, but you’re still duplicating function definitions across all instances of the Person
type. The constructor is just a factory that hooks the same values to individual objects. It’s wasteful and could lead to inconsistent behavior if you were to change it at some point. Consider this:
> const jdoe = Nouveau Person('John Doe');
> const jsmith = Nouveau Person('John Smith');
> jsmith.sayHi = _ => console.log('What?');
> jdoe.sayHi();
salut, mon Nom est John Doe.
> jsmith.sayHi();
Quoi?
Since every object gets a copy of its attributes, including functions, you must carefully update all instances to keep a uniform behavior. Otherwise, they’ll do different things, which typically isn’t what you want. Objects might have a different state, but their behavior usually won’t change.
Prototypes
As a rule of thumb, you should move the business logic from the constructor, which is concerned about data, to the prototype object:
function Person(Nom)
cette.Nom = Nom;
Person.prototype.sayHi = function()
console.log(`Hi, my name is $ cette.Nom.`);
;
Every object has a prototype. You can access your custom data type’s prototype by referring to the .prototype
attribute of your constructor. It’ll already have a few predefined attributes, such as .toString()
, that are common to all objects in JavaScript. You can add more attributes with your custom methods and values.
When JavaScript is looking for an object’s attribute, it begins by trying to find it in that object. Upon failure, it moves on to the respective prototype. Therefore, attributes defined in a prototype are shared across all instances of the corresponding type.
Prototypes are chained, so the attribute lookup continues until there are no more prototypes in the chain. This is analogous to type hierarchy through inheritance.
Not only can you create methods in one place thanks to the prototypes, but you can also create static attributes by attaching them to one:
> Person.prototype.PI = 3.14;
> Nouveau Person('John Doe').PI;
3.14
> Nouveau Person('John Smith').PI;
3.14
To illustrate the power of prototypes, you may try to extend the behavior of existing objects, or even a built-in data type. Let’s add a new method to the chaîne
type in JavaScript by specifying it in the prototype object:
String.prototype.toSnakeCase = function()
revenir cette.replace(/s+/g, '')
.split(/(?<=[a-z])(?=[A-Z])/g)
.map(X => X.toLowerCase())
.joindre('_');
;
It uses regular expressions to transform the text into snake_case. Suddenly, string variables, constants, and even string literals can benefit from it:
> "loremIpsumDolorSit".toSnakeCase();
'lorem_ipsum_dolor_sit'
However, this is a double-edged sword. In a similar way, someone could override one of the existing methods in a prototype of a popular type, which would break the assumptions made elsewhere. Such monkey patching can be useful in testing but is otherwise very dangerous.
Des classes
Since ES6, there’s been an alternative way to define prototypes that uses a much more familiar syntax:
classe Person
constructor(Nom)
cette.Nom = Nom;
sayHi()
console.log(`Hi, my name is $ cette.Nom.`);
Even though this looks like you’re defining a classe, it’s only a convenient high-level metaphor for specifying custom data types in JavaScript. Behind the scenes, there are no real classes! For that reason, some people go so far as to advocate against using this new syntax at all.
Vous pouvez avoir getters et setters in your class, which are similar to the Python’s class properties:
> classe Carré
… constructor(size)
… cette.size = size; // Triggers the setter
…
… ensemble size(valeur)
… cette._size = valeur; // Sets the private field
…
… get région()
… revenir cette._size**2;
…
…
> const box = Nouveau Carré(3);
> console.log(box.région);
9
> box.size = 5;
> console.log(box.région);
25
When you omit the setter, you create a read-only property. That’s misleading, however, because you can still access the underlying privé
field like you can in Python.
A common pattern to encapsulate internal implementation in JavaScript is an Immediately Invoked Function Expression (IIFE), which can look like this:
> const odometer = (function(initiale)
… let mileage = initiale;
… revenir
… get: function() revenir mileage; ,
… mettre: function(miles) mileage += miles;
… ;
… )(33000);
> odometer.mettre(65);
> odometer.mettre(12);
> odometer.get();
33077
In other words, it’s an anonymous function that calls itself. You can use the newer arrow function to create an IIFE too:
const odometer = ((initiale) =>
let mileage = initiale;
revenir
get: _ => mileage,
mettre: (miles) => mileage += miles
;
)(33000);
This is how JavaScript historically emulated modules to avoid name collisions in the global namespace. Without an IIFE, which uses closures and function scope to expose only a limited public-facing API, everything would be accessible from the calling code.
Sometimes you want to define a factory or a utility function that logically belongs to your class. In Python, you have the @classmethod
et @staticmethod
decorators, which allow you to associate static methods with the class. To achieve the same result in JavaScript, you need to use the statique
method modifier:
classe Couleur
statique brown()
revenir Nouveau Couleur(244, 164, 96);
statique mix(color1, color2)
revenir Nouveau Couleur(...color1.channels.map(
(X, je) => (X + color2.channels[[[[je]) / 2
));
constructor(r, g, b)
cette.channels = [[[[r, g, b];
const color1 = Couleur.brown();
const color2 = Nouveau Couleur(128, 0, 128);
const blended = Couleur.mix(color1, color2);
Note that there’s no way of defining static class attributes at the moment, at least not without additional transpiler plugins.
Chaining prototypes can resemble class inheritance quand vous extend
one class from another:
classe Person
constructor(firstName, lastName)
cette.firstName = firstName;
cette.lastName = lastName;
fullName()
revenir "$ cette.firstName $ cette.lastName";
classe Gentleman étend Person
signature()
revenir 'Monsieur. " + super.fullName()
In Python, you could extend more than one class, but that it isn’t possible in JavaScript. To reference attributes from the parent class, you can use super()
, which has to be called in the constructor to pass arguments up the chain.
Decorators
Decorators are yet another feature that JavaScript copied from Python. They’re still technically a proposal that is subject to change, but you can test them out using an online playground or a local transpiler. Be warned, however, that they require a bit of configuration. Depending on the chosen plugin and its options, you’ll get different syntax and behavior.
Several frameworks already use a custom syntax for decorators, which need to be transpiled into plain JavaScript. If you opt for the TC-39 proposal, then you’ll be able to decorate only classes and their members. It seems there won’t be any special syntax for function decorators in JavaScript.
JavaScript Quirks
It took ten days for Brendan Eich to create a prototype of what later became JavaScript. After it was presented to the stakeholders at a business meeting, the language was considered production ready and didn’t go through a lot of changes for many years.
Unfortunately, that made the language infamous for its oddities. Some people didn’t even regard JavaScript as a “real” programming language, which made it a victim of many jokes and memes.
Today, the language is much friendlier than it used to be. Nevertheless, it’s worth knowing what to avoid, since a lot of legacy JavaScript is still out there waiting to bite you.
Bogus Array
Python’s lists and tuples are implemented as arrays in the traditional sense, whereas JavaScript’s Array
type has more in common with Python’s dictionary. What’s an array, then?
In computer science, an array is a data structure that occupies a contiguous block of memory, and whose elements are ordered and have homogeneous sizes. This way, you can access them randomly with a numerical index.
In Python, a list is an array of pointers that are typically integer numbers, which reference heterogeneous objects scattered around in various regions of memory.
Remarque: For low-level arrays in Python, you might be interested in checking out the built-in array
module.
JavaScript’s array is an object whose attributes happen to be numbers. They’re not necessarily stored next to each other. However, they keep the right order during iteration.
When you delete an element from an array in JavaScript, you make a gap:
> const des fruits = [[[['apple', 'banana', 'orange'];
> supprimer des fruits[[[[1];
vrai
> console.log(des fruits);
[[[['apple', empty, 'orange']
> des fruits[[[[1];
undefined
The array doesn’t change its size after the removal of one of its elements:
> console.log(des fruits.longueur);
3
Conversely, you can put a new element at a distant index even though the array is much shorter:
> des fruits[[[[dix] = 'watermelon';
> console.log(des fruits.longueur);
11
> console.log(des fruits);
[[[['apple', empty, 'orange', empty × 7, 'watermelon']
This wouldn’t work in Python.
Array Sorting
Python is clever about sorting data because it can tell the difference between element types. When you sort a list of numbers, for example, it’ll put them in ascending order by default:
>>> sorted([[[[53, 2020, 42, 1918, 7])
[7, 42, 53, 1918, 2020]
However, if you wanted to sort a list of strings, then it would magically know how to compare the elements so that they appear in lexicographical order:
>>> sorted([[[['lorem', 'ipsum', 'dolor', 'sit', 'amet'])
['amet', 'dolor', 'ipsum', 'lorem', 'sit']
Things get complicated when you start to mix different types:
>>> sorted([[[[42, 'not a number'])
Traceback (most recent call last):
File "" , line 1, in
TypeError: '<' not supported between instances of 'str' and 'int'
By now, you know that Python is a strongly typed language and doesn’t like mixing types. JavaScript, on the other hand, is the opposite. It’ll eagerly convert elements of incompatible types according to some obscure rules.
Vous pouvez utiliser .sort()
to do the sorting in JavaScript:
> [[[['lorem', 'ipsum', 'dolor', 'sit', 'amet'].Trier();
[[[['amet', 'dolor', 'ipsum', 'lorem', 'sit']
It turns out that sorting strings works as expected. Let’s see how it copes with numbers:
> [[[[53, 2020, 42, 1918, 7].Trier();
[[[[1918, 2020, 42, 53, 7]
What happened here is that the array elements got implicitly converted to strings and were sorted lexicographically. To prevent that, you have to provide your custom sorting stratégie as a function of two elements to compare, for example:
> [[[[53, 2020, 42, 1918, 7].Trier((une, b) => une - b);
[[[[7, 42, 53, 1918, 2020]
The contract between your strategy and the sorting method is that your function should return one of three values:
- Zero when the two elements are equal
- A positive number when elements need to be swapped
- A negative number when the elements are in the right order
This is a common pattern present in other languages, and it was also the old way of sorting in Python.
Automatic Semicolon Insertion
At this point, you know that semicolons in JavaScript are optional because the interpreter will insert them automatically at the end of each instruction if you don’t do so yourself.
This can lead to surprising results under some circumstances:
function makePerson(Nom)
revenir
(
fullName: Nom,
createdAt: Nouveau Date()
)
In this example, you might expect the JavaScript engine to insert a missing semicolon at the very end of your function, right after the closing parenthesis of the object literal. However, when you call the function, this happens:
> const jdoe = makePerson('John Doe');
> console.log(jdoe);
undefined
Your function changed the intended action by returning an undefined
parce que deux semicolons were inserted instead of one:
function makePerson(Nom)
revenir;
(
fullName: Nom,
createdAt: Nouveau Date()
);
As you can see, relying on the fact that semicolons are optional introduces some risk of errors in your code. On the other hand, it won’t help if you start putting semicolons everywhere.
To fix this example, you need to change your code formatting so that the returned value begins on the same line as the revenir
statement:
function makePerson(Nom)
revenir
fullName: Nom,
createdAt: Nouveau Date()
;
In some situations, you can’t rely on automatic semicolon insertion and you need to put one explicitly instead. For example, you can’t leave out the semicolon when you start a new line with a parenthesis:
const total = 2 + 3
(4 + 5).toString()
This produces a runtime error due to the lack of a semicolon, which makes the two lines collapse into one:
const total = 2 + 3(4 + 5).toString();
A numeric literal can’t be called like a function.
Confusing Loops
Loops in JavaScript are particularly confusing because there are so many of them and they look alike, whereas Python has just two. The primary type of loop in JavaScript is the pour
loop, which was transplanted from Java:
const des fruits = [[[['apple', 'banana', 'orange'];
pour (let je = 0; je < des fruits.longueur; je++)
console.log(des fruits[[[[je]);
It has three parts, all of which are optional:
- Initialization:
let i = 0
- État:
i < fruits.length
- Cleanup:
i++
The first part executes only once before the loop starts, and it typically sets the initial value for the counter. Then, after each iteration, the cleanup part runs to update the counter. Right after that, the condition is evaluated to determine if the loop should continue. This is roughly equivalent to iterating over a list of indices in Python:
des fruits = [[[['apple', 'banana', 'orange']
pour je dans gamme(len(des fruits)):
impression(des fruits[[[[je])
Notice how much work Python does for you. On the other hand, having the loop internals exposed gives you a lot of flexibility. This type of loop is generally deterministic because you know how many times it’ll iterate from the beginning.
In JavaScript, you can make the conventional pour
loop non-deterministic and even infinite by omitting one or more of its parts:
pour (;;)
// An infinite loop
However, a more idiomatic way to make such an iteration would involve the tandis que
loop, which is quite similar to the one you’d find in Python:
tandis que (vrai)
const âge = prompt('How old are you?');
si (âge >= 18)
break;
In addition to this, JavaScript has a do...while
loop, which is guaranteed to run at least once because it checks the condition after its body. You can rewrite this example in the following way:
let âge;
faire
âge = prompt('How old are you?');
tandis que (âge < 18);
Apart from stopping an iteration midway with the break
keyword, you can skip to the next iteration using the continue
keyword as you would in Python:
pour (let je = 0; je < dix; je++)
si (je % 2 === 0)
continue;
console.log(je);
What you can’t do, though, is use the else
clause on loops.
You might be tempted to try out the for...in
loop in JavaScript, thinking it would iterate over values like a Python pour
loop. Although it looks similar and has a similar name, it actually behaves very differently!
UNE for...in
loop in JavaScript iterates over attributes of the given object, including the ones found in the prototype chain:
> const object = Nom: 'John Doe', âge: 42;
> pour (const attribute dans object)
… console.log("$ attribute = $ object[[[[attribute]");
…
Nom = John Doe
âge = 42
Should you want to exclude attributes attached to the prototype, you can call hasOwnProperty()
. It will test whether a given attribute belongs to an object instance.
When you feed the for...in
loop with an array, it’ll iterate over the array’s numeric indices. As you know by now, arrays in JavaScript are just glorified dictionaries:
> const des fruits = [[[['apple', 'banana', 'orange'];
… pour (const fruit dans des fruits)
… console.log(fruit);
…
0
1
2
On the other hand, arrays expose .forEach()
, which can substitute for a loop:
const des fruits = [[[['apple', 'banana', 'orange'];
des fruits.forEach(fruit => console.log(fruit));
This is a higher-order function that accepts a callback that will run for every element in the array. This pattern fits a bigger picture since JavaScript takes a functional approach to iteration in general.
Remarque: To test if a single attribute is defined in an object, use the dans
operator:
> 'toString' dans [[[[1, 2, 3];
vrai
> '__str__' dans [[[[1, 2, 3];
false
Finally, when the ES6 specification introduced the iterable and iterator protocols, it allowed the implementation of a long-awaited loop that would iterate over sequences. However, since the for...in
name was already taken, they had to come up with a different one.
le for...of
loop is the closest relative to the pour
loop in Python. With it, you can iterate over any iterable object, including strings and arrays:
const des fruits = [[[['apple', 'banana', 'orange'];
pour (const fruit de des fruits)
console.log(fruit);
This is probably the most intuitive way for a Python programmer to iterate in JavaScript.
Constructor Without Nouveau
Let’s go back to the Person
type defined earlier:
function Person(Nom)
cette.Nom = Nom;
cette.sayHi = function()
console.log(`Hi, my name is $ cette.Nom.`);
If you forget to call that constructor correctly, with the Nouveau
keyword in front of it, then it’ll fail silently and leave you with an undefined
variable:
> let bob = Person('Bob');
> console.log(bob);
undefined
There’s a trick to protect yourself against this mistake. When you omit the Nouveau
keyword, there won’t be any object to bind to, so the cette
variable inside the constructor will point to the global object, such as the window
object in the web browser. You can detect that and delegate to a valid constructor invocation:
> function Person(Nom)
… si (cette === window)
… revenir Nouveau Person(Nom);
…
… cette.Nom = Nom;
… cette.sayHi = function()
… console.log(`Hi, my name is $ cette.Nom.`);
…
…
> let person = Person('John Doe');
> console.log(person);
Person Nom: 'John Doe', sayHi: ƒ
This is the only reason you might want to include a revenir
statement in your constructor.
Remarque: The triple equals sign (===
) is intentional and has to do with the weak typing in JavaScript. You’ll learn more about it below.
Global Scope by Default
Unless you’re already at the global scope, your variables automatically become global when you don’t precede their declarations with one of these keywords:
It’s easy to fall into this trap, especially when you’re coming from Python. For example, such a variable defined in a function will become visible outside of it:
> function call()
… global = 42;
… let local = 3.14
…
> call();
> console.log(global);
42
> console.log(local);
ReferenceError: local est ne pas defined
Interestingly, the rules determining whether you declare a local or a global variable in Python are much more complicated than this. There are also other kinds of variable scope in Python.
Function Scope
This quirk is only present in legacy code, which uses the var
keyword for variable declaration. You’ve learned that when a variable is declared like that, it won’t be global. But it isn’t going to have a local scope either.
No matter how deep in the function a variable is defined, it’ll be scoped to the entire function:
> function call()
… si (vrai)
… pour (let je = 0; je < dix; je++)
… var notGlobalNorLocal = 42 + je;
…
…
… notGlobalNorLocal-;
… console.log(notGlobalNorLocal);
…
> call();
50
The variable is visible and still alive at the top level of the function right before exiting. However, nested functions don’t expose their variables to the outer scope:
> function call()
… function inner()
… var notGlobalNorLocal = 42;
…
… inner();
… console.log(notGlobalNorLocal);
…
> call();
ReferenceError: notGlobalNorLocal est ne pas defined
It works the other way around, though. Inner functions can see the variables from the outer scope, but it gets even more interesting when you return the inner function for later use. This creates a closure.
Hoisting
This one is related to the previous quirk and, again, applies to variables declared with the infamous var
keyword.
Let’s begin with a little riddle:
var X = 42;
function call()
console.log(X); // A = ???
var X = 24;
console.log(X); // B = ???
call();
console.log(X); // C = ???
Consider what the result will be:
- A =
42
, B =24
, C =42
- A =
42
, B =24
, C =24
- A =
24
, B =24
, C =42
- A =
24
, B =24
, C =24
SyntaxError: Identifier 'x' has already been declared
While you’re thinking about your answer, let’s take a closer look at what hoisting is. In short, it’s an implicit mechanism in JavaScript that moves variable declarations to the top of the function, but only those that use the var
keyword. Keep in mind that it moves declarations rather than definitions.
In some programming languages, like C, all variables must be declared at the beginning of a function. Other languages, such as Pascal, go even further by dedicating a special section for variable declarations. JavaScript tried to mimic that.
Alright, ready? The correct answer is none of the above! It’ll print the following:
- A =
undefined
- B =
24
- C =
42
To clarify the reasoning behind these results, you can manually perform the hoisting that JavaScript would do in this situation:
var X = 42;
function call()
var X;
console.log(X); // A = undefined
X = 24;
console.log(X); // B = 24
call();
console.log(X); // C = 42
The global variable is temporarily masked by the local variable because the name lookup goes outward. The declaration of X
inside the function is moved up. When a variable is declared but not initialized, it has the undefined
valeur.
Variables aren’t the only construct that is affected by hoisting. Normally, when you define a function in JavaScript, you can call it even before its definition:
call(); // Prints "hello"
function call()
console.log('hello');
This won’t work in an interactive shell where individual pieces of code are evaluated immediately.
Now, when you declare a variable using the var
keyword and assign a function expression to that variable, it’ll be hoisted:
call(); // TypeError: call is not a function
var call = function()
console.log('hello');
;
As a result, your variable will remain undefined
until you initialize it.
Illusory Function Signatures
Function signatures don’t exist in JavaScript. Whichever formal parameters you declare, they have no impact on function invocation.
Specifically, you can pass any number of arguments to a function that doesn’t expect anything, and they’ll just be ignored:
> function currentYear()
… revenir Nouveau Date().getFullYear();
…
> currentYear(42, 'foobar');
2020
You can also refrain from passing arguments that are seemingly required:
> function truthy(expression)
… revenir !!expression;
…
> truthy();
false
Formal parameters serve as a documentation and allow you to refer to arguments by name. Otherwise, they’re not needed. Within any function, you have access to a special arguments
variable, which represents the actual parameters that were passed:
> function sum()
… revenir [[[[arguments].reduce((une, X) => une + X);
…
> sum(1, 2, 3, 4);
dix
arguments
is an array-like object that is iterable and has numeric indices, but unfortunately it doesn’t come with .forEach()
. To wrap it in an array, you can use the spread operator.
This used to be the only way of defining variadic functions in JavaScript before the rest parameter in ES6.
Implicit Type Coercion
JavaScript is a weakly typed programming language, which is manifested in its ability to cast incompatible types implicitly.
This can give false positives when you compare two values:
si ('2' == 2) { // Evaluates to true
In general, you should prefer the strict comparison operator (===
) to be safe:
> '2' === 2;
false
> '2' !== 2;
vrai
This operator compares both the values and the les types of their operands.
No Integer Type
Python has a few data types to represent numbers:
The previous Python generation also had the long
type, which was eventually merged into int
.
Other programming languages are even more generous, giving you fine-grained control over memory consumption, value range, floating-point precision, and the treatment of sign.
JavaScript has just one numeric type: the Number
, which corresponds to Python’s float
data type. Under the hood, it’s a 64-bit double-precision number that conforms to the IEEE 754 specification. This was simple and sufficient for early web development, but it can cause a few problems today.
Remarque: To get the integer part of a floating-point number in JavaScript, you can use the built-in parseInt()
.
First of all, it’s remarkably wasteful in most situations. If you were to represent pixels of a single FHD video frame with JavaScript’s Number
, then you’d have to allocate about 50 MB of memory. In a programming language with support for a stream of bytes, such as Python, you’d need a fraction of that amount of memory.
Secondly, floating-point numbers suffer from a rounding error due to how they’re represented in computer memory. As such, they’re unsuitable for applications requiring high precision, such as monetary calculations:
> 0,1 + 0,2;
0.30000000000000004
They’re unsafe in representing very big and very small numbers:
> const X = Number.MAX_SAFE_INTEGER + 1;
> const y = Number.MAX_SAFE_INTEGER + 2;
> X === y;
vrai
But that’s not the worst part. After all, computer memory is getting cheaper by the day, and there are ways to circumvent the rounding error.
When Node.js became popular, people started using it to write back-end applications. They needed a way of accessing the local file system. Some operating systems identify files by arbitrary integer numbers. Occasionally, these numbers wouldn’t have an exact representation in JavaScript, so you couldn’t open the file, or you’d read some random file without knowing.
To address the problem of handling big numbers in JavaScript, there’s going to be another primitive type that can reliably represent integer numbers of any size. Some web browsers already support this proposal:
> const X = BigInt(Number.MAX_SAFE_INTEGER) + 1n;
> const y = BigInt(Number.MAX_SAFE_INTEGER) + 2n;
> X === y;
false
Since you can’t mix the new BigInt
data type with regular numbers, you have to either wrap them or use a special literal:
> typeof 42;
'number'
> typeof 42n;
'bigint'
> typeof BigInt(42);
'bigint'
Apart from that, the BigInt
number will be compatible with two somewhat-related typed arrays for signed and unsigned integers:
BigInt64Array
BigUint64Array
While a regular BigInt
can store arbitrarily large numbers, the elements of these two arrays are limited to just 64 bits.
nul
vs undefined
Programming languages provide ways to represent the absence of a value. Python has Aucun
, Java has nul
, and Pascal has néant
, for example. In JavaScript, you get not only nul
mais aussi undefined
.
It may seem odd to have more than one way to represent missing values when one is already too much:
I call it my billion-dollar mistake. It was the invention of the null reference in 1965. (…) This has led to innumerable errors, vulnerabilities, and system crashes, which have probably caused a billion dollars of pain and damage in the last forty years.
- Tony Hoare
La différence entre nul
et undefined
is quite subtle. Variables that are declared but uninitialized will implicitly get the value of undefined
. le nul
value, on the other hand, is never assigned automatically:
let X; // undefined
let y = nul;
At any time, you can manually assign the undefined
value to a variable:
This distinction between nul
et undefined
was often used to implement default function arguments before ES6. One of the possible implementations was this:
function fn(obligatoire, optionnel)
si (typeof optionnel === 'undefined')
optionnel = 'default';
// ...
If—for whatever reason—you wanted to keep an empty value for the optional parameter, then you couldn’t pass undefined
explicitly because it would get overwritten by the default value again.
To differentiate between these two scenarios, you would pass a nul
value instead:
fn(42); // optional = "default"
fn(42, undefined); // optional = "default"
fn(42, nul); // optional = null
Apart from having to deal with nul
et undefined
, you may sometimes experience a ReferenceError
exception:
> foobar;
ReferenceError: foobar est ne pas defined
This indicates that you’re trying to refer to a variable that hasn’t been declared in the current scope, whereas undefined
means declared but uninitialized, and nul
means declared and initialized but with an empty value.
Scope of cette
Methods in Python must declare a special self
parameter unless they’re static or class methods. The parameter holds a reference to a particular instance of the class. Its name can be anything because it’s always passed as the first positional argument.
In JavaScript, like in Java, you can take advantage of a special cette
keyword, which corresponds to the current instance. But what does courant instance mean? It depends on how you invoke your function.
Recall the syntax for object literals:
> let jdoe =
… Nom: 'John Doe',
… whoami: function()
… console.log(cette);
…
… ;
> jdoe.whoami();
Nom: "John Doe", whoami: ƒ
Using cette
in a function lets you refer to a particular object that owns that function without hard-coding a variable name. It doesn’t matter if the function is defined in place as an anonymous expression or if it’s a regular function like this one:
> function whoami()
… console.log(cette);
…
> let jdoe = Nom: 'John Doe', whoami;
> jdoe.whoami();
Nom: "John Doe", whoami: ƒ
What matters is the object that you’re calling the function on:
> jdoe.whoami();
Nom: "John Doe", whoami: ƒ
> whoami();
Window …
In the first line, you call whoami()
through an attribute of the jdoe
object. The value of cette
is the same as the jdoe
variable in that case. However, when you call that same function directly, cette
becomes the global object instead.
Remarque: This rule doesn’t apply to a constructor function invoked with the Nouveau
keyword. In such a case, the function’s cette
reference will point to the newly created object.
You can think of JavaScript functions as methods attached to the global object. In a web browser, window
is the global object, so in reality, the code snippet above is short for this:
> jdoe.whoami();
Nom: "John Doe", whoami: ƒ
> window.whoami();
Window …
Do you see a pattern here?
By default, the value of cette
inside a function depends on the object sitting in front of the dot operator. As long as you control how your function gets called, everything will be fine. It becomes a problem only when you don’t call the function yourself, which is a common case for callbacks.
Let’s define another object literal to demonstrate this:
const collection =
articles: [[[['apple', 'banana', 'orange'],
type: 'fruit',
spectacle: function()
cette.articles.forEach(function(item)
console.log("$ item is a $ cette.type");
);
;
collection
is a collection of elements that have a common type. Currently, .show()
doesn’t work as expected because it doesn’t reveal an element type:
> collection.spectacle();
Pomme est une undefined
banana est une undefined
orange est une undefined
Bien que this.items
correctly refers to the array of fruits, the callback function seems to have received a different cette
référence. That’s because the callback isn’t tied to the object literal. It’s as if it were defined elsewhere:
function callback(item)
console.log("$ item is a $ cette.type");
const collection =
articles: [[[['apple', 'banana', 'orange'],
type: 'fruit',
spectacle: function()
cette.articles.forEach(callback);
;
The most straightforward way to work around this would be to replace cette
in the callback with a custom variable or a constant:
const collection =
articles: [[[['apple', 'banana', 'orange'],
type: 'fruit',
spectacle: function()
const cette = cette;
cette.articles.forEach(function(item)
console.log("$ item is a $ cette.type");
);
;
You persist the value of cette
in a local constant, which the callback then refers to. Since the callback is defined as an inner function, it can access variables and constants from the outer scope. This wouldn’t have been possible with a stand-alone function.
Now the result is correct:
> collection.spectacle();
Pomme est une fruit
banana est une fruit
orange est une fruit
This pattern is so common that .forEach()
accepts an optional parameter for substituting cette
in the callback:
const collection =
articles: [[[['apple', 'banana', 'orange'],
type: 'fruit',
spectacle: function()
cette.articles.forEach(function(item)
console.log("$ item is a $ cette.type");
, cette);
;
collection.spectacle();
This is more elegant than the custom hack that you saw before, and it’s also more universal because it lets you pass a regular function. While not every built-in method in JavaScript is so gracious, there are three more ways to tinker with the cette
reference:
.apply()
.bind()
.call()
These are methods available on function objects. Avec .apply()
et .call()
, you can invoke a function while injecting arbitrary context to it. They both work the same way but pass arguments using different syntaxes:
> function whoami(X, y)
… console.log(cette, X, y);
…
> let jdoe = Nom: 'John Doe';
> whoami.apply(jdoe, [[[[1, 2]);
Nom: "John Doe" 1 2
> whoami.call(jdoe, 1, 2);
Nom: "John Doe" 1 2
Of the three, .bind()
is the most powerful because it allows you to permanently change the value of cette
for future invocations. It works a bit differently as it returns a new function that is bound to the given context:
> const newFunction = whoami.bind(jdoe);
> newFunction(1, 2);
Nom: "John Doe" 1 2
> newFunction(3, 4);
Nom: "John Doe" 3 4
This can be useful in solving the earlier problem of the unbound callback:
const collection =
articles: [[[['apple', 'banana', 'orange'],
type: 'fruit',
spectacle: function()
cette.articles.forEach(callback.bind(cette));
;
Nothing that you’ve just read about cette
in JavaScript applies to arrow functions in ES6. That’s good, by the way, because it removes a lot of ambiguity. There’s no context binding in arrow functions, nor is there an implicit cette
reference available. Au lieu, cette
is treated as an ordinary variable and is subject to lexical scoping rules.
Let’s rewrite one of the previous examples using arrow functions:
> const collection =
… articles: [[[['apple', 'banana', 'orange'],
… type: 'fruit',
… spectacle: () =>
… cette.articles.forEach((item) =>
… console.log("$ item is a $ cette.type");
… );
…
… ;
> collection.spectacle();
TypeError: Cannot lis propriété 'forEach' de undefined
If you trade both functions for their arrow counterparts, then you’ll get an exception because there’s no cette
variable in the scope anymore. You might keep the outer function so that its cette
reference can be picked up by the callback:
const collection =
articles: [[[['apple', 'banana', 'orange'],
type: 'fruit',
spectacle: function()
cette.articles.forEach((item) =>
console.log("$ item is a $ cette.type");
);
;
collection.spectacle();
As you can see, arrow functions aren’t a complete replacement for traditional functions.
This section was merely the tip of the iceberg. To find out more about quirky JavaScript behaviors, take a look at tricky code examples, which are also available as an installable Node.js module. Another great source of wisdom is Douglas Crockford’s book JavaScript: The Good Parts, which has a section devoted to the bad and awful parts as well.
What’s Next?
As a Pythonista, you know that mastering a programming language and its ecosystem is only the beginning of your path to success. There are more abstract concepts to grasp along the way.
Document Object Model (DOM)
If you’re planning to do any sort of client-side development, then you can’t escape getting familiar with the DOM.
Remarque: You might have used the same DOM interface before to handle XML documents in Python.
To allow for manipulating HTML documents in JavaScript, web browsers expose a standard interface called the DOM, which is comprised of various objects and methods. When a page loads, your script can gain access to the internal representation of the document through a predefined document
instance:
const corps = document.corps;
It’s a global variable available to you anywhere in your code.
Every document is a tree of elements. À traverse this hierarchy, you can start at the root and use the following attributes to move in different directions:
- Up:
.parentElement
- Left:
.previousElementSibling
- Droite:
.nextElementSibling
- Down:
.children
,.firstElementChild
,.lastElementChild
These attributes are conveniently available on all elements in the DOM tree, which would be perfect for recursive traversal:
const html = document.firstElementChild;
const corps = html.lastElementChild;
const element = corps.children[[[[2].nextElementSibling;
Most attributes will be nul
if they don’t lead to an element in the tree. The only exception is the .children
property, which always returns an array-like object that can be empty.
Frequently, you won’t know where an element is. le document
object, as well as every other element in the tree, has a few methods for element lookup. You can search elements by tag name, ID attribute, CSS class name, or even using a complex CSS selector.
You can look for one element at a time or multiple elements at once. For example, to match elements against a CSS selector, you’d call one of these two methods:
.querySelector(selector)
.querySelectorAll(selector)
The first one returns the first occurrence of the matching element or nul
, while the second method always returns an array-like object with all the matching elements. Calling these methods on the document
object will cause the entire document to be searched. You can restrict the scope of the search by calling the same methods on a previously found element:
const div = document.querySelector('div'); // The 1st div in the whole document
div.querySelectorAll('p'); // All paragraphs inside that div
Once you have a reference to an HTML element, you can do a bunch of things with it, such as:
- Attach data to it
- Change its style
- Change its content
- Change its placement
- Make it interactive
- Remove it altogether
Vous pouvez également create new elements and add them to the DOM tree:
const parent = document.querySelector('.content');
const child = document.createElement('div');
parent.appendChild(child);
The most challenging part about using DOM is getting skilled at building accurate CSS selectors. You can practice and learn using one of many interactive playgrounds available online.
JavaScript Frameworks
The DOM interface is a set of primitive building blocks for creating interactive user interfaces. It gets the job done, but as your client-side code grows, it becomes increasingly difficult to maintain. The business logic and the presentation layer start to intersect, violating the separation of concerns principle, while code duplication piles up.
Web browsers add fuel to the fire by not having a unified interface across the board. Sometimes, a feature that you seek to use isn’t available on all major browsers or is implemented in different ways. To ensure consistent behavior, you need to include an appropriate boilerplate code that hides the implementation details.
To deal with these problems, people started sharing JavaScript libraries that encapsulated common patterns and made the browser API a little less jarring. The most popular library of all time by far is jQuery, which until recently dwarfed today’s front-end cadres in popularity:
Although this isn’t a completely honest apples-and-oranges comparison, it shows just how popular jQuery used to be. In peak time, it had more hits than all the other major frameworks and libraries combined. Despite being slightly old and unfashionable, it’s still used in a lot of legacy projects and sometimes even in recent ones.
Remarque: A library contains low-level utility functions that you can call, whereas a framework has total control over your code life cycle. A framework also imposes a certain application structure and is heavyweight in comparison with a library.
jQuery has good documentation and a minimal API as there’s only one function to remember, the versatile $()
. You can use that function to create and search for elements, change their style, handle events, and much more.
Modern web browsers are beaucoup better in terms of consistency and support for emerging web standards. So much so, in fact, that some people choose to develop client-side code in vanilla JavaScript without the help of any front-end framework.
So why do most people tend to use a front-end framework, anyway?
There are pros and cons to using frameworks, but the biggest advantage seems to be that they allow you to operate on a higher level of abstraction. Instead of thinking in terms of document elements, you can build self-contained and reusable components, which increases your productivity as a programmer.
That, combined with declarative state management provided by the framework, allows you to tackle the complexity of a highly interactive client-side JavaScript application. If you insist on not using a JavaScript framework for long enough, then you typically end up unwittingly building your own.
As for which popular framework to choose, that depends on your goal. To stay relevant, you should invest time into learning pure JavaScript first since front-end frameworks come and go notoriously fast.
If you’re looking to land a job as a front-end developer or even as a full-stack engineer, then you should take a look at the job descriptions. Chances are they’ll expect someone with experience in one of these frameworks:
At the time of writing this article, these were arguably the most popular JavaScript frameworks.
Conclusion
In this tutorial, you learned about JavaScript’s origins, its alternatives, and where the language is headed. You compared Python vs JavaScript by taking a closer look at their similarities and differences in syntax, runtime environments, associated tools, and jargon. Finally, you learned how to avoid fatal mistakes in JavaScript.
You’re now able to:
- Comparer Python vs JavaScript
- Choose the right language for the job
- Write a shell script in JavaScript
- Generate dynamic content on a web page
- Take advantage of the JavaScript ecosystem
- Avoid common pitfalls in JavaScript
Try using Node.js for your next scripting project or building an interactive client-side application in the web browser. When you combine what you’ve just learned with your existing Python knowledge, the sky’s the limit.
[ad_2]