Mior Agency

Quand les utiliser (Partie 2) — Smashing Magazine

L’écriture de programmes en JavaScript est accessible dans un premier temps. La langue est indulgente et on s’habitue à ses possibilités. Avec le temps et l’expérience de travail sur des projets complexes, vous commencez à apprécier des choses comme le contrôle et la précision dans le flux de développement.

Une autre chose que vous pourriez commencer à apprécier est la prévisibilité, mais c’est beaucoup moins une garantie en JavaScript. Alors que les valeurs primitives sont suffisamment prédictives, les objets ne le sont pas. Lorsque vous obtenez un objet en entrée, vous devez tout vérifier :

  • C’est un objet ?
  • Avez-vous cette propriété que vous recherchez?
  • Lorsqu’une propriété a undefinedest-ce sa valeur, ou la propriété elle-même manque-t-elle?

Il est compréhensible que ce niveau d’incertitude vous laisse un peu paranoïaque dans le sens où vous commencez à remettre en question toutes vos options. Par la suite, votre code devient défensif. Pensez davantage à savoir si vous avez traité tous les mauvais cas ou non (très probablement non). Et au final, votre programme est surtout une collection de chèques au lieu d’apporter une réelle valeur au projet.

lors de la fabrication d’objets primitif, de nombreux points de défaillance potentiels sont déplacés vers un emplacement unique, où les objets sont initialisés. Si vous pouvez vous assurer que vos objets sont initialisés avec un certain ensemble de propriétés et que ces propriétés ont certaines valeurs, vous n’avez pas à vérifier des choses comme les propriétés ailleurs dans votre programme. pourriez-vous garantir que undefined est une valeur si nécessaire.

Examinons l’une des manières dont nous pouvons rendre les objets primitifs. Ce n’est pas le seul moyen ni le plus intéressant. Son but est plutôt de démontrer que travailler avec des objets en lecture seule n’a pas à être fastidieux ou difficile.

Note: Je vous recommande également de consulter la première partie de la série, où j’ai couvert certains aspects de JavaScript qui aident à rapprocher les objets des valeurs primitives, ce qui nous permet à son tour de tirer parti des fonctionnalités de langage communes qui ne sont généralement pas associées à un objet. . comme les comparaisons et les opérateurs arithmétiques.

Faire des articles primitifs en vrac

Le plus simple, le plus primitif La manière (jeu de mots) de créer un objet primitif est la suivante :

const my_object = Object.freeze({});

Cette ligne unique donne un objet qui peut représenter n’importe quoi. Par exemple, vous pouvez implémenter une interface à onglets en utilisant un objet vide pour chaque onglet.

import React, { useState } from "react";

const summary_tab = Object.freeze({});
const details_tab = Object.freeze({});

function TabbedContainer({ summary_children, details_children }) {
    const [ active, setActive ] = useState(summary_tab);
  
    return (
        <div className="tabbed-container">
            <div className="tabs">
                <label
                    className={active === summary_tab ? "active" : ""}
                    onClick={() => {
                        setActive(summary_tab);
                    }}
                >
                    Summary
                </label>
                <label
                    className={active === details_tab ? "active": ""}
                    onClick={() => {
                        setActive(details_tab);
                    }}
                >
                    Details
                </label>
            </div>
            <div className="tabbed-content">
                {active === summary_tab && summary_children}
                {active === details_tab && details_children}
            </div>
        </div>
    );
}

export default TabbedContainer;

Si vous êtes comme moi, ça tabs l’élément ne demande qu’à être retravaillé. En y regardant de plus près, vous remarquerez que les éléments de l’onglet sont similaires et nécessitent deux choses, comme un référence d’objet et un chaîne de balise. Incluons le label propriété dans le tabs objets et déplacer les objets eux-mêmes vers un tableau. Et puisque nous ne prévoyons pas de changer tabs de toute façon, rendons également ce tableau en lecture seule pendant que nous y sommes.

const tab_kinds = Object.freeze([
    Object.freeze({ label: "Summary" }),
    Object.freeze({ label: "Details" })
]);

Cela fait ce dont nous avons besoin, mais c’est verbeux. L’approche que nous allons examiner maintenant est souvent utilisée pour masquer les opérations répétitives afin de réduire le code aux seules données. De cette façon, il est plus évident que les données sont erronées. Ce que nous voulons aussi, c’est freeze objets (y compris le tableau) par défaut au lieu d’être quelque chose que nous devons nous rappeler d’écrire. Pour la même raison, le fait que nous devions spécifier un nom de propriété à chaque fois laisse place à des erreurs, telles que des fautes de frappe.

Pour initialiser facilement et de manière cohérente des tableaux d’objets primitifs, j’utilise un populate fonction. En fait, je n’ai pas une seule fonction qui fait le travail. J’en crée généralement un à la fois en fonction de ce dont j’ai besoin sur le moment. Dans le cas particulier de cet article, c’est l’un des plus simples. Voici comment nous procéderons :

