portaldacalheta.pt
  • Principal
  • Gestion De L'ingénierie
  • Conseils Et Outils
  • Rise Of Remote
  • Agile
Back-End

En tant que développeur JS, c'est ce qui me tient éveillé la nuit



JavaScript est une drôle de langue. Bien qu'inspiré de Smalltalk, il utilise une syntaxe de type C. Il combine des aspects des paradigmes de programmation procédurale, fonctionnelle et orientée objet (POO). Il a nombreux , souvent redondantes, pour résoudre presque tous les problèmes de programmation imaginables et n’ont pas d’opinion prononcée sur ceux qui sont préférés. Il est typé faiblement et dynamiquement, avec une approche mazelike de la coercition de type qui trébuche même les développeurs expérimentés.

JavaScript a également ses verrues, ses pièges et ses fonctionnalités douteuses. Les nouveaux programmeurs se débattent avec certains de ses concepts les plus difficiles - pensez à l'asynchronisme, aux fermetures et au levage. Les programmeurs ayant de l'expérience dans d'autres langages supposent raisonnablement que les choses avec des noms et des apparences similaires fonctionneront de la même manière en JavaScript et sont souvent fausses. Les tableaux ne sont pas vraiment des tableaux; quel est le problème this, qu'est-ce qu'un prototype et que fait new faire réellement?



Le problème avec les classes ES6

Le pire contrevenant est de loin le nouveau dans la dernière version de JavaScript, ECMAScript 6 (ES6): Des classes . Certaines des discussions autour des cours sont franchement alarmantes et révèlent un malentendu profondément enraciné sur le fonctionnement réel de la langue:



«JavaScript est enfin un réel langage orienté objet maintenant qu'il a des classes! »



Ou:

'Les cours nous libèrent de la réflexion sur le modèle d'héritage cassé de JavaScript.'



Ou même:

'Les cours constituent une approche plus sûre et plus simple pour créer des types en JavaScript.'



Ces déclarations ne me dérangent pas, car elles impliquent qu'il y a quelque chose qui ne va pas avec héritage prototypique ; mettons de côté ces arguments. Ces affirmations me dérangent car aucune d’elles n’est vraie et elles démontrent les conséquences de l’approche JavaScript «tout pour tous» en matière de conception de langage: elle paralyse la compréhension du langage par un programmeur plus souvent qu’elle ne le permet. Avant d’aller plus loin, illustrons-le.

JavaScript Pop Quiz # 1: Quelle est la différence essentielle entre ces blocs de code?

function PrototypicalGreeting(greeting = 'Hello', name = 'World') { this.greeting = greeting this.name = name } PrototypicalGreeting.prototype.greet = function() { return `${this.greeting}, ${this.name}!` } const greetProto = new PrototypicalGreeting('Hey', 'folks') console.log(greetProto.greet()) class ClassicalGreeting { constructor(greeting = 'Hello', name = 'World') { this.greeting = greeting this.name = name } greet() { return `${this.greeting}, ${this.name}!` } } const classyGreeting = new ClassicalGreeting('Hey', 'folks') console.log(classyGreeting.greet())

La réponse ici est il n'y en a pas . Celles-ci font effectivement la même chose, il s’agit uniquement de savoir si la syntaxe de classe ES6 a été utilisée.



société c vs société s vs llc

Certes, le deuxième exemple est plus expressif. Pour cette seule raison, vous pourriez soutenir que class est un bel ajout à la langue. Malheureusement, le problème est un peu plus subtil.

JavaScript Pop Quiz # 2: Que fait le code suivant?

function Proto() { this.name = 'Proto' return this; } Proto.prototype.getName = function() { return this.name } class MyClass extends Proto { constructor() { super() this.name = 'MyClass' } } const instance = new MyClass() console.log(instance.getName()) Proto.prototype.getName = function() { return 'Overridden in Proto' } console.log(instance.getName()) MyClass.prototype.getName = function() { return 'Overridden in MyClass' } console.log(instance.getName()) instance.getName = function() { return 'Overridden in instance' } console.log(instance.getName())

La bonne réponse est qu'il imprime sur la console:



> MyClass > Overridden in Proto > Overridden in MyClass > Overridden in instance

