Wie Sie vielleicht wissen, in JavaScript '' + null = "null"
und '' + undefined = "undefined"
(in den meisten Browsern kann ich testen: Firefox, Chrome und IE). Ich würde gerne wissen, woher diese Kuriosität stammt (was zum Teufel war bei Brendan Eich im Kopf?!) Und ob es ein Ziel gibt, sie in einer zukünftigen Version von ECMA zu ändern. Es ist in der Tat ziemlich frustrierend, 'sthg' + (var || '')
Strings mit Variablen zu verketten und ein Framework von Drittanbietern wie Underscore oder ein anderes zu verwenden, um einen Hammer zum Stampfen von Gelee-Nägeln zu verwenden.
Bearbeiten:
Um die von StackOverflow geforderten Kriterien zu erfüllen und meine Frage zu klären, gibt es drei Gründe:
- Was ist die Geschichte hinter der Seltsamkeit, die JS zur Konvertierung
null
oderundefined
zu ihrem Zeichenfolgenwert bei derString
Verkettung führt? - Gibt es eine Chance für eine Änderung dieses Verhaltens in zukünftigen ECMAScript-Versionen?
- Was ist der schönste Weg, um
String
mit Potentialennull
oderundefined
Objekten zu verketten, ohne in dieses Problem zu geraten (einige"undefined"
davon"null"
in der Mitte des Strings zu bekommen)? Durch die subjektiven Kriterien hübscheste , ich meine: kurz, sauber und effektiv. Keine Notwendigkeit zu sagen, dass'' + (obj ? obj : '')
das nicht wirklich hübsch ist ...
javascript
null
undefined
string-concatenation
Doc Davluz
quelle
quelle
R
Hintergrund und dort bekommen Sie, was Sie gesagt habenpaste0("a", NULL) == "a"
toString
odervalueOf
Methoden zugewiesen wurden . Ich würde mir vorstellen, dass es so viele Umstände gibt, unter denen Sie möchten, dass sie angezeigt werden,null
oderundefined
wie Sie möchten, dass sie als leere Zeichenfolge angezeigt werden. Glücklicherweise stoße ich kaum auf dieses Problem, da ich Zeichenfolgen häufig mit Join verkette (es löst andere Probleme, z. B. Verkettungen bei Verwendung von ternären (?
) Ausdrücken).Antworten:
Es gibt verschiedene Möglichkeiten, und Sie haben sie teilweise selbst erwähnt. Um es kurz zu machen, der einzige saubere Weg, den ich mir vorstellen kann, ist eine Funktion:
const Strings = {}; Strings.orEmpty = function( entity ) { return entity || ""; }; // usage const message = "This is a " + Strings.orEmpty( test );
Natürlich können (und sollten) Sie die tatsächliche Implementierung an Ihre Bedürfnisse anpassen. Und schon deshalb halte ich diese Methode für überlegen: Sie hat die Kapselung eingeführt.
Wirklich, Sie müssen nur fragen, was der "schönste" Weg ist, wenn Sie keine Kapselung haben. Sie stellen sich diese Frage, weil Sie bereits wissen, dass Sie sich an einen Ort begeben werden, an dem Sie die Implementierung nicht mehr ändern können. Sie möchten also, dass sie sofort perfekt ist. Aber das ist die Sache: Anforderungen, Ansichten und sogar Umgebungen ändern sich. Sie entwickeln sich. Warum erlauben Sie sich nicht, die Implementierung mit nur einer Zeile und vielleicht einem oder zwei Tests zu ändern?
Man könnte dies als Betrug bezeichnen, da es nicht wirklich antwortet, wie die eigentliche Logik implementiert werden soll. Aber das ist mein Punkt: Es spielt keine Rolle. Na ja, vielleicht ein bisschen. Aber wirklich, es besteht kein Grund zur Sorge, weil es so einfach wäre, Änderungen vorzunehmen. Und da es nicht inline ist, sieht es auch viel hübscher aus - ob Sie es auf diese Weise oder auf eine raffiniertere Weise implementieren oder nicht.
Wenn Sie im gesamten Code die
||
Inline-Funktion wiederholen, treten zwei Probleme auf:Und dies sind zwei Punkte, die allgemein als Anti-Patterns bekannt sind, wenn es um qualitativ hochwertige Softwareentwicklung geht.
Einige Leute werden sagen, dass dies zu viel Aufwand ist; Sie werden über Leistung sprechen. Das ist Unsinn. Zum einen erhöht dies kaum den Overhead. Wenn Sie sich darüber Sorgen machen, haben Sie die falsche Sprache gewählt. Sogar jQuery verwendet Funktionen. Die Menschen müssen über die Mikrooptimierung hinwegkommen.
Die andere Sache ist: Sie können einen Code "compiler" = minifier verwenden. Gute Tools in diesem Bereich versuchen zu erkennen, welche Anweisungen während des Kompilierungsschritts inline sind. Auf diese Weise halten Sie Ihren Code sauber und wartbar und können immer noch den letzten Leistungsabfall erzielen, wenn Sie immer noch daran glauben oder wirklich eine Umgebung haben, in der dies wichtig ist.
Schließlich haben Sie etwas Vertrauen in Browser. Sie werden den Code optimieren und machen heutzutage verdammt gute Arbeit.
quelle
Sie können verwenden, um
Array.prototype.join
zu ignorierenundefined
undnull
:['a', 'b', void 0, null, 6].join(''); // 'ab6'
Nach der Spezifikation :
Angesichts dessen,
In einigen Fällen ist das aktuelle Verhalten sogar sinnvoll.
function showSum(a,b) { alert(a + ' + ' + b + ' = ' + (+a + +b)); }
Wenn zum Beispiel die obige Funktion ohne Argumente aufgerufen wird,
undefined + undefined = NaN
ist sie wahrscheinlich besser als+ = NaN
.Im Allgemeinen denke ich, dass, wenn Sie einige Variablen in einen String einfügen möchten, das Anzeigen
undefined
odernull
Sinn macht. Wahrscheinlich hat das auch Eich gedacht.Natürlich gibt es Fälle, in denen es besser wäre, diese zu ignorieren, beispielsweise wenn Zeichenfolgen miteinander verbunden werden. Aber für diese Fälle können Sie verwenden
Array.prototype.join
.Höchst wahrscheinlich nicht.
Da gibt es schon
Array.prototype.join
Fall , würde das Ändern des Verhaltens der Zeichenfolgenverkettung nur Nachteile, aber keine Vorteile verursachen. Außerdem würde es alte Codes brechen, so dass es nicht abwärtskompatibel wäre.Array.prototype.join
scheint der einfachste zu sein. Ob es das schönste ist oder nicht, kann meinungsbasiert sein.quelle
join
Argument verwenden..trim()
nach.join()
hinzufügen, werden führende Leerzeichen / Testfelder entfernt. (Bei Verbindung mit Leerzeichen). In ähnlicher Weise können andere Arten von Zeichen entfernt werdenDie ECMA-Spezifikation
Nur um den Grund zu verdeutlichen, warum es sich in Bezug auf die Spezifikation so verhält, ist dieses Verhalten seit Version eins vorhanden . Die Definition dort und in 5.1 ist semantisch äquivalent, ich werde die 5.1-Definitionen zeigen.
Abschnitt 11.6.1: Der Additionsoperator (+)
Wenn also einer der Werte a ist
String
,ToString
wird er für beide Argumente verwendet (Zeile 7) und diese werden verkettet (Zeile 7a).ToPrimitive
Gibt alle Nicht-Objektwerte unverändert zurücknull
undundefined
bleibt daher unberührt:Abschnitt 9.1 ToPrimitive
Bei allen nicht -
Object
Typen, einschließlichNull
undUndefined
,[t]he result equals the input argument (no conversion).
soToPrimitive
tut hier nichts.Schließlich Abschnitt 9.8 ToString
Tabelle 13 gibt
"undefined"
für denUndefined
Typ und"null"
für denNull
Typ an.Wird es sich ändern? Ist es überhaupt eine "Kuriosität"?
Wie andere bereits betont haben, ist es sehr unwahrscheinlich, dass sich dies ändert, da dies die Abwärtskompatibilität beeinträchtigen würde (und keinen wirklichen Nutzen bringt), zumal dieses Verhalten seit der Version der Spezifikation von 1997 dasselbe ist. Ich würde es auch nicht wirklich als Kuriosität betrachten.
Wenn Sie dieses Verhalten ändern würden, würden Sie die Definition von
ToString
for ändernnull
undundefined
oder würden Sie den Additionsoperator für diese Werte in Sonderfällen festlegen?ToString
wird an vielen, vielen Stellen in der Spezifikation verwendet und"null"
scheint eine unumstrittene Wahl für die Darstellung zu seinnull
. Um nur einige Beispiele zu nennen: In Java"" + null
ist die Zeichenfolge"null"
und in Pythonstr(None)
die Zeichenfolge"None"
.Problemumgehung
Andere haben gute Abhilfen gegeben, aber ich möchte hinzufügen , dass ich bezweifle , die Sie verwenden möchten ,
entity || ""
wie Sie Ihre Strategie , da sie beschließttrue
zu"true"
aberfalse
zu""
. Der Array-Join in dieser Antwort weist das eher erwartete Verhalten auf, oder Sie können die Implementierung dieser Antwort ändern, um sie zu überprüfenentity == null
(beidenull == null
undundefined == null
sind wahr).quelle
Ich würde sagen, die Chancen sind sehr gering. Und es gibt mehrere Gründe:
Wir wissen bereits, wie ES5 und ES6 aussehen
Die zukünftigen ES-Versionen sind bereits fertig oder im Entwurf. Keiner, afaik, ändert dieses Verhalten. Beachten Sie dabei, dass es Jahre dauern wird, bis diese Standards in Browsern in dem Sinne etabliert sind, dass Sie Anwendungen mit diesen Standards schreiben können, ohne sich auf Proxy-Tools verlassen zu müssen, die sie zu tatsächlichem Javascript kompilieren.
Versuchen Sie einfach, die Dauer zu schätzen. Nicht einmal ES5 wird von den meisten Browsern vollständig unterstützt, und es wird wahrscheinlich noch einige Jahre dauern. ES6 ist noch nicht einmal vollständig spezifiziert. Aus heiterem Himmel schauen wir uns noch mindestens fünf Jahre an.
Browser machen ihre eigenen Sachen
Es ist bekannt, dass Browser zu bestimmten Themen ihre eigenen Entscheidungen treffen. Sie wissen nicht, ob alle Browser diese Funktion auf genau dieselbe Weise vollständig unterstützen. Natürlich würden Sie wissen, sobald es Teil des Standards ist, aber selbst wenn es angekündigt wurde, Teil von ES7 zu werden, wäre es ab sofort bestenfalls Spekulation.
Und Browser können hier ihre eigene Entscheidung treffen, insbesondere weil:
Diese Änderung bricht
Eines der größten Dinge an Standards ist, dass sie normalerweise versuchen, abwärtskompatibel zu sein. Dies gilt insbesondere für das Web, in dem derselbe Code für alle Arten von Umgebungen ausgeführt werden muss.
Wenn der Standard eine neue Funktion einführt und diese in alten Browsern nicht unterstützt wird, ist das eine Sache. Bitten Sie Ihren Kunden, seinen Browser für die Nutzung der Website zu aktualisieren. Aber wenn Sie Ihren Browser aktualisieren und plötzlich die Hälfte des Internets für Sie kaputt geht, ist das ein Fehler, ähm-nein.
Sicher, diese spezielle Änderung wird wahrscheinlich nicht viele Skripte beschädigen. Aber das ist normalerweise ein schlechtes Argument, weil ein Standard universell ist und jede Chance berücksichtigen muss. Überlegen Sie einfach
"use strict";
als Anweisung, in den strengen Modus zu wechseln. Es zeigt, wie viel Aufwand ein Standard in den Versuch steckt, alles kompatibel zu machen, weil sie den strengen Modus zum Standard (und sogar nur zum Modus) hätten machen können. Mit dieser cleveren Anweisung können Sie jedoch zulassen, dass alter Code unverändert ausgeführt wird, und trotzdem den neuen, strengeren Modus nutzen.
Ein weiteres Beispiel für die Abwärtskompatibilität: der
===
Operator.==
ist grundlegend fehlerhaft (obwohl einige Leute anderer Meinung sind) und es hätte einfach seine Bedeutung ändern können. Stattdessen===
wurde eingeführt, so dass alter Code weiterhin ausgeführt werden kann, ohne zu brechen. Gleichzeitig können neue Programme mit einer strengeren Prüfung geschrieben werden.Und damit ein Standard die Kompatibilität bricht, muss es einen sehr guten Grund geben. Was uns dazu bringt
Es gibt einfach keinen guten Grund
Ja, es nervt dich. Das ist verständlich. Aber letztlich ist es nichts , das nicht gelöst werden kann , sehr leicht . Verwenden Sie
||
, schreiben Sie eine Funktion - was auch immer. Sie können es fast kostenlos zum Laufen bringen. Was ist also wirklich der Vorteil, wenn Sie all die Zeit und Mühe in die Analyse dieser Veränderung investieren, von der wir wissen, dass sie ohnehin bricht? Ich verstehe den Punkt einfach nicht.Javascript hat mehrere Schwachstellen in seinem Design. Und es ist zunehmend ein größeres Problem geworden, da die Sprache immer wichtiger und mächtiger wurde. Obwohl es sehr gute Gründe gibt, viele seiner Designs zu ändern, sind andere Dinge nicht dazu gedacht, geändert zu werden .
Haftungsausschluss: Diese Antwort basiert teilweise auf Meinungen.
quelle
Um null und '' hinzuzufügen, müssen sie ein gemeinsames Mindesttypkriterium erfüllen, das in diesem Fall ein Zeichenfolgentyp ist.
null wird aus diesem Grund in "null" konvertiert und da es sich um eine Zeichenfolge handelt, werden die beiden verkettet.
Das gleiche passiert mit Zahlen:
4 + '' = '4'
Da sich dort eine Zeichenfolge befindet, die nicht in eine beliebige Zahl konvertiert werden kann, wird die 4 stattdessen in eine Zeichenfolge konvertiert.
quelle
Warum nicht filtern, um die Richtigkeit zu überprüfen und dann beitreten?
const concatObject = (obj, separator) => Object.values(obj) .filter((val) => val) .join(separator); let myAddress = { street1: '123 Happy St', street2: undefined, city: null, state: 'DC', zip: '20003' } concatObject(myAddress, ', ')
> "123 Happy St, DC, 20003"
quelle