Underscore: Erreur des méthodes de chaîne de tableaux sur des valeurs non-tableaux

Créé le 20 mars 2016  ·  6Commentaires  ·  Source: jashkenas/underscore

Erreur des méthodes de chaîne de tableau sur les valeurs non-tableau, contrairement aux autres méthodes de catégorie de tableau.

_().pop(); // error
_('').pop(); // error
_().first() // undefined

Commentaire le plus utile

Cela soulève un autre problème, devrions-nous convertir les array-likes en array ?

_('hello').first() // 'h'
_('hello').pop() // ?

Tous les 6 commentaires

Cela soulève un autre problème, devrions-nous convertir les array-likes en array ?

_('hello').first() // 'h'
_('hello').pop() // ?

Cela semble toujours être un problème - nous l'avons rencontré après avoir fait passer notre version de 1.8.3 à 1.9.0

Edit : Il semble que cela ait été corrigé dans la version 1.9.1. Si tel est le cas, ce problème doit-il rester ouvert ?

Ce comportement est toujours présent dans 1.10.2.

Un contraste un peu plus convaincant dans mon option, puisque les deux impliquent de modifier une valeur en place :

Object.assign(undefined, {a: 1})  // error
_().extend({a: 1})  // undefined

Array.prototype.push.call(undefined, 1)  // error
_().push(1)  // error

Je suis un peu d'accord que c'est incohérent.

Il convient de considérer dans quel type de situation réelle cela se produirait le plus probablement et à quoi s'attendre de l'objet enveloppé dans de tels cas.

Au milieu d'une chaîne, l'objet enveloppé est prévisible. Par exemple, dans la chaîne ci-dessous, l'objet enveloppé qui est passé à push sera soit Array soit undefined . Pour cette raison, je dirais que les méthodes wrapper Array devraient implémenter une vérification nulle. C'est facile.

_.chain(something).map(f).push(x)  // hoping to push x to an array

Si l'objet enveloppé prédit est plus susceptible d'être autre chose qu'un tableau, il s'agit sans doute d'une erreur du programmeur. Cela ne me dérange pas de lancer une exception dans ce cas.

_.chain(something).map(f).join('').push(x)  // hoping to push x to a string??

Au début d'une chaîne, l'histoire peut sembler un peu différente à première vue, mais je soutiendrai que c'est la même chose et que nous devrions la laisser à un contrôle nul. Au moins, le programmeur doit avoir une raison de s'attendre à ce que something soit un tableau mutable, car sinon, il n'y aurait aucune raison d'appeler push :

function giveMeAMutableArraylike(something) {
    _.chain(something).push(x)
}

L'attente selon laquelle something est un tableau mutable pourrait ne pas être satisfaite pour diverses raisons :

  1. giveMeAMutableArraylike a fini par être payé avec null ou undefined pour une raison quelconque. Ceci est comparable au premier exemple de chaîne au milieu et peut être résolu avec le même contrôle nul.
  2. L'appelant essaie de faire faire à giveMeAMutableArraylike quelque chose qu'il ne peut manifestement pas faire, c'est-à-dire rompre le contrat. Ceci est comparable au deuxième exemple de chaîne au milieu. Encore une fois, je pense que c'est bien de lancer une erreur dans ce cas.
  3. something est une valeur nue au lieu d'un tableau avec un seul élément, c'est-à-dire v au lieu de [v] . Il s'agit d'une situation courante dans de nombreuses API JavaScript. Si le contrat de giveMeAMutableArraylike permet, il est évidemment faux de lancer une erreur indépendamment de ce que v se trouve être.

Dans ce dernier cas, Underscore ne peut pas savoir si giveMeAMutableArraylike autorise ou non les éléments nus simples, il appartient donc au programmeur de giveMeAMutableArraylike d'implémenter son contrat particulier. Il n'y a rien qu'une bibliothèque comme Underscore puisse faire qui fera la bonne chose pour tous les contrats possibles :

| Comportement actuel | Envelopper dans un tableau à un seul élément
---|---|---
Le contrat autorise un seul élément nu | _Le programmeur doit intervenir_ | Underscore fait automatiquement ce qu'il faut
Le contrat n'autorise pas un seul élément nu | Underscore fait automatiquement ce qu'il faut | _Le programmeur doit intervenir_

De plus, comment décidez-vous si une valeur doit être enveloppée ? Certains contrats peuvent vouloir envelopper tout ce qui n'est pas un Array , tandis que d'autres peuvent vouloir passer des arguments et des objets simples tels quels. Encore une fois, c'est quelque chose qu'une bibliothèque comme Underscore ne peut pas deviner au nom de l'utilisateur.

Des situations plus exotiques sont également envisageables, par exemple un contrat qui autorise des objets immuables de type tableau tels que des chaînes et qui les convertira d'abord en Array . Il est impossible de couvrir toutes les variantes possibles. Selon le principe du moindre changement, il n'y a tout simplement aucun argument convaincant pour soutenir certains contrats au détriment de contrats qui étaient déjà pris en charge. Il existe également de solides arguments en faveur d'une mise en œuvre aussi minime que possible.

Je pense donc que la bonne solution consiste à insérer simplement une vérification nulle et à en rester là. C'est le seul changement au comportement de Underscore qui semble défendable dans toutes les situations imaginables et il est également cohérent avec le comportement de _.extend . Une vérification nulle pourrait toujours être considérée comme une correction de bogue, tandis que faire autre chose que cela la transformerait rapidement en un changement cassant arbitraire.

@jashkenas pourriez-vous éclairer cela ? Si vous êtes d'accord, je préparerai une demande d'extraction qui implémente la vérification nulle.

@jgonggrijp — Pouvez-vous développer en une phrase ou deux le comportement que vous souhaitez implémenter avec la vérification null ?

Est-ce que les méthodes de tableau lèveront explicitement une exception lorsqu'elles seront appelées sur une valeur nulle ? Ou qu'ils vont noop lorsqu'ils sont appelés sur une valeur nulle ?

@jashkenas Oui. Le comportement que j'implémenterais est un no-op, suivant le style des fonctions de tableau Underscore :

https://github.com/jashkenas/underscore/blob/4cf715f593805ba8d7c5685cd06c82b3cd9b55ae/modules/index.js#L495

Sauf que la vérification length ne serait pas nécessaire, donc je voudrais simplement insérer

if (obj == null) return chainresult(this, obj);

entre ces deux lignes :

https://github.com/jashkenas/underscore/blob/4cf715f593805ba8d7c5685cd06c82b3cd9b55ae/modules/index.js#L1654 -L1655

Plus des tests bien sûr, et si ces tests révèlent un besoin, peut-être aussi une vérification null qui transforme la méthode en no-op avant cette ligne :

https://github.com/jashkenas/underscore/blob/4cf715f593805ba8d7c5685cd06c82b3cd9b55ae/modules/index.js#L1665

Ça me va bien !

Cette page vous a été utile?
0 / 5 - 0 notes

Questions connexes

umarfarooq125 picture umarfarooq125  ·  8Commentaires

githublyp picture githublyp  ·  3Commentaires

arieljake picture arieljake  ·  4Commentaires

acl0056 picture acl0056  ·  5Commentaires

xiaoliwang picture xiaoliwang  ·  3Commentaires