Si vous avez répondu incorrectement, vous ne comprenez pas ce que class est en fait. Ce n’est pas votre faute. Tout comme Array, class n'est pas une fonctionnalité linguistique, c'est obscurantisme syntaxique . Il essaie de cacher le modèle d'héritage prototypique et les idiomes maladroits qui l'accompagnent, et cela implique que JavaScript fait quelque chose qui n'est pas.

On vous a peut-être dit que class a été introduit à JavaScript pour rendre les développeurs de POO classiques issus de langages comme Java plus à l'aise avec le modèle d'héritage de classe ES6. Si vous sont l'un de ces développeurs, cet exemple vous a probablement horrifié. Cela devrait. Cela montre que JavaScript est class Le mot-clé n'est fourni avec aucune des garanties qu'une classe est censée fournir. Il montre également l'une des principales différences dans le modèle d'héritage prototype: les prototypes sont instances d'objet , ne pas les types .



Prototypes vs classes

La différence la plus importante entre l'héritage basé sur une classe et sur un prototype est qu'une classe définit un type qui peut être instancié au moment de l'exécution, alors qu'un prototype est lui-même une instance d'objet.

Un enfant d'une classe ES6 en est un autre type définition qui étend le parent avec de nouvelles propriétés et méthodes, qui à leur tour peuvent être instanciées au moment de l'exécution. Un enfant d'un prototype est un autre objet exemple qui délègue au parent toutes les propriétés qui ne sont pas implémentées sur l'enfant.

Note latérale: Vous vous demandez peut-être pourquoi j'ai mentionné les méthodes de classe, mais pas les méthodes prototypes. C'est parce que JavaScript n'a pas de concept de méthodes. Les fonctions sont première classe en JavaScript, et ils peuvent avoir des propriétés ou être des propriétés d'autres objets.

Un constructeur de classe crée une instance de la classe. Un constructeur en JavaScript n'est qu'une simple fonction ancienne qui renvoie un objet. La seule particularité d'un constructeur JavaScript est que, lorsqu'il est appelé avec le new mot-clé, il affecte son prototype comme prototype de l'objet retourné. Si cela vous semble un peu déroutant, vous n’êtes pas seul - c’est le cas, et c’est en grande partie pourquoi les prototypes sont mal compris.

Pour mettre un point très fin là-dessus, un enfant d'un prototype n'est pas un copie de son prototype, ni un objet avec le même forme comme son prototype. L'enfant a une référence vivante à le prototype, et toute propriété de prototype qui n’existe pas sur l’enfant est une référence unidirectionnelle à une propriété du même nom sur le prototype.

Considérer ce qui suit:

let parent = { foo: 'foo' } let child = { } Object.setPrototypeOf(child, parent) console.log(child.foo) // 'foo' child.foo = 'bar' console.log(child.foo) // 'bar' console.log(parent.foo) // 'foo' delete child.foo console.log(child.foo) // 'foo' parent.foo = 'baz' console.log(child.foo) // 'baz' Remarque: vous n'écririez presque jamais de code comme celui-ci dans la vraie vie - c'est une pratique terrible - mais cela démontre succinctement le principe.

Dans l'exemple précédent, tandis que child.foo était undefined, il faisait référence à parent.foo. Dès que nous avons défini foo sur child, child.foo avait la valeur 'bar', mais parent.foo a conservé sa valeur d'origine. Une fois que nous delete child.foo il fait à nouveau référence à parent.foo, ce qui signifie que lorsque nous modifions la valeur du parent, child.foo fait référence à la nouvelle valeur.

