Nullsicherer Eigenschaftszugriff (und bedingte Zuweisung) in ES6 / 2015

149

Gibt es nullin ES6 (ES2015 / JavaScript.next / Harmony) einen Operator für den Zugriff auf sichere Eigenschaften (Null-Weitergabe / Existenz), wie beispielsweise ?.in CoffeeScript ? Oder ist es für ES7 geplant?

var aThing = getSomething()
...
aThing = possiblyNull?.thing

Dies wird ungefähr so ​​sein:

if (possiblyNull != null) aThing = possiblyNull.thing

Idealerweise sollte die Lösung nicht (gerade undefined) aThingif zugeordnet possiblyNullwerdennull

ᆼ ᆺ ᆼ
quelle
3
@naomik Diese Art der Nullprüfung kann sehr nützlich sein, wenn Anweisungen, bei denen Sie nach einer tief verschachtelten Eigenschaft suchen, z. B. if( obj?.nested?.property?.value )anstelle vonif( obj && obj.nested && obj.nested.property && obj.nested.property.value )
Sean Walsh
@SeanWalsh Wenn Ihre Objekte so tief verschachtelt sind oder wenn Ihre Funktionen so tief in Ihre Objekte graben, gibt es wahrscheinlich auch einige andere Probleme mit Ihrer App.
Vielen Dank,
1
vergleiche var appConfig = loadConfig(config, process.env); connect(appConfig.database);mit connect(config). Sie können ein viel einfacheres Objekt zu übergeben , connectanstatt vorbei das ganze configObjekt, die Sie verwenden können conf.username, conf.passwordstatt wie etwas versucht config[process.env]?.database?.username, config[process.env]?.database?.password. Referenz: Gesetz von Demeter .
Vielen Dank,
Wenn Sie beispielsweise Standardeinstellungen festlegen oder Eigenschaften bereinigen (dies kann im loadConfigobigen Beispiel erfolgen), können Sie Annahmen über das Vorhandensein von Eigenschaften treffen und die Nullprüfung in unzähligen Bereichen Ihrer App überspringen.
Vielen Dank,
4
@naomik Solange die Sprache das Verschachteln von Objekten unterstützt, ist sie immer noch eine nützliche Funktion - unabhängig davon, was Sie oder ich von der Architektur der App selbst halten. Abgesehen davon sind solche komplexen Objektgraphen in ORMs, die ein komplexes Datenmodell modellieren, sehr häufig.
Sean Walsh

Antworten:

97

Update (2020-01-31): Es scheint, dass die Leute dies immer noch finden. Hier ist die aktuelle Geschichte:

Update (01.08.2017): Wenn Sie ein offizielles Plugin verwenden möchten, können Sie den Alpha-Build von Babel 7 mit der neuen Transformation ausprobieren. Ihr Kilometerstand kann variieren

https://www.npmjs.com/package/babel-plugin-transform-optional-chaining

Original :

Eine Funktion, die dies erreicht, befindet sich derzeit in Stufe 1: Optionale Verkettung.

https://github.com/tc39/proposal-optional-chaining

Wenn Sie es heute verwenden möchten, gibt es ein Babel-Plugin, das dies ermöglicht.

https://github.com/davidyaha/ecmascript-optionals-proposal

Grundtage
quelle
Verstehe ich richtig, dass dies keine bedingte Zuweisung beinhaltet? street = user.address?.streetwürde auf streetjeden fall setzen?
27 ᆺ ᆼ
1
Eigentlich denke ich leider, dass du recht hast. Street Ich denke würde zugewiesen werden undefined. Aber zumindest würde es nicht versuchen, auf undefinierte Eigenschaften zuzugreifen.
Grundtage
2
Es sieht auch so aus, als könnten Sie den Operator auf der linken Seite, und wenn es etwas zu undefiniert bewertet, wird die rechte Hand nicht ausgewertet. Das Babel-Plugin kann etwas variieren, habe es selbst noch nicht viel getestet.
Grundtage
1
In Bezug auf die bedingte Zuweisung auf der linken Seite des =wird dies in der offiziellen Spezifikation derzeit nicht unterstützt. github.com/tc39/proposal-optional-chaining#not-supported
basicdays
1
Ab Mai 2020 scheinen aktuelle Browser und Typescript dies implementiert zu haben!
Josh Diehl
65

Es ist nicht so schön wie das? Operator, aber um ein ähnliches Ergebnis zu erzielen, können Sie Folgendes tun:

user && user.address && user.address.postcode

