Cargo-Culting en JavaScript

La programmation culte de la cargaison est ce que fait un programmeur quand il ou elle ne connaît pas assez bien une langue ou un paradigme particulier, et finit par écrire du code redondant et éventuellement nuisible. Il se lève assez souvent au pays de JavaScript. Dans cet article, j'explore le concept de la programmation culte du cargo et les endroits à surveiller en JavaScript..

Les règles dogmatiques font surface et se répandent jusqu'à ce qu'elles soient considérées comme la norme.

La culture de la cargaison est parfois définie comme "une adhésion extrême à la forme au lieu du contenu". La forme, en programmation, étant la syntaxe, les paradigmes, les styles et les modèles que nous employons. Le contenu étant la chose abstraite que vous cherchez à représenter à travers votre code - la substance même de votre programme. Une personne qui manque de compréhension dans un domaine est susceptible de copier la forme des autres sans vraiment comprendre, et donc son contenu - son programme - peut en souffrir.

La culture des marchandises est curieusement courante en JavaScript, probablement à cause de la faible barrière à l'entrée dans le monde du développement front-end. Vous pouvez créer une page HTML avec un peu de JavaScript en quelques secondes. En conséquence, de nombreuses personnes acquièrent une maîtrise suffisante de ces technologies pour se sentir à l'aise de créer et d'imposer des règles à elles-mêmes et à autrui. Éventuellement, d’autres nouveaux arrivants copient ces règles. Les règles dogmatiques font surface et se répandent jusqu'à ce qu'elles soient considérées comme la norme:

  • Toujours utiliser des opérateurs d'égalité strictes
  • Ne jamais utiliser eval
  • Toujours utiliser une seule déclaration var par scope
  • Utilisez toujours un IIFE - il vous "protège"

Une règle continue à se répandre jusqu'à ce qu'un programmeur utilise uniquement une technique donnée en raison de sa popularité, au lieu de considérer chaque cas d'utilisation spécifique indépendamment.


JavaScript Abuzz avec des points-virgules

Si vous avez eu l'occasion d'assister à la plaisanterie spirituelle et à la rhétorique du développeur de logiciels au fil des ans, vous aurez remarqué une tendance à discuter longuement de choses apparemment minuscules. Des choses comme le point-virgule, la virgule, l'espace blanc ou l'accolade.

La syntaxe telle que les points-virgules ou les espaces peut sembler être purement des éléments de forme, pas de contenu. Mais beaucoup de ces règles de syntaxe subtiles peuvent avoir des effets significatifs en JavaScript. Si vous ne comprenez pas le "formulaire", vous ne pouvez pas commencer à comprendre le "contenu".

Ainsi, dans cet article, nous identifierons les zones de formulaire en JavaScript fréquemment utilisées pour la cargaison, c’est-à-dire, copiées sans compréhension..

Comment JavaScript peut sembler… une image de la présentation "La politique de JavaScript" d'Angus Croll

Indéfini

Angus Croll, dans une récente présentation intitulée "The Politics Of JavaScript", a mis en lumière l'un des éléments les plus courants du dogme JS dont les gens sont victimes d'un culte du cargo:

if (typeof myObject.foo === 'undefined') …

La plupart du temps, faire une vérification aussi longue pour indéfini est inutile. La technique est devenue courante parce que les gens copiaient d'autres personnes, pas à cause de sa valeur réelle.

Bien sûr, il y a des moments où:

 typeof x === 'undefined'

… Est préférable à:

x === non défini

Mais, également, il y a des moments où ce dernier est préféré. Un aperçu rapide des options:

 // Détermine si 'x' n'est pas défini: x === typedefiné de x == 'type non défini' de x === 'undefined' x === void 0 // Détermine si 'x' est non défini OU null: x = = null x == non défini

Les gens ont commencé à utiliser le Type de approche parce qu'ils se protégeaient contre:

  • Une variable potentiellement non déclarée (les approches non-typeof lanceraient des erreurs de type)
  • Quelqu'un a écrasé globalement ou non défini dans une portée parente. Certains environnements vous permettent d’écraser indéfini à quelque chose comme vrai. Vous devez vous demander: "Est-il probable que quelqu'un ait écrasé non défini, et si mon script doit plier à une telle bêtise?"

Mais la plupart du temps, ils se protègent des soucis. C'est un fourre-tout d'éviter d'avoir à connaître les détails. Connaître les détails peut vous aider. Chaque caractère de votre code doit exister avec un but en tête.