Regardons ce qui vient de se passer (à des fins d'illustration plus claire, nous allons faire comme s'il s'agissait de Strings et non de chaînes littérales, la différence n'a pas d'importance ici):

Parcourir la chaîne de prototypes pour montrer comment les références manquantes sont traitées en JavaScript.

La façon dont cela fonctionne sous le capot, et surtout les particularités de new et this, sont un sujet pour un autre jour, mais Mozilla a un article détaillé sur la chaîne d'héritage prototype de JavaScript si vous souhaitez en savoir plus.

La clé à retenir est que les prototypes ne définissent pas un type; ils sont eux-mêmes instances et ils sont mutables au moment de l’exécution, avec tout ce que cela implique et implique.

Encore avec moi? Revenons à la dissection des classes JavaScript.

JavaScript Pop Quiz # 3: Comment implémentez-vous la confidentialité dans les classes?

Nos propriétés de prototype et de classe ci-dessus ne sont pas tant «encapsulées» que «suspendues de manière précaire par la fenêtre». Nous devrions résoudre ce problème, mais comment?

Aucun exemple de code ici. La réponse est que vous ne pouvez pas.

JavaScript n'a aucun concept de confidentialité, mais il comporte des fermetures:

function SecretiveProto() { const secret = 'The Class is a lie!' this.spillTheBeans = function() { console.log(secret) } } const blabbermouth = new SecretiveProto() try { console.log(blabbermouth.secret) } catch(e) { // TypeError: SecretiveClass.secret is not defined } blabbermouth.spillTheBeans() // 'The Class is a lie!'

Comprenez-vous ce qui vient de se passer? Sinon, vous ne comprenez pas les fermetures. C'est pas grave, vraiment - ils ne sont pas aussi intimidants qu'on le prétend, ils sont super utiles et vous devriez prenez le temps d'en apprendre davantage sur eux .

JavaScript Pop Quiz # 4: Quel est l'équivalent de ce qui précède en utilisant class Mot-clé?

Désolé, c'est une autre question piège. Vous pouvez faire essentiellement la même chose, mais cela ressemble à ceci:

class SecretiveClass { constructor() { const secret = 'I am a lie!' this.spillTheBeans = function() { console.log(secret) } } looseLips() { console.log(secret) } } const liar = new SecretiveClass() try { console.log(liar.secret) } catch(e) { console.log(e) // TypeError: SecretiveClass.secret is not defined } liar.spillTheBeans() // 'I am a lie!'

Faites-moi savoir si cela semble plus facile ou plus clair que dans SecretiveProto. À mon avis, c’est un peu pire - cela rompt l’utilisation idiomatique de class des déclarations en JavaScript et cela ne fonctionne pas beaucoup comme vous vous attendez à partir de, disons, Java. Cela sera précisé par ce qui suit:

JavaScript Pop Quiz # 5: Que fait SecretiveClass::looseLips() Faire?

Découvrons-le:

try { liar.looseLips() } catch(e) { // ReferenceError: secret is not defined }

Eh bien… c'était gênant.

meilleures pratiques de conception de sites Web mobiles

JavaScript Pop Quiz # 6: Qu'est-ce que les développeurs JavaScript expérimentés préfèrent: les prototypes ou les classes?

Vous l'avez deviné, c'est une autre question piège: les développeurs JavaScript expérimentés ont tendance à éviter les deux lorsqu'ils le peuvent. Voici une bonne façon de procéder avec JavaScript idiomatique:

function secretFactory() { const secret = 'Favor composition over inheritance, `new` is considered harmful, and the end is near!' const spillTheBeans = () => console.log(secret) return { spillTheBeans } } const leaker = secretFactory() leaker.spillTheBeans()

Il ne s’agit pas seulement d’éviter la laideur inhérente à l’héritage ou d’imposer l’encapsulation. Pensez à ce que vous pourriez faire d'autre avec secretFactory et leaker ce que vous ne pourriez pas faire facilement avec un prototype ou une classe.

D'une part, vous pouvez le déstructurer car vous n'avez pas à vous soucier du contexte de this:

const { spillTheBeans } = secretFactory() spillTheBeans() // Favor composition over inheritance, (...)

C’est plutôt sympa. En plus d'éviter new et this tomfoolery, cela nous permet d'utiliser nos objets de manière interchangeable avec les modules CommonJS et ES6. Cela facilite également la composition:

function spyFactory(infiltrationTarget) { return { exfiltrate: infiltrationTarget.spillTheBeans } } const blackHat = spyFactory(leaker) blackHat.exfiltrate() // Favor composition over inheritance, (...) console.log(blackHat.infiltrationTarget) // undefined (looks like we got away with it)

Clients de blackHat n’a pas à vous soucier de l’endroit où exfiltrate est venu de, et spyFactory n'a pas à jouer avec Function::bind jonglage de contexte ou propriétés profondément imbriquées. Attention, nous n'avons pas à nous soucier beaucoup de this dans du code procédural synchrone simple, mais cela cause toutes sortes de problèmes dans le code asynchrone qu'il vaut mieux éviter.

Avec un peu de réflexion, spyFactory pourrait être développé en un outil d'espionnage hautement sophistiqué qui pourrait gérer toutes sortes de cibles d'infiltration - ou en d'autres termes, un façade .

Bien sûr, vous pouvez également le faire avec une classe, ou plutôt un assortiment de classes, qui héritent toutes d'un abstract class ou interface… sauf que JavaScript n'a pas de concept d'abrégé ou d'interface.

Revenons à l'exemple de l'accueil pour voir comment nous le mettre en œuvre avec une usine:

function greeterFactory(greeting = 'Hello', name = 'World') { return { greet: () => `${greeting}, ${name}!` } } console.log(greeterFactory('Hey', 'folks').greet()) // Hey, folks!

Vous avez peut-être remarqué que ces usines deviennent de plus en plus laconiques au fur et à mesure, mais ne vous inquiétez pas, elles font la même chose. Les roues d'entraînement se détachent, les gars!

C'est déjà moins standard que le prototype ou la version de classe du même code. Deuxièmement, il réalise l'encapsulation de ses propriétés plus efficacement. En outre, il a une mémoire et une empreinte de performances plus faibles dans certains cas (cela peut ne pas sembler être le cas à première vue, mais le compilateur JIT travaille silencieusement en coulisse pour réduire la duplication et déduire les types).

C'est donc plus sûr, c'est souvent plus rapide et c'est plus facile d'écrire du code comme celui-ci. Pourquoi avons-nous à nouveau besoin de cours? Oh, bien sûr, la réutilisabilité. Que se passe-t-il si nous voulons des variantes d'accueil malheureuses et enthousiastes? Eh bien, si nous utilisons le ClassicalGreeting classe, nous sautons probablement directement dans l’imagination d’une hiérarchie de classes. Nous savons que nous devrons paramétrer la ponctuation, nous allons donc procéder à une petite refactorisation et ajouter des enfants:

// Greeting class class ClassicalGreeting { constructor(greeting = 'Hello', name = 'World', punctuation = '!') { this.greeting = greeting this.name = name this.punctuation = punctuation } greet() { return `${this.greeting}, ${this.name}${this.punctuation}` } } // An unhappy greeting class UnhappyGreeting extends ClassicalGreeting { constructor(greeting, name) { super(greeting, name, ' :(') } } const classyUnhappyGreeting = new UnhappyGreeting('Hello', 'everyone') console.log(classyUnhappyGreeting.greet()) // Hello, everyone :( // An enthusiastic greeting class EnthusiasticGreeting extends ClassicalGreeting { constructor(greeting, name) { super(greeting, name, '!!') } greet() { return super.greet().toUpperCase() } } const greetingWithEnthusiasm = new EnthusiasticGreeting() console.log(greetingWithEnthusiasm.greet()) // HELLO, WORLD!!

C'est une bonne approche, jusqu'à ce que quelqu'un vienne et demande une fonctionnalité qui ne rentre pas proprement dans la hiérarchie et que tout cesse d'avoir un sens. Mettez une épingle dans cette pensée pendant que nous essayons d'écrire la même fonctionnalité avec des usines:

const greeterFactory = (greeting = 'Hello', name = 'World', punctuation = '!') => ({ greet: () => `${greeting}, ${name}${punctuation}` }) // Makes a greeter unhappy const unhappy = (greeter) => (greeting, name) => greeter(greeting, name, ':(') console.log(unhappy(greeterFactory)('Hello', 'everyone').greet()) // Hello, everyone :( // Makes a greeter enthusiastic const enthusiastic = (greeter) => (greeting, name) => ({ greet: () => greeter(greeting, name, '!!').greet().toUpperCase() }) console.log(enthusiastic(greeterFactory)().greet()) // HELLO, WORLD!!

Il n’est pas évident que ce code soit meilleur, même s’il est un peu plus court. En fait, vous pourriez dire qu’il est plus difficile à lire, et qu’il s’agit peut-être d’une approche obtuse. Ne pourrions-nous pas simplement avoir un unhappyGreeterFactory et un enthusiasticGreeterFactory?

Ensuite, votre client arrive et dit: «J'ai besoin d'un nouveau greeter qui est mécontent et veut que toute la salle le sache!»

console.log(enthusiastic(unhappy(greeterFactory))().greet()) // HELLO, WORLD :(

Si nous devions utiliser plus d'une fois cet accueil enthousiaste et malheureux, nous pourrions nous faciliter la tâche:

const aggressiveGreeterFactory = enthusiastic(unhappy(greeterFactory)) console.log(aggressiveGreeterFactory('You're late', 'Jim').greet())

Il existe des approches de ce style de composition qui fonctionnent avec des prototypes ou des classes. Par exemple, vous pouvez repenser UnhappyGreeting et EnthusiasticGreeting comme décorateurs . Il faudrait encore plus de passe-partout que l'approche de style fonctionnel utilisée ci-dessus, mais c'est le prix que vous payez pour la sécurité et l'encapsulation de réel Des classes.

Le problème, c'est qu'en JavaScript, vous n'obtenez pas cette sécurité automatique. Des frameworks JavaScript qui mettent l'accent sur class l'usage fait beaucoup de «magie» pour dissimuler ce genre de problèmes et oblige les classes à se comporter. Jetez un œil à Polymer’s ElementMixin code source quelque temps, je te défie. Ce sont des niveaux arcanes JavaScript archi-magiciens, et je veux dire cela sans ironie ni sarcasme.

Bien sûr, nous pouvons résoudre certains des problèmes évoqués ci-dessus avec Object.freeze ou Object.defineProperties à un effet plus ou moins grand. Mais pourquoi imiter le formulaire sans la fonction, tout en ignorant les outils JavaScript Est-ce que nous fournir nativement que nous pourrions ne pas trouver dans des langages comme Java? Utiliseriez-vous un marteau étiqueté «tournevis» pour enfoncer une vis, alors que votre boîte à outils avait comme véritable tournevis assis juste à côté?

combien vaut l'industrie de la musique

Trouver les bonnes pièces

Les développeurs JavaScript mettent souvent l'accent sur les bonnes parties du langage, à la fois familièrement et en référence à le livre du même nom . Nous essayons d'éviter les pièges posés par ses choix de conception de langage plus discutables et de nous en tenir aux parties qui nous permettent d'écrire du code propre, lisible, minimisant les erreurs et réutilisable.

Il y a des arguments raisonnables sur les parties de JavaScript admissibles, mais j'espère vous avoir convaincu que class n'en fait pas partie. À défaut, j'espère que vous comprenez que l'héritage en JavaScript peut être un désordre déroutant et que class ne le corrige ni ne vous évite d'avoir à comprendre les prototypes. Un crédit supplémentaire si vous avez compris les indices selon lesquels les modèles de conception orientés objet fonctionnent correctement sans classes ni héritage ES6.

Je ne vous dis pas d’éviter class entièrement. Parfois, vous avez besoin d'héritage et class fournit une syntaxe plus claire pour ce faire. En particulier, class X extends Y est beaucoup plus agréable que l'ancienne approche de prototype. En plus de cela, de nombreux frameworks frontaux populaires encouragent son utilisation et vous devriez probablement éviter d'écrire du code non standard étrange par principe. Je n'aime pas où ça va.

Dans mes cauchemars, toute une génération de bibliothèques JavaScript est écrite en utilisant class, dans l'espoir qu'elle se comporte de la même manière que d'autres langages populaires. De nouvelles classes entières de bugs (jeu de mots) sont découvertes. Les anciens sont ressuscités qui auraient facilement pu être laissés dans le cimetière de JavaScript malformé si nous n’avions pas été négligemment tombés dans le class piège. Les développeurs JavaScript expérimentés sont en proie à ces monstres, car ce qui est populaire n'est pas toujours ce qui est bon.

Finalement, nous abandonnons tous dans la frustration et commençons à réinventer les roues dans Rust, Go, Haskell ou qui sait quoi d'autre, puis à compiler vers Wasm pour le Web, et de nouveaux frameworks et bibliothèques Web prolifèrent dans l'infini multilingue.

Cela me tient vraiment éveillé la nuit.

Comprendre les bases

Qu'est-ce qu'ECMAScript 6?

ES6 est la dernière implémentation stable d'ECMAScript, le standard ouvert sur lequel JavaScript est basé. Il ajoute un certain nombre de nouvelles fonctionnalités au langage, notamment un système de module officiel, des variables et des constantes à portée de bloc, des fonctions fléchées et de nombreux autres nouveaux mots-clés, syntaxes et objets intégrés.

ES6 est-il la dernière version d'ECMAScript?

ES6 (ES2015) est la norme la plus récente qui est stable et entièrement mise en œuvre (à l'exception des appels de queue appropriés et de certaines nuances) dans les dernières versions des principaux navigateurs et autres environnements JS. ES7 (ES2016) et ES8 (ES2017) sont également des spécifications stables, mais la mise en œuvre est assez mitigée.

ES6 est-il orienté objet?

JavaScript a un support solide pour la programmation orientée objet, mais il utilise un modèle d'héritage différent (prototypique) par rapport aux langages OO les plus populaires (qui utilisent l'héritage classique). Il prend également en charge les styles de programmation procéduraux et fonctionnels.

Que sont les classes ES6?

Dans ES6, le mot-clé «classe» et les fonctionnalités associées constituent une nouvelle approche pour créer des prototypes de constructeurs. Ce ne sont pas de vraies classes d'une manière qui serait familière aux utilisateurs de la plupart des autres langages orientés objet.

Quels mots-clés peuvent être utilisés pour implémenter l'héritage dans ES6?

On peut implémenter l'héritage dans JavaScript ES6 via les mots-clés «classe» et «étend». Une autre approche consiste à utiliser l'idiome de fonction «constructeur» plus l'affectation de fonctions et de propriétés statiques au prototype du constructeur.

Qu'est-ce que l'héritage de prototype en JavaScript?

Dans l'héritage prototypique, les prototypes sont des instances d'objet auxquelles les instances enfants délèguent des propriétés non définies. En revanche, les classes dans l'héritage classique sont des définitions de type, à partir desquelles les classes enfants héritent des méthodes et des propriétés lors de l'instanciation.

Architecture d'algorithmes d'optimisation avec HorusLP

La Technologie

Architecture d'algorithmes d'optimisation avec HorusLP
eCommerce UX - Un aperçu des meilleures pratiques (avec infographie)

eCommerce UX - Un aperçu des meilleures pratiques (avec infographie)

Conception Ux

Articles Populaires
API de réseaux sociaux: le portail Internet vers le monde réel
API de réseaux sociaux: le portail Internet vers le monde réel
Explorer les raisons de la critique du Design Thinking
Explorer les raisons de la critique du Design Thinking
La concurrence et la tolérance aux pannes simplifiées: un didacticiel Akka avec des exemples
La concurrence et la tolérance aux pannes simplifiées: un didacticiel Akka avec des exemples
Dix fonctionnalités Kotlin pour booster le développement Android
Dix fonctionnalités Kotlin pour booster le développement Android
Soutenir l'offre technologique grâce à l'éducation STEM
Soutenir l'offre technologique grâce à l'éducation STEM
 
État de l'industrie Fintech (avec infographie)
État de l'industrie Fintech (avec infographie)
DevOps: qu'est-ce que c'est et pourquoi c'est important
DevOps: qu'est-ce que c'est et pourquoi c'est important
Scala vs Java: pourquoi devrais-je apprendre Scala?
Scala vs Java: pourquoi devrais-je apprendre Scala?
Qu'est-ce qu'un round down et comment en éviter un
Qu'est-ce qu'un round down et comment en éviter un
Création d'illustrations époustouflantes avec Sketch et Looper en un rien de temps
Création d'illustrations époustouflantes avec Sketch et Looper en un rien de temps
Articles Populaires
  • analyse de régression dans les évaluations immobilières
  • comment faire un plan comptable
  • quels sont les éléments de conception
  • node js côté client javascript
  • vous mettez en place un système pour un graphiste
  • API Web de base .net
  • comment programmer un robot en c++
Catégories
  • Gestion De L'ingénierie
  • Conseils Et Outils
  • Rise Of Remote
  • Agile
  • © 2022 | Tous Les Droits Sont Réservés

    portaldacalheta.pt