Da nullund undefinedbeide falsche Werte sind ( siehe diese Referenz ), wird auf die Eigenschaft nach dem &&Operator nur zugegriffen, wenn der Präzedenzfall nicht null oder undefiniert ist.

Alternativ können Sie eine Funktion wie die folgende schreiben:

function _try(func, fallbackValue) {
    try {
        var value = func();
        return (value === null || value === undefined) ? fallbackValue : value;
    } catch (e) {
        return fallbackValue;
    }
}

Verwendung:

_try(() => user.address.postcode) // return postcode or undefined 

Oder mit einem Fallback-Wert:

_try(() => user.address.postcode, "none") // return postcode or a custom string
tocqueville
quelle
Richtig, wahrscheinlich sollte ich die Frage klären, die Hauptsache, nach der ich
gesucht habe,
nur eine Folge davon; !(user && user.address && user.address.postcode)
Um
foo && foo.bar && foo.bar.quux ...In einer großen Codebasis ist dies hässlich und fügt eine Menge Komplexität hinzu, die Sie besser vermeiden sollten.
Skylar Saveland
1
Dies ist die sauberste Lösung, die ich im Internet gefunden habe. Ich benutze dies mit Typoskript:_get<T>(func: () => T, fallbackValue?: T) :T
Tomwassing
3
Wenn user.address.postcodeundefiniert ist, _try(() => user.address.postcode, "")wird undefinedstatt zurückgegeben "". Der Code _try(() => user.address.postcode, "").lengthlöst also eine Ausnahme aus.
Alexander Chen
33

Nein. Sie können in JavaScript lodash # get oder ähnliches verwenden.

Girafa
quelle
4
Gibt es Vorschläge, es zu ES7 hinzuzufügen?
21 ᆺ ᆼ
1
Ich habe keine gefunden.
Girafa
3
@ PeterVarga: Viele. Zu viel Diskussion. Grammatik ist keine einfache Sache für diese Funktion
Bergi
1
lodash.getist insofern etwas anders, als es offensichtlich keine bedingte Zuweisung durchführen kann.
21 ᆺ ᆼ
17

Vanille-Alternative für sicheren Zugang zu Eigentum

(((a.b || {}).c || {}).d || {}).e

Die prägnanteste bedingte Zuordnung wäre wahrscheinlich diese

try { b = a.b.c.d.e } catch(e) {}
Yagger
quelle
Wie würden Sie die Aufgabe in der Frage mit diesem Mechanismus schreiben? Benötigen Sie nicht noch eine Bedingung, um nicht zuzuweisen, falls die possiblyNullnicht definiert ist / null?
15 ᆺ ᆼ
Natürlich müssen Sie noch prüfen, ob eine Aufgabe ausgeführt werden soll oder nicht. Wenn Sie den Operator '=' verwenden, wird zwangsläufig etwas zugewiesen, sei es Daten, undefiniert oder null. Das Obige ist also nur ein sicherer Zugang zu Eigentum. Gibt es überhaupt einen Operator für bedingte Zuweisungen in einer beliebigen Sprache?
Yagger
Natürlich hat es zum Beispiel CoffeeScript auch||=
ᆼ ᆺ ᆼ
+1 Schon mein erster Gedanke war (((a.b || {}).c || {}).d || {}).e. Aber genauer wäre es((((a || {}).b || {}).c || {}).d || {}).e
IsmailS
6

Nein, in ES6 gibt es keinen Null-Ausbreitungsoperator. Sie müssen mit einem der bekannten Muster gehen .

Möglicherweise können Sie jedoch die Destrukturierung verwenden:

({thing: aThing} = possiblyNull);

Es gibt viele Diskussionen (z. B. diese ), um einen solchen Operator in ES7 hinzuzufügen, aber keine hat sich wirklich durchgesetzt.

Bergi
quelle
Das sah vielversprechend aus, aber zumindest unterscheidet sich das, was Babel damit macht, nicht von nuraThing = possiblyNull.thing
21 ᆺ ᆼ
1
@PeterVarga: Ups, Sie haben Recht, die Destrukturierung funktioniert, wenn die Eigenschaft nicht vorhanden ist, aber nicht, wenn das Objekt vorhanden ist null. Sie müssten einen Standardwert angeben, der diesem Muster sehr ähnlich ist , jedoch eine verwirrendere Syntax aufweist.
Bergi
4

Anhand der Liste hier gibt es derzeit keinen Vorschlag, Ecmascript um eine sichere Durchquerung zu erweitern. Es gibt also nicht nur keinen guten Weg, dies zu tun, sondern es wird in absehbarer Zeit auch nicht hinzugefügt.