function populate(...names) {
    return function(...elements) {
        return Object.freeze(
            elements.map(function (values) {
                return Object.freeze(names.reduce(
                    function (result, name, index) {
                        result[name] = values[index];
                        return result;
                    },
                    Object.create(null)
                ));
            })
        );
    };
}

Si celui-ci semble dense, en voici un qui est plus lisible :

function populate(...names) {
    return function(...elements) {
        const objects = [];

        elements.forEach(function (values) {
            const object = Object.create(null);

            names.forEach(function (name, index) {
                object[name] = values[index];
            });

            objects.push(Object.freeze(object));
        });

        return Object.freeze(objects);
    };
}

Avec ce type de fonction en main, nous pouvons créer le même tableau d’objets à onglets comme suit :

const tab_kinds = populate(
    "label"
)(
    [ "Summary" ],
    [ "Details" ]
);

Chaque tableau du deuxième appel représente les valeurs des objets résultants. Disons maintenant que nous voulons ajouter plus de propriétés. Nous aurions besoin d’ajouter un nouveau nom au premier appel et une valeur à chaque tableau lors du deuxième appel.

const tab_kinds = populate(
    "label",
    "color",
    "icon"
)(                                          
    [ "Summary", colors.midnight_pink, "💡" ],
    [ "Details", colors.navi_white, "🔬" ]
);

Étant donné un espace blanc, vous pourriez le faire ressembler à une table. De cette façon, il est beaucoup plus facile d’attraper une erreur dans les grandes définitions.

Vous avez peut-être remarqué que populate renvoie une autre fonction. Il y a plusieurs raisons de s’en tenir à deux appels de fonction. Tout d’abord, j’aime la façon dont deux appels contigus créent une ligne vide qui sépare les clés et les valeurs. Deuxièmement, j’aime pouvoir créer ces types de générateurs pour des objets similaires. Par exemple, disons que nous devons créer ces objets d’étiquette pour différents composants et que nous voulons les stocker dans différents tableaux.

Revenons à l’exemple et voyons ce que nous en tirons. populate fonction:

import React, { useState } from "react";
import populate_label from "./populate_label";

const tabs = populate_label(
    [ "Summary" ],
    [ "Details" ]
);

const [ summary_tab, details_tab ] = tabs;

