Underscore: Fehler bei Array-Kettenmethoden bei Nicht-Array-Werten

Erstellt am 20. März 2016  ·  6Kommentare  ·  Quelle: jashkenas/underscore

Fehler bei Array-Kettenmethoden bei Nicht-Array-Werten, was im Gegensatz zu anderen Array-Kategoriemethoden steht.

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

Hilfreichster Kommentar

Dies wirft ein weiteres Problem auf, sollten wir Array-Likes in ein Array umwandeln?

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

Alle 6 Kommentare

Dies wirft ein weiteres Problem auf, sollten wir Array-Likes in ein Array umwandeln?

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

Dies scheint immer noch ein Problem zu sein – wir sind darauf gestoßen, nachdem wir unsere Version von 1.8.3 auf 1.9.0 hochgestuft haben

Bearbeiten: Es sieht so aus, als ob dies in 1.9.1 behoben wurde, wenn ja, muss dieses Problem offen bleiben?

Dieses Verhalten ist in 1.10.2 immer noch vorhanden.

Ein etwas überzeugenderer Kontrast in meiner Option, da beide die Änderung eines vorhandenen Werts beinhalten:

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

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

Ich stimme etwas zu, dass dies inkonsistent ist.

Es lohnt sich zu überlegen, in welcher realen Situation dies am wahrscheinlichsten vorkommt und was in solchen Fällen von dem umhüllten Objekt zu erwarten ist.

In der Mitte einer Kette ist das umhüllte Objekt vorhersagbar. In der Kette unten ist beispielsweise das umschlossene Objekt, das an push übergeben wird, entweder Array oder undefined . Aus diesem Grund würde ich argumentieren, dass die Array Wrapper-Methoden eine Nullprüfung implementieren sollten. Das ist einfach.

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

Wenn das vorhergesagte umschlossene Objekt eher etwas anderes als ein Array ist, dann handelt es sich wohl um einen Programmierfehler. Es macht mir nichts aus, in diesem Fall eine Ausnahme auszulösen.

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

Am Anfang einer Kette mag die Geschichte auf den ersten Blick etwas anders erscheinen, aber ich werde argumentieren, dass es dasselbe ist und dass wir es bei einer Nullprüfung belassen sollten. Zumindest muss es so sein, dass der Programmierer einen Grund hat zu erwarten, dass something ein änderbares Array ist, da es sonst keinen Grund gäbe, push aufzurufen:

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

Die Erwartung, dass something ein änderbares Array ist, könnte aus verschiedenen Gründen nicht erfüllt werden:

  1. giveMeAMutableArraylike $ wurde aus irgendeinem Grund mit null oder undefined gecallt. Dies ist vergleichbar mit dem ersten Chain-in-the-Middle-Beispiel und kann mit derselben Nullprüfung angegangen werden.
  2. Der Anrufer versucht, giveMeAMutableArraylike dazu zu bringen, etwas zu tun, was er offensichtlich nicht kann, dh den Vertrag zu brechen. Dies ist vergleichbar mit dem zweiten Chain-in-the-Middle-Beispiel. Auch hier denke ich, dass es in Ordnung ist, einen Fehler zu werfen.
  3. something ist ein reiner Wert anstelle eines Arrays mit einem einzelnen Element, dh v anstelle von [v] . Dies ist eine häufige Situation in vielen JavaScript-APIs. Wenn der Vertrag von giveMeAMutableArraylike dies zulässt, ist es offensichtlich falsch, einen Fehler zu werfen, unabhängig davon, was v gerade ist.

Im letzteren Fall kann Underscore nicht wissen, ob giveMeAMutableArraylike einzelne Bare-Elemente zulässt oder nicht, also ist es Sache des Programmierers von giveMeAMutableArraylike , seinen speziellen Vertrag zu implementieren. Es gibt nichts, was eine Bibliothek wie Underscore tun kann, um für alle möglichen Verträge das Richtige zu tun:

| Aktuelles Verhalten | Wrap in Einzelelement-Array
---|---|---
Vertrag erlaubt bloßes einzelnes Element | _Programmierer muss eingreifen_ | Underscore macht automatisch das Richtige
Der Vertrag erlaubt kein bloßes Einzelelement | Der Unterstrich macht automatisch das Richtige | _Programmierer muss eingreifen_

Wie entscheiden Sie außerdem, ob ein Wert umschlossen werden soll? Einige Verträge möchten möglicherweise alles umschließen, was kein Array ist, während andere möglicherweise arguments und einfache Objekte unverändert weitergeben möchten. Auch dies ist etwas, was eine Bibliothek wie Underscore nicht für den Benutzer erraten kann.

Auch exotischere Situationen sind denkbar, zum Beispiel ein Vertrag, der unveränderliche Array-ähnliche Objekte wie Strings zulässt und diese zuerst in Array konvertiert. Es ist unmöglich, alle möglichen Variationen abzudecken. Nach dem Prinzip der geringsten Änderung gibt es einfach kein überzeugendes Argument dafür, einige Verträge auf Kosten bereits unterstützter Verträge zu unterstützen. Es gibt auch starke Argumente dafür, die Implementierung so gering wie möglich zu halten.

Ich denke also, die richtige Lösung besteht darin, nur eine Nullprüfung einzufügen und es dabei zu belassen. Dies ist die einzige Änderung am Verhalten von Underscore, die in allen denkbaren Situationen vertretbar erscheint und auch mit dem Verhalten von _.extend übereinstimmt. Ein Null-Check könnte immer noch als Bugfix angesehen werden, während alles andere es schnell in eine willkürliche Breaking Change verwandeln würde.

@jashkenas könntest du dein Licht darauf richten? Wenn Sie damit einverstanden sind, bereite ich einen Pull-Request vor, der die Nullprüfung implementiert.

@jgonggrijp – Können Sie das Verhalten, das Sie mit der Nullprüfung implementieren möchten, in ein oder zwei Sätzen erläutern?

Ist es so, dass Array-Methoden explizit eine Ausnahme auslösen, wenn sie für einen Nullwert aufgerufen werden? Oder dass sie noopen, wenn sie mit einem nullischen Wert aufgerufen werden?

@jashkenas Ja. Das Verhalten, das ich implementieren würde, ist ein No-Op, das dem Stil der Underscore-Array-Funktionen folgt:

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

Nur dass der length -Check nicht erforderlich wäre, also würde ich einfach einfügen

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

zwischen diesen beiden Zeilen:

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

Plus Tests natürlich, und wenn diese Tests einen Bedarf ergeben, vielleicht auch eine Nullprüfung, die die Methode vor dieser Zeile in eine No-Op verwandelt:

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

Klingt gut für mich!

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen

Verwandte Themen

arieljake picture arieljake  ·  4Kommentare

Francefire picture Francefire  ·  5Kommentare

xiaoliwang picture xiaoliwang  ·  3Kommentare

arypbatista picture arypbatista  ·  3Kommentare

sky0014 picture sky0014  ·  8Kommentare