Antimon
quelle
1
// Typescript
static nullsafe<T, R>(instance: T, func: (T) => R): R {
    return func(instance)
}

// Javascript
function nullsafe(instance, func) {
    return func(instance);
};

// use like this
const instance = getSomething();
let thing = nullsafe(instance, t => t.thing0.thing1.thingx);
Gary Fu
quelle
1

Optionale Verkettung ?.und Nullish Coalescing??

Sie können jetzt direkt ?.Inline verwenden, um die Existenz sicher zu testen. Alle modernen Browser unterstützen dies.

?? kann verwendet werden, um einen Standardwert festzulegen, wenn undefiniert oder null.

aThing = possiblyNull ?? aThing
aThing = a?.b?.c ?? possiblyNullFallback ?? aThing

Wenn eine Eigenschaft vorhanden ist, fahren Sie ?.mit der nächsten Prüfung fort oder geben Sie den gültigen Wert zurück. Jeder Fehler wird sofort kurzgeschlossen und kehrt zurück undefined.

const example = {a: ["first", {b:3}, false]}

example?.a  // ["first", {b:3}, false]
example?.b  // undefined

example?.a?.[0]     // "first"
example?.a?.[1]?.a  // undefined
example?.a?.[1]?.b  // 3

domElement?.parentElement?.children?.[3]?.nextElementSibling

null?.()                // undefined
validFunction?.()       // result
(() => {return 1})?.()  // 1

Um einen standardmäßig definierten Wert sicherzustellen, können Sie verwenden ??. Wenn Sie den ersten Wahrheitswert benötigen, können Sie verwenden ||.

example?.c ?? "c"  // "c"
example?.c || "c"  // "c"

example?.a?.[2] ?? 2  // false
example?.a?.[2] || 2  // 2

Wenn Sie einen Fall nicht überprüfen, muss die Eigenschaft auf der linken Seite vorhanden sein. Wenn nicht, wird eine Ausnahme ausgelöst.

example?.First         // undefined
example?.First.Second  // Uncaught TypeError: Cannot read property 'Second' of undefined

?.Browser-Unterstützung - 78%, Juli 2020

??Browser-Unterstützung - 78%

Mozilla-Dokumentation

- -

Logische Nullzuweisung, 2020+ Lösung

Derzeit werden den Browsern neue Operatoren hinzugefügt ??=, ||=und &&=. Sie tun nicht ganz das, wonach Sie suchen, können jedoch je nach Ziel Ihres Codes zu demselben Ergebnis führen.

HINWEIS: Diese sind in öffentlichen Browserversionen noch nicht üblich , aber Babel sollte sich gut transpilieren lassen. Wird aktualisiert, wenn sich die Verfügbarkeit ändert.

??=prüft, ob die linke Seite undefiniert oder null ist, kurzgeschlossen, falls bereits definiert. Wenn nicht, wird der linken Seite der Wert für die rechte Seite zugewiesen. ||=und &&=sind ähnlich, basieren jedoch auf den Operatoren ||und &&.

Grundlegende Beispiele

let a          // undefined
let b = null
let c = false

a ??= true  // true
b ??= true  // true
c ??= true  // false

Objekt- / Array-Beispiele

let x = ["foo"]
let y = { foo: "fizz" }

x[0] ??= "bar"  // "foo"
x[1] ??= "bar"  // "bar"

y.foo ??= "buzz"  // "fizz"
y.bar ??= "buzz"  // "buzz"

x  // Array [ "foo", "bar" ]
y  // Object { foo: "fizz", bar: "buzz" }

Browser-Unterstützung Juli 2020 - 0,03%

Mozilla-Dokumentation

Gibolt
quelle
1
Die Frage
betrifft
Es wurden einige Beispiele mit ?.und ??und eine detaillierte bevorstehende Lösung hinzugefügt , die für Ihre Situation geeignet sein könnte. Die beste aktuelle Lösung ist wahrscheinlich, es einfach zu tunaThing = possiblyNull?.thing ?? aThing
Gibolt
0

Eine sichere Deep-Get-Methode scheint eine natürliche Passform für underscore.js zu sein, aber dort besteht das Problem darin, die Zeichenfolgenprogrammierung zu vermeiden. Ändern der Antwort von @ Felipe, um die Programmierung von Zeichenfolgen zu vermeiden (oder zumindest Randfälle an den Aufrufer zurückzuschieben):