function TabbedContainer({ summary_children, details_children }) {
    const [ active, setActive ] = useState(summary_tab);
      
    return (
        <div className="tabbed-container">
            <div className="tabs">
                {tabs.map((tab) => (
                    <label
                        key={tab.label}
                        className={tab === active ? "active" : ""}
                        onClick={() => {
                            setActive(tab);
                        }}
                    >
                        {tab.label}
                    </label>
                )}
            </div>
            <div className="tabbed-content">
                {summary_tab === active && summary_children}
                {details_tab === active && details_children}
            </div>
        </div>
    );
}

export default TabbedContainer;

L’utilisation d’objets primitifs facilite l’écriture de la logique de l’interface utilisateur.

En utilisant des fonctions comme populate il est moins fastidieux de créer ces objets et de voir à quoi ressemblent les données.

Plus après le saut! Continuez à lire ci-dessous ↓

vérifie cette radio

L’une des alternatives à l’approche ci-dessus que j’ai trouvée est de conserver le active état, que l’onglet soit sélectionné ou non, stocké en tant que propriété de tabs objet:

const tabs = [
    {
        label: "Summary",
        selected: true
    },
    {
        label: "Details",
        selected: false
    },
];

On remplace ainsi tab === active avec tab.selected. Cela peut sembler une amélioration, mais regardez comment nous devrions changer l’onglet sélectionné :

function select_tab(tab, tabs) {
    tabs.forEach((tab) => tab.selected = false);
    tab.selected = true;
}

Comme c’est logique pour un bouton radio, un seul élément peut être sélectionné à la fois. Ainsi, avant de définir un élément à sélectionner, nous devons d’abord nous assurer que tous les autres éléments sont désélectionnés. Oui, c’est idiot de faire comme ça pour un tableau avec seulement deux éléments, mais le monde réel est plein de listes plus longues que cet exemple.

Avec un objet primitif, il faut une seule variable représentant l’état sélectionné. Je suggère de définir la variable sur l’un des éléments pour en faire l’élément actuellement sélectionné ou de le définir sur undefined si votre implémentation ne permet aucune sélection.

Avec des éléments à choix multiples comme des cases à cocher, l’approche est presque la même. Nous remplaçons la variable select par un tableau. Chaque fois qu’un élément est sélectionné, nous le poussons vers ce tableau, ou dans le cas de Redux, créons un nouveau tableau avec cet élément présent. Pour le désélectionner, nous raccordons ou filtrons l’élément.

let selected = []; // Nothing is selected.

// Select.
selected = selected.concat([ to_be_selected ]);

// Unselect.
selected = selected.filter((element) => element !== to_be_unselected);

// Check if an element is selected.
selected.includes(element);

Encore une fois, c’est simple et concis. Pas besoin de se rappeler si la propriété s’appelle selected soit active; vous utilisez l’objet lui-même pour le déterminer. Lorsque votre programme devient plus complexe, ces lignes sont les moins susceptibles d’être refactorisées.

À la fin, ce n’est pas le travail d’un élément de liste de décider de le sélectionner ou non. Vous ne devez pas inclure ces informations dans votre déclaration. Par exemple, que se passe-t-il si vous sélectionnez simultanément et ne sélectionnez pas plusieurs listes à la fois ?

Alternative aux chaînes

La dernière chose que j’aimerais aborder est un exemple d’utilisation de chaînes que je rencontre souvent.

Le texte est un bon compromis pour l’interopérabilité. Vous définissez quelque chose comme une chaîne et obtenez instantanément une représentation d’un contexte. C’est comme avoir une bouffée d’énergie instantanée en mangeant du sucre. Comme pour le sucre, le meilleur scénario est que vous n’obtenez rien à long terme. Cela dit, c’est en dessous de la moyenne et vous revenez inévitablement affamé.

Le problème avec les cordes, c’est qu’elles sont pour les humains. Il est naturel pour nous de distinguer les choses en leur donnant un nom. Mais un programme ne comprend pas la signification de ces noms.

Les chaînes ne sont pas comprises par la plupart des éditeurs de code et des environnements de développement intégrés (IDE). En d’autres termes, vos outils ne vous diront pas si la chaîne est correcte ou non.

Votre programme ne sait que si deux chaînes sont égal Ou non. Et même dans ce cas, dire si les chaînes sont égales ou inégales ne donne pas nécessairement une idée si l’une de ces chaînes contient ou non une faute de frappe.

Les objets fournissent plus de moyens de voir que quelque chose ne va pas avant d’exécuter le programme. Comme vous ne pouvez pas avoir de littéraux pour les objets primitifs, vous devez obtenir une référence quelque part. Par exemple, s’il s’agit d’une variable et que vous faites une faute de frappe, vous obtenez un erreur de référence. Il existe des outils qui pourraient détecter ce genre de chose avant que le fichier ne soit enregistré.

Si vous deviez obtenir vos objets à partir d’un tableau ou d’un autre objet, JavaScript ne vous donnera pas d’erreur lorsque la propriété ou l’index n’existe pas. ce que vous obtenez est undefined, et c’est quelque chose que vous pourriez vérifier. Vous n’avez qu’une chose à vérifier. Avec les chaînes, vous avez des surprises que vous voudrez peut-être éviter, comme lorsqu’elles sont vides.

Une autre utilisation des chaînes que j’essaie d’éviter est de vérifier si nous obtenons l’objet que nous voulons. Cela se fait généralement en stockant une chaîne dans une propriété appelée id. Comme, disons que nous avons une variable. Pour vérifier s’il contient l’objet que nous voulons, nous devrons peut-être vérifier si une chaîne dans le id la propriété correspond à ce que nous attendons. Pour ce faire, nous vérifierions d’abord si la variable contient un objet. Si la variable contient un objet, mais que l’objet n’a pas le id propriété, alors on obtient undefined, et nous allons bien. Cependant, si nous avons l’une des valeurs les plus basses dans cette variable, nous ne pouvons pas demander directement la propriété. Au lieu de cela, nous devons faire quelque chose pour nous assurer que seuls les objets arrivent à ce point, ou pour faire les deux vérifications à la place.

const myID = "Oh, it's so unique";

function magnification(value) {
    if (value && typeof value === "object" && value.id === myID) {
        // do magic
    }
}

Voici comment nous pouvons faire la même chose avec des objets primitifs :

import data from "./the file where data is stored";

function magnification(value) {
    if (value === data.myObject) {
        // do magic
    }
}

L’avantage des chaînes est qu’elles sont une seule chose qui pourrait être utilisée pour l’identification interne et qui sont immédiatement reconnaissables dans les journaux. Bien sûr, ils sont faciles à utiliser dès la sortie de la boîte, mais ils ne sont pas vos amis à mesure que la complexité d’un projet augmente.

Réunion il y a peu d’avantages à s’appuyer sur des chaînes pour autre chose que la sortie vers l’utilisateur. Le manque d’interopérabilité de chaîne dans les objets primitifs pourrait être résolu progressivement et sans avoir besoin de changer la façon dont vous gérez les opérations de base comme les comparaisons.

Fin

Travailler directement avec des objets nous libère des pièges qui viennent avec d’autres méthodes. Notre code devient plus simple parce que nous écrivons ce que votre programme doit faire. En organisant votre code avec des objets primitifs, nous sommes moins affectés par la nature dynamique de JavaScript et de certains de ses bagages. Les objets primitifs nous donnent plus de garanties et un plus haut degré de prévisibilité.

Lecture supplémentaire sur SmashingMag

édito fracassant
(jj, et k, le)

Laissez un commentaire

Derniers Posts
Une Question ? Un Projet ?
Quel que soit votre projet, MIOR AGENCY vous écoute, analyse vos besoins et propose des pistes de travail en conséquence. Vous avancez avec sérénité et confiance.