La seule fois où vous devriez avoir besoin d'utiliser un Type de vérifier indéfini est lorsque vous recherchez une variable qui peut ne pas avoir été déclarée, par ex. vérification de jQuery dans la portée globale:

 if (typeof jQuery! = 'undefined') //… Utilisez jQuery

La chose est, si jQuery Est-ce que existons, alors nous pouvons être sûrs que c’est un objet - une chose "véridique". Donc cela suffirait:

 // ou: if (window.jQuery) 

Le grand débat strict / non strict

Prenons quelque chose de très courant et généralement considéré comme un bon conseil, en utilisant uniquement une égalité stricte:

 a === b

La stricte égalité est dite bonne car elle évite les ambiguïtés. Il vérifie à la fois la valeur et le type, ce qui signifie que nous n'avons pas à nous soucier de la coercition implicite. Avec une égalité non stricte, nous devons cependant nous en préoccuper:

 1 == 1 // true - d'accord, c'est bien 1 == "1" // true - hmm 1 == [1] // true - wat!?

Il semblerait donc judicieux d’éviter totalement l’égalité non stricte, non? En fait non. Il existe de nombreuses situations dans lesquelles une égalité stricte crée beaucoup de redondance, et une égalité non stricte est préférable..

Lorsque vous savez avec 100% de certitude que les types des deux opérandes sont identiques, vous pouvez éviter le recours à l'égalité stricte. Par exemple, je sais toujours que le Type de l'opérateur renvoie une chaîne, et mon opérande de droite est également une chaîne (par exemple,. "nombre"):

 // avec strict-equals typeof x === 'nombre' // avec non-strict-égaux: typeof x == 'nombre'

Ils sont tous deux effectivement identiques. Dans ce cas, je ne suggère pas nécessairement d'abandonner les règles d'égalité stricte. Je suggère que nous restions conscients de ce que nous faisons pour pouvoir faire les meilleurs choix compte tenu de chaque situation..

Un autre exemple très utile est lorsque vous voulez savoir si une valeur est soit nul ou indéfini. Avec une égalité stricte, vous pourriez faire ceci:

 if (valeur === non définie || valeur === null) //…

Avec une égalité non stricte, c'est beaucoup plus simple:

 if (valeur == null) //…

Il n’ya pas de piège ici: il fait exactement ce que nous voulons, mais sans doute moins visiblement. Mais si nous connaissons la langue, alors quel est le problème? C'est juste là dans la spécification:

La comparaison x == y, où X et y sont des valeurs, produit vrai ou faux. Une telle comparaison est effectuée comme suit:

  • Si x est null et y est non défini, retourne true.
  • Si x n'est pas défini et que y est null, retourne true.

Si vous écrivez en JavaScript avec l'intention de le lire, voire pas du tout, par des personnes connaissant JavaScript, alors je vous dirais que vous ne devriez pas vous sentir mal à tirer parti des règles de langage implicites, comme celles-ci.


hasOwnProperty

le hasOwnProperty La méthode est utilisée pour déterminer si une propriété appartient directement à un objet. Est-il communément trouvé dans pour… dans boucles pour s'assurer que vous ne jouez qu'avec les propriétés directes et non les propriétés héritées.

 for (var i dans objet) if (object.hasOwnProperty (i)) // Nous pouvons faire des choses avec 'objet [i]'

Il est important de noter que le pour-dans L'instruction ne parcourra que des propriétés énormes. Les méthodes héritées natives, par exemple, ne sont pas énumérables et vous n'avez donc pas besoin de vous en soucier..

le hasOwnProperty check vous empêche spécifiquement de toucher aux propriétés que vous ou un script tiers avez définies, c'est-à-dire lorsque le prototype de votre objet a des propriétés énumérables.

Si vous savez que le prototype de votre objet (ou le prototype de son prototype etc.) n'a pas de propriétés énumérables, alors tu n'as pas à t'inquiéter à propos de l'utilisation hasOwnProperty dans ton pour-dans boucles. Et, si votre objet est initialisé, via ES5 Object.create (null), alors vous ne pourrez même pas appeler hasOwnProperty directement sur l'objet (aucun prototype ne signifie aucune méthode native héritée). Cela signifie que l'utilisation de hasOwnProperty par défaut dans tous vos pour-dans les boucles peuvent casser parfois.

Une solution potentielle pour les objets avec nul prototypes est d'utiliser une référence enregistrée à hasOwnProperty, ainsi:

 var hasOwnProperty = Object.prototype.hasOwnProperty; // Plus tard dans votre code: for (var i dans someObject) if (hasOwnProperty.call (someObject, i)) //…