function safeGet(obj, props) {
   return (props.length==1) ? obj[keys[0]] :safeGet(obj[props[0]], props.slice(1))
}

Beispiel:

var test = { 
  a: { 
    b: 'b property value',
    c: { }
  } 
}
safeGet(test, ['a', 'b']) 
safeGet(test, "a.b".split('.'))  
Prototyp
quelle
Um Rick zu paraphrasieren: Das klingt nur nach String-Programmierung mit zusätzlichen Schritten.
Tocqueville
1
lodash implementiert jetzt Deep Get / Set _.get(obj, array_or_dotstring)
Prototyp
1
Aber um fair zu sein, ist selbst die Javascript-Punkt- und String-Accessor-Notation im Grunde eine String-Programmierung, obj.a.b.cvsobj['a']['b']['c']
Prototyp
1
Woher kommt keysdas?
Levi Roberts
-2

Ich weiß, dass dies eine JavaScript-Frage ist, aber ich denke, Ruby behandelt dies auf alle angeforderten Arten, daher denke ich, dass dies ein relevanter Bezugspunkt ist.

.&, tryUnd && ihre Stärken und mögliche Gefahren haben. Eine große Auswahl dieser Optionen finden Sie hier: http://mitrev.net/ruby/2015/11/13/the-operator-in-ruby/

TLDR; Die Schlussfolgerung der Rubyisten ist, dass dies digsowohl augenschonender als auch eine stärkere Garantie dafür ist, dass ein Wertnull zugewiesen wird oder zugewiesen wird.

Hier ist eine einfache Implementierung in TypeScript:

export function dig(target: any, ...keys: Array<string>): any {
  let digged = target
  for (const key of keys) {
    if (typeof digged === 'undefined') {
      return undefined // can also return null or a default value
    }
    if (typeof key === 'function') {
      digged = key(digged)
    } else {
      digged = digged[key]
    }
  }
  return digged
}

Dies kann für jede Verschachtelungstiefe verwendet werden und behandelt Funktionen.

a = dig(b, 'c', 'd', 'e');
foo = () => ({});
bar = dig(a, foo, 'b', 'c')

Der tryAnsatz ist in JS ebenso gut zu lesen, wie in den vorherigen Antworten gezeigt. Es ist auch keine Schleife erforderlich, was ein Nachteil dieser Implementierung ist.

theUtherSide
quelle
Ich habe diese Antwort hier als lustige / vollständige / alternative Lösung aufgeführt. B / c Ich mag die Art und Weise, wie Ruby dies mit syntaktischem Zucker macht. Stellen Sie dies einfach für die Community bereit - Stimmen Sie noch einmal ab und ich werde diesen Beitrag gerne löschen. Prost!
theUtherSide
Wirklich cool zu sehen, dass ECMAScript2019 einen großartigen Vorschlag für Ruby-ähnliche "Optionale Verkettung" hat: github.com/tc39/proposal-optional-chaining Es ist heute über das Babel-Plugin verfügbar und funktioniert auch zum Aufrufen von Methoden für ein Objekt: babeljs.io / docs / de / babel-plugin-
vorschlag
-5

Ich dachte, diese Frage müsste für 2018 etwas aktualisiert werden. Dies kann ohne Verwendung von Bibliotheken problemlos durchgeführt werden Object.defineProperty()und kann wie folgt verwendet werden:

myVariable.safeGet('propA.propB.propC');

Ich halte dies für sicher (und js-ethisch) aufgrund der writeableund enumerableDefinitionen, die jetzt für die definePropertyMethode von verfügbar sind Object, wie in MDN dokumentiert

Funktionsdefinition unten:

Object.defineProperty(Object.prototype, 'safeGet', { 
    enumerable: false,
    writable: false,
    value: function(p) {
        return p.split('.').reduce((acc, k) => {
            if (acc && k in acc) return acc[k];
            return undefined;
        }, this);
    }
});

Ich habe einen jsBin mit Konsolenausgabe zusammengestellt, um dies zu demonstrieren. Beachten Sie, dass ich in der jsBin-Version auch eine benutzerdefinierte Ausnahme für leere Werte hinzugefügt habe. Dies ist optional, und deshalb habe ich es aus der obigen Minimaldefinition herausgelassen.

Verbesserungen sind willkommen

Felipe
quelle
2
Damit schreiben Sie de facto Code in Strings. Das ist wirklich eine schlechte Idee. Es macht es unmöglich, Ihren Code umzugestalten und ist nicht IDE-freundlich.
Tocqueville