Salut les amis ! Il y a quelque temps, en parcourant les dernières propositions du TC39, je suis tombé sur une proposition qui m'a enthousiasmé – et un peu sceptique. Il s'agit d'une syntaxe d'application partielle pour JavaScript. À première vue, cela semble être la solution parfaite à de nombreux problèmes de codage courants, mais en y réfléchissant, j'ai réalisé qu'il y avait à la fois beaucoup de choses à aimer et des possibilités d'amélioration.
Mieux encore, ces préoccupations ont donné naissance à une toute nouvelle idée qui pourrait rendre JavaScript encore plus puissant. Laissez-moi vous emmener dans ce voyage, complété par des exemples réalistes de la façon dont ces fonctionnalités pourraient changer notre façon de coder chaque jour.
TLDR : l'article provient de mon ancien numéro à la proposition : https://github.com/tc39/proposal-partial-application/issues/53
L'application partielle vous permet de « prédéfinir » certains arguments d'une fonction, renvoyant une nouvelle fonction pour une utilisation ultérieure. Notre code actuel ressemble à ceci :
const fetchWithAuth = (path: string) => fetch( { headers: { Authorization: "Bearer token" } }, path, ); fetchWithAuth("/users"); fetchWithAuth("/posts");
La proposition introduit une syntaxe ~() pour cela :
const fetchWithAuth = fetch~({ headers: { Authorization: "Bearer token" } }, ?); fetchWithAuth("/users"); fetchWithAuth("/posts");
Vous voyez ce qui se passe ? La fonction fetchWithAuth pré-remplit l'argument des en-têtes, vous n'avez donc qu'à fournir l'URL. C'est comme .bind() mais plus flexible et plus facile à lire.
La proposition vous permet également d'utiliser ? comme espace réservé pour les arguments non remplis et ... pour un paramètre de repos. Par exemple :
const sendEmail = send~(user.email, ?, ...); sendEmail("Welcome!", "Hello and thanks for signing up!"); sendEmail("Reminder", "Don't forget to confirm your email.");
Ce que je préfère, c'est que je n'ai pas besoin de dupliquer les annotations de type !
Cela semble utile, non ? Mais il y a bien plus à déballer.
Commençons par un problème pratique : fermetures de fonctions et références de variables obsolètes.
Disons que vous planifiez une notification. Vous pourriez écrire quelque chose comme ceci :
function notify(state: { data?: Data }) { if (state.data) { setTimeout(() => alert(state.data), 1000) } }
Avez-vous déjà vu le problème ? La propriété « data » peut changer pendant le délai d'attente et l'alerte n'affichera rien ! Pour résoudre ce problème, il faut transmettre explicitement la référence de valeur. Espérons que "setTimeout" accepte des arguments supplémentaires pour la transmettre dans le rappel :
function notify(state: { data?: Data }) { if (state.data) { setTimeout((data) => alert(data), 1000, state.data) } }
Pas mal, mais ce n'est pas largement pris en charge par les API. Une application partielle pourrait rendre ce modèle beaucoup plus universel :
function notify(state: { data?: Data }) { if (state.data) { setTimeout(alert~(state.data), 1000) } }
En verrouillant state.data au moment de la création de la fonction, nous évitons les bugs inattendus dus à des références obsolètes.
Un autre avantage pratique de l'application partielle est l'élimination du travail redondant lors du traitement de grands ensembles de données.
Par exemple, vous disposez d'une logique de mappage, qui doit calculer des données supplémentaires pour chaque étape d'itération :
const fetchWithAuth = (path: string) => fetch( { headers: { Authorization: "Bearer token" } }, path, ); fetchWithAuth("/users"); fetchWithAuth("/posts");
Le problème vient de l'accès proxy à this.some.another, c'est assez lourd pour appeler chaque étape d'itération. Il serait préférable de refactoriser ce code comme ceci :
const fetchWithAuth = fetch~({ headers: { Authorization: "Bearer token" } }, ?); fetchWithAuth("/users"); fetchWithAuth("/posts");
Avec une application partielle, nous pouvons le faire de manière moins verbeuse :
const sendEmail = send~(user.email, ?, ...); sendEmail("Welcome!", "Hello and thanks for signing up!"); sendEmail("Reminder", "Don't forget to confirm your email.");
En intégrant des calculs partagés, vous rendez le code plus concis et plus facile à suivre, sans sacrifier les performances.
Maintenant, c’est ici que j’ai commencé à me gratter la tête. Si la syntaxe proposée est élégante, JavaScript possède déjà de nombreux opérateurs. Surtout les opérateurs de point d'interrogation ?. L'ajout de ~() pourrait rendre le langage plus difficile à apprendre et à analyser.
Et si nous pouvions obtenir la même fonctionnalité sans introduire de nouvelle syntaxe ?
Imaginez étendre Function.prototype avec une méthode tie :
function notify(state: { data?: Data }) { if (state.data) { setTimeout(() => alert(state.data), 1000) } }
C'est un peu plus verbeux mais évite d'introduire un tout nouvel opérateur. En utilisant un symbole spécial supplémentaire pour les espaces réservés, nous pouvons remplacer le point d'interrogation.
function notify(state: { data?: Data }) { if (state.data) { setTimeout((data) => alert(data), 1000, state.data) } }
Il polypile parfaitement sans complexité supplémentaire de construction !
function notify(state: { data?: Data }) { if (state.data) { setTimeout(alert~(state.data), 1000) } }
Mais ce n’est que la pointe de l’iceberg. cela rend le concept d'espace réservé réutilisable dans différentes API.
C’est ici que les choses deviennent vraiment intéressantes. Et si nous élargissions le concept des symboles pour permettre des opérations paresseuses ?
Supposons que vous traitiez une liste de produits pour un site de commerce électronique. Vous souhaitez afficher uniquement les articles en promotion, avec leurs prix arrondis. Normalement, vous écririez ceci :
class Store { data: { list: [], some: { another: 42 } } get computedList() { return this.list.map((el) => computeElement(el, this.some.another)) } contructor() { makeAutoObservable(this) } }
Mais cela nécessite de parcourir le tableau deux fois. Avec des opérations paresseuses, nous pourrions combiner les deux étapes en une seule passe :
class Store { data: { list: [], some: { another: 42 } } get computedList() { const { another } = this.some return this.list.map((el) => computeElement(el, another)) } contructor() { makeAutoObservable(this) } }
Le Symbol.skip indique au moteur d'exclure les éléments du tableau final, ce qui rend l'opération à la fois efficace et expressive !
Imaginez calculer le revenu total des cinq premières ventes. Normalement, vous utiliseriez un conditionnel à l'intérieur de .reduce() :
class Store { data: { list: [], some: { another: 42 } } get computedList() { return this.list.map(computeElement~(?, this.some.another)) } contructor() { makeAutoObservable(this) } }
Cela fonctionne, mais il traite toujours chaque élément du tableau. Avec des réductions paresseuses, nous pourrions signaler une résiliation anticipée :
function notify(state: { data?: Data }) { if (state.data) { setTimeout(alert.tie(state.data), 1000) } }
La présence de Symbol.skip pourrait indiquer au moteur d'arrêter d'itérer dès que la condition est remplie, économisant ainsi de précieux cycles.
Ces idées – application partielle, transparence référentielle et opérations paresseuses – ne sont pas que des concepts académiques. Ils résolvent des problèmes du monde réel :
Que nous nous en tenions à ~() ou que nous explorions des alternatives comme tie et Symbol.skip, les principes sous-jacents ont un énorme potentiel pour améliorer la façon dont nous écrivons JavaScript.
Je vote pour l'approche symbolique car elle est facile à polyfill et a diverses utilisations.
Je suis curieux : qu’en pensez-vous ? Est-ce que ~() est la bonne direction ou devrions-nous explorer des approches basées sur des méthodes ? Et quel serait l'impact des opérations paresseuses sur votre flux de travail ? Discutons-en dans les commentaires !
La beauté de JavaScript réside dans son évolution pilotée par la communauté. En partageant et en débattant d’idées, nous pouvons façonner un langage qui fonctionne mieux pour tout le monde. Poursuivons la conversation !
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!