Cela fonctionnera même si l'objet n'a pas de prototype (dans le cas de Object.create (null)). Mais, bien sûr, nous ne devrions le faire en premier lieu que si nous savons que nous en avons besoin. Si vous écrivez un script tiers pour un environnement "hostile", vous devez absolument vérifier les propriétés héritées énumérables. Sinon, cela peut ne pas être nécessaire tout le temps.

Remarque: IE9 et Safari 2.0 compliquent encore les choses lorsque vous essayez d'identifier des propriétés énumérables qui sont déjà définies comme non énumérables. Il vaut la peine de vérifier une implémentation de boucle forOwn véritablement inter-navigateur.

Pour conclure: votre utilisation de hasOwnProperty devrait dépendre de l'objet faisant l'objet d'une boucle. Cela dépend des hypothèses que vous pouvez faire en toute sécurité. Protégez-vous aveuglément en utilisant le hasOwnProperty ne suffira pas dans tous les cas. Méfiez-vous aussi des différences entre les navigateurs.


Sur-parenthèse

Une autre redondance commune qui s'insinue dans le code JS est la parenthèse. Dans les expressions, il est utilisé pour forcer un regroupement spécifique de sous-expressions. Sans eux, vous êtes à la merci des précédents et des associativités des opérateurs. Par exemple:

 A && B || C A && (B || C) (A && B) || C

L'un d'eux n'est pas comme l'autre. Les parenthèses forcent un groupe spécifique et beaucoup de gens préfèrent la clarté supplémentaire. Dans ce cas, l'opérateur AND logique a une priorité plus élevée que l'opérateur OR logique, ce qui signifie que ce sont les première et dernière lignes qui sont équivalentes. La deuxième ligne est une opération logique totalement différente.

Une priorité plus élevée signifie qu’elle se produira avant les autres opérations d’une série d’opérations..

Pour éviter cette complexité, les développeurs optent souvent pour une "politique de parenthèses", qui consiste à continuer à ajouter des parenthèses jusqu'à ce qu'il soit clairement défini quelles opérations ont lieu, à la fois pour vous et pour les lecteurs potentiels du code. On peut soutenir que cette verbosité finit par rendre les choses moins claires bien que.

C'est parfois délicat pour un lecteur. Il faut considérer que toutes les parenthèses données peuvent avoir été ajoutées pour les raisons suivantes:

  • Il était nécessaire de remplacer la priorité / associativité par défaut
  • Pour aucune raison fonctionnelle, juste pour "protection" ou "clarté"

Prenons cet exemple:

 UN B ? doFoo (): doBaz ()

Sans connaissance des règles de priorité des opérateurs, nous pouvons voir ici deux opérations possibles:

 (UN B) ? doFoo (): doBaz () A && (B? doFoo (): doBaz ())

Dans ce cas, c'est le AND logique qui a la priorité la plus élevée, ce qui signifie que l'expression entre parenthèses équivalente est:

 (UN B) ? doFoo (): doBaz ()

Nous ne devrions cependant pas nous sentir obligés d'ajouter ces parenthèses dans notre code. Cela arrive implicitement. Une fois que nous reconnaissons que cela se produit implicitement, nous sommes libres de l'ignorer et de nous concentrer sur le programme lui-même..

Il existe bien sûr des arguments valables pour conserver les parenthèses lorsque le regroupement implicite n’est pas clair. Cela dépend vraiment de vous et de ce avec quoi vous êtes à l'aise. Cependant, je vous implorerais d'apprendre les précédents et vous pourrez alors choisir le meilleur itinéraire, en fonction du code spécifique que vous utilisez..


Clés d'objet

Il n'est pas rare de voir des citations redondantes dans les littéraux d'objet:

 var data = 'date': '2011-01-01', 'id': 3243, 'action': 'UPDATE', 'lié': '1253': 2, '3411': 3;

En plus des chaînes, JavaScript vous permet d'utiliser des noms d'identifiant et des nombres valides en tant que clés littérales d'objet. Vous pouvez donc ré-écrire le texte ci-dessus dans:

 var data = date: '2011-01-01', id: 3243, action: 'UPDATE', associé: 1253: 2, 3411: 3;

Parfois, vous préférerez peut-être utiliser des guillemets avec plus de cohérence, en particulier si un nom de champ est un mot réservé en JavaScript (comme «classe» ou «instance de»). Et c'est bien.

Utiliser des guillemets n'est pas une mauvaise chose. Mais c'est redondant. Savoir que vous n'avez pas à les utiliser est la moitié de la bataille gagnée. C'est maintenant votre choix de faire ce que vous voulez.


Position de la virgule

Il y a énormément de préférence subjective en ce qui concerne le placement de ponctuation dans les programmes. Plus récemment, le monde de JavaScript a été rempli de rhétorique et de mécontentement.

Initialiser un objet en JavaScript traditionnellement idiomatique ressemble à ceci:

 var obj = a: 1, b: 2, c: 3;

Il existe une approche alternative qui gagne du terrain:

 var obj = a: 1, b: 2, c: 3;

L'avantage supposé de placer les virgules avant chaque paire clé-valeur (à l'exception de la première) est que cela signifie que vous ne devez toucher qu'une ligne pour supprimer une propriété. En utilisant l'approche traditionnelle, vous auriez besoin de supprimer "c: 3"et ensuite la virgule finale sur la ligne ci-dessus. Mais avec l'approche par la virgule d'abord, vous pouvez simplement supprimer", c: 3". Les promoteurs affirment que cela rend moins probable la virgule de trait et nettoie également les diffs de contrôle de source.

Les opposants, cependant, affirment que cette approche ne permet de résoudre le "problème" des virgules suivies qu'en introduisant un nouveau problème de virgule en tête. Essayez de supprimer la première ligne et vous vous retrouvez avec une virgule sur la ligne suivante. Ceci est en fait considéré comme une bonne chose par les partisans de la première virgule, car une virgule en tête lancerait immédiatement un SyntaxError. Cependant, une virgule de fin ne jette rien, sauf dans IE6 et 7. Ainsi, si le développeur ne teste pas son JS dans ces versions d'IE, les virgules de fin peuvent souvent s'insinuer dans le code de production, ce qui n'est jamais bon. Une virgule en tête apparaît dans tous les environnements, elle risque donc moins d'être manquée.

Bien sûr, vous pourriez soutenir que tout cela est sans objet. Nous devrions probablement utiliser des linters comme JSLint ou le kinder JSHint. Ensuite, nous sommes libres d’utiliser le placement de ponctuation et d’espace qui a le plus de sens pour nous et nos collègues..

Ne commençons même pas à utiliser le style de la première virgule dans les déclarations de variables…

 var a = 1, b = 2, c = 3;

Tu code pour les psychopathes?

Nous devrions nous efforcer d'apprendre les langues que nous utilisons à un niveau suffisant pour éviter la culture de la cargaison et les techniques de codage à tout faire trop protectrices. Et nous devrions faire confiance à nos collègues et autres développeurs pour faire de même..

Nous avons également discuté de l'abandon de cruft au profit des idiosyncracies et des règles implicites d'une langue. Pour certains, cela pose des problèmes de maintenabilité, en particulier si une personne plus âgée dans l’acquisition d’une langue donnée s’approche du code. Par exemple, s’ils ne connaissent pas l’égalité faible / stricte de JavaScript?

Au sujet de la maintenabilité, cette célèbre citation nous rappelle:

Codez toujours comme si la personne qui finissait par maintenir votre code était un psychopathe violent qui sait où vous habitez.

Je ne sais pas si c'est vraiment un bon conseil. Même prise métaphoriquement, cela suggère une méfiance vis-à-vis de la compétence du mainteneur de fiction - et la nécessité de s’inquiéter de sa compréhension avant tout. Je préférerais écrire du code sachant qu'il sera pris en charge par des personnes connaissant leur métier. Donc, à titre de contradiction possible ou même d’additif à cette citation, j’offre:

Codez toujours comme si la personne qui finissait par gérer votre code connaissait bien le langage et ses constructions et cherchait à comprendre le domaine problématique en lisant votre code.

Bien que cela ne soit pas toujours vrai, nous devrions le rechercher. Nous devrions nous efforcer de faire en sorte que les personnes travaillant sur une technologie spécifique possèdent les connaissances suffisantes pour le faire. Le savant cargo-culter dit:

Si je m'adonne toujours à un niveau de compréhension inférieur dans mon code - en marchant doucement - en respectant strictement les conventions, les guides de style et les choses que voient les "experts", je ne pourrai jamais améliorer ma propre compréhension, ni profiter d'un la langue dans toute son étrangeté et sa beauté. Je suis heureusement et heureusement installé dans ce monde de règles et d'absolus, mais pour avancer, je dois sortir de ce monde et embrasser une compréhension supérieure..