Legen Sie den Standardwert für Javascript-Objektattribute fest

82

Gibt es eine Möglichkeit, das Standardattribut eines Javascript-Objekts so festzulegen, dass:

var emptyObj = {};
// do some magic
emptyObj.nonExistingAttribute // => defaultValue

IE kann ignoriert werden, Chrome Frame hat mich von diesen Kopfschmerzen befreit.

Sandstrom
quelle
JEDES nicht existierende Attribut oder nur ein BEKANNTER Attributname?
js1568

Antworten:

117

Seit ich die Frage vor einigen Jahren gestellt habe, sind die Dinge gut vorangekommen.

Proxies sind Teil von ES6. Das folgende Beispiel funktioniert in Chrome, Firefox, Safari und Edge :

var handler = {
  get: function(target, name) {
    return target.hasOwnProperty(name) ? target[name] : 42;
  }
};

var p = new Proxy({}, handler);

p.answerToTheUltimateQuestionOfLife; //=> 42

Lesen Sie mehr in Mozillas Dokumentation zu Proxies .

Sandstrom
quelle
Wenn Sie planen, Internet Explorer (vor Edge) zu unterstützen, haben Sie kein Glück: caniuse.com/#search=proxy Außerdem unterstützt polyfill github.com/tvcutsem/harmony-reflect IE
Tiagojdferreira
21
Herzlichen Glückwunsch, Sie haben Phoenix verdient ( Goldabzeichen : Beantwortete eine Ihrer eigenen Fragen mindestens ein Jahr später mit einer Antwort, die die Anzahl der Stimmen der beliebtesten Antwort mindestens verdoppelte)
Ziggy
Object.withDefault = (defaultValue, o = {}) => neuer Proxy (o, {get: (o, k) => (k in o)? O [k]: defaultValue}); o = Object.withDefault (42); ox // => 42 ox = 10 ox // => 10 o.xx // => 42
Vlad V
Die Verwendung von Proxy bedeutet Object.getEntriesalso auch, dass ein Proxy nicht aufgerufen werden kann :(
Meredith
1
Wenn Sie meinen Object.entries, können Sie den Handler ändern, um Eigenschaften festzulegen, auf die zugegriffen wird. Wechseln Sie return target.hasOwnProperty(name) ? target[name] : 42;zu if (!target.hasOwnProperty(name)) target[name] = 42; return target[name];.
Dosentmatter
30

Verwenden Destrukturierung (neu in ES6)

Es gibt eine großartige Dokumentation von Mozila sowie einen fantastischen Blog-Beitrag , der die Syntax besser erklärt als ich.

Zur Beantwortung Ihrer Frage

var emptyObj = {};
const { nonExistingAttribute = defaultValue } = emptyObj;
console.log(nonExistingAttribute); // defaultValue

Weitergehen

Kann ich diese Variable umbenennen ? Sicher!

const { nonExistingAttribute: coolerName = 15} = emptyObj;
console.log(coolerName); // 15

Was ist mit verschachtelten Daten? Her damit!

var nestedData = {
    name: 'Awesome Programmer',
    languages: [
        {
            name: 'javascript',
            proficiency: 4,
        }
    ],
    country: 'Canada',
};

var {name: realName, languages: [{name: languageName}]} = nestedData ;

console.log(realName); // Awesome Programmer
console.log(languageName); // javascript
alexdriedger
quelle
28

Es gibt keine Möglichkeit, dies in Javascript festzulegen. Die Rückgabe undefinedfür nicht vorhandene Eigenschaften ist Teil der Javascript-Kernspezifikation. Siehe die Diskussion für diese ähnliche Frage . Wie ich dort vorgeschlagen habe, besteht ein Ansatz (obwohl ich ihn nicht wirklich empfehlen kann) darin, eine globale getPropertyFunktion zu definieren :

function getProperty(o, prop) {
    if (o[prop] !== undefined) return o[prop];
    else return "my default";
}

var o = {
    foo: 1
};

getProperty(o, 'foo'); // 1
getProperty(o, 'bar'); // "my default"

Dies würde jedoch zu einer Reihe von nicht standardisiertem Code führen, der für andere schwer zu lesen wäre, und es könnte unbeabsichtigte Konsequenzen in Bereichen haben, in denen Sie einen undefinierten Wert erwarten oder wollen. Überprüfen Sie besser einfach, während Sie gehen:

var someVar = o.someVar || "my default";
nrabinowitz
quelle
10
Warnung: var someVar = o.someVar || "my default";Wird möglicherweise unerwartete Ergebnisse haben, wenn o.someVar ausgefüllt ist, aber als falsch ausgewertet wird (z. B. null, 0, ""). someVar = o.someVar === undefined ? o.someVar : "my default";wäre besser. Ich verwende normalerweise ||alleine, wenn der Standardwert auch false. (e.g. o.someVar || ergibt 0`)
Shanimal
5
Das ist ein guter Punkt - dies funktioniert nirgendwo, wo ein false-y-Wert eine gültige Eingabe ist, und Sie müssen dies berücksichtigen, wenn Sie dieses Muster verwenden. In den meisten Fällen ist es jedoch sinnvoll, eine Eigenschaft explizit nulloder falseauf die gleiche Weise wie eine nicht festgelegte Eigenschaft zu behandeln. In diesem Fall ist dies eine doppelte Pflicht. Die Warnung ist jedoch fair.
Nrabinowitz
5
@ Shimalimal Ihr Beispiel ist falsch herum, es sollte someVar = o.someVar === undefined ? "my default" : o.someVar;nur ein kleines Problem sein, aber es hat mich ein wenig
geworfen,
ja @Metalskin wir würden uns nicht für den undefinierten Wert interessieren, oder? lol. Entschuldigung, hoffentlich hat der Fehler Sie nicht zu viel Zeit gekostet :)
Shanimal
11

Dies klingt sicher nach der typischen Verwendung von Objekten auf Prototyp-Basis:

// define a new type of object
var foo = function() {};  

// define a default attribute and value that all objects of this type will have
foo.prototype.attribute1 = "defaultValue1";  

// create a new object of my type
var emptyObj = new foo();
console.log(emptyObj.attribute1);       // outputs defaultValue1
jfriend00
quelle
Wenn Sie console.log (emptyObj) aufrufen, wird {} zurückgegeben. nicht {attribute1: 'defaultValue1'}
throrin19
2
Ja, da attribute1: defaultValue1es sich um den Prototyp handelt und console.log nur Elemente auflistet, die für das Objekt der obersten Ebene festgelegt wurden, nicht für den Prototyp. Aber der Wert ist da, wie meine console.log(emptyObj.attribute1)Shows zeigen.
jfriend00
es ist richtig, aber es ist das gleiche Problem mit JSON.stringify(emptyobj). Ich war gezwungen, eine Methode zu erstellen, die alle Attribute als Antwort auf dieses Problem
zurückgibt
11

Mein Code ist:

function(s){
    s = {
        top: s.top || 100,    // default value or s.top
        left: s.left || 300,  // default value or s.left
    }
    alert(s.top)
}
Kiyan
quelle
1
Diese Lösung gefällt mir am besten, weil sie meiner Arbeit in PHP ähnelt. function foo( array $kwargs = array() ) { // Fill in defaults for optional keyworded arguments. $kwargs += array( 'show_user' => true, 'show_links' => false, ); ...
Mike Finch
10

Ich erreiche dies mit der object.assignFunktion

const defaultProperties = { 'foo': 'bar', 'bar': 'foo' };
const overwriteProperties = { 'foo': 'foo' };
const newObj = Object.assign({}, defaultProperties, overwriteProperties);
console.log(defaultProperties);  // {"foo": "bar", "bar": "foo"}
console.log(overwriteProperties);  // { "foo": "foo" };
console.log(newObj);  // { "foo": "foo", "bar": "foo" }
Colin
quelle
5
Wenn Sie dies tun, überschreiben Sie auch die Werte des overwritePropertiesrichtigen Weges:const newObj = Object.assign({}, defaultProperties, overwriteProperties)
Francisco Puga
6

Ich denke, der einfachste Ansatz ist die Verwendung Object.assign.

Wenn Sie diese Klasse haben:

class MyHelper {
    constructor(options) {
        this.options = Object.assign({
            name: "John",
            surname: "Doe",
            birthDate: "1980-08-08"
        }, options);
    }
}

Sie können es so verwenden:

let helper = new MyHelper({ name: "Mark" });
console.log(helper.options.surname); // this will output "Doe"

Dokumentation (mit Polyfill): https://developer.mozilla.org/it/docs/Web/JavaScript/Reference/Global_Objects/Object/assign

Gianlucaparadise
quelle
6

Einfachste aller Lösungen:

dict = {'first': 1,
        'second': 2,
        'third': 3}

Jetzt,

dict['last'] || 'Excluded'

gibt 'Excluded' zurück, was der Standardwert ist.

Gil Baggio
quelle
3
Dies schlägt fehl, wenn Sie ein etwas anderes Diktat haben:dict = {'first': 0, 'second': 1, 'third': 2}
Vlad V
2
Dies ist auch eine großartige Möglichkeit, Werte zu verketten, die möglicherweise nicht vorhanden sind. Zum Beispiel a.b.c.d. Wenn a, d oder c undefiniert sind, haben Sie einen Fehler gefunden, aber Sie können es einfach tun(((a || {}).b || {}).c || {}).d) || "default"
hobberwickey
1
Mit Abstand beste Lösung!
Arthur Harduim
5

Oder Sie können dies versuchen

dict = {
 'somekey': 'somevalue'
};

val = dict['anotherkey'] || 'anotherval';
user1386350
quelle
15
Schlechte Idee, wenn dikt ['anotherkey'] 0 ist.
Ray Toal
Vielleicht sollten Sie dies berücksichtigen: codereadability.com/…
ebragaparah
@ RayToal Dann würde das nicht behoben String()? Wie in:val = dict[String('anotherkey')] || 'anotherval';
Godstime Osarobo
Nein, tut mir leid, das würde es nicht. Gleiches Problem. Versuchen Sie es: Zuerst tun Sie es d = {x: 1, y: 0}dann d['y'] || 100und nicht es gibt fälschlicherweise 100. Dann versuchen Sie Ihre Idee, die d[String('y')] || 100- es gibt immer noch unkorrekt 100, wenn es 0 geben sollte.
Ray Toal
4

Dies scheint mir die einfachste und lesbarste Art zu sein:

let options = {name:"James"}
const default_options = {name:"John", surname:"Doe"}

options = Object.assign({}, default_options, options)

Object.assign () Referenz

Joaquin Keller
quelle
Ich wollte antworten, dass die Unterstrichbibliothek die Methode _.defaults (object, * defaults) dafür hat, aber Ihre Antwort wird ohne Bibliothek gleich!
DaniCE
2

Ich habe gestern einen Artikel gesehen, in dem eine Object.__noSuchMethod__Eigenschaft erwähnt wird: JavascriptTips Ich hatte keine Gelegenheit, damit herumzuspielen, daher weiß ich nichts über die Browserunterstützung, aber vielleicht könnten Sie das auf irgendeine Weise nutzen?

James Long
quelle
Hab diese Seite auch gesehen, müssen wir den gleichen HN-Artikel gelesen haben. Nur Methoden. Es könnte mit defineGetter gemacht werden, aber das ist leider nicht in ECMA5. Sie haben keinen anderen Getter / Setter-Ansatz, der meiner Ansicht nach schlechter ist (erfordert die vorherige Definition von Eigenschaften).
Sandstrom
Dies könnte eine Antwort in der Zukunft sein :) Hoffen wir, dass die Browser bald dort sein werden :) ♬
jave.web
1
Ich glaube, die zukünftige Antwort wird sein, einen Proxy zu verwenden
James Long
@ JamesLong ja, du bist richtig! Es stellt sich heraus, dass die Zukunft gekommen ist :) 3 Jahre nachdem ich die Frage gestellt habe, funktioniert dies jetzt in FF (und bald in anderen Browsern). Ich habe unten eine Antwort hinzugefügt.
Sandstrom
2

Ich bin überrascht, dass noch niemand den ternären Operator erwähnt hat.

var emptyObj = {a:'123', b:'234', c:0};
var defaultValue = 'defaultValue';
var attr = 'someNonExistAttribute';
emptyObj.hasOwnProperty(attr) ? emptyObj[attr] : defaultValue;//=> 'defaultValue'


attr = 'c'; // => 'c'
emptyObj.hasOwnProperty(attr) ? emptyObj[attr] : defaultValue; // => 0

Auf diese Weise erhält der Wert von 'c' auch dann den richtigen Wert, wenn er 0 ist.

Alan Dong
quelle
1
Object.withDefault = (defaultValue,o={}) => {
  return new Proxy(o, {
    get: (o, k) => (k in o) ? o[k] : defaultValue 
  });
}

o = Object.withDefault(42);
o.x  //=> 42

o.x = 10
o.x  //=> 10
o.xx //=> 42
Vlad V.
quelle
Also ... Proxies zu verwenden bedeutet auch, dass Sie jetzt alle netten Objektmethoden verlieren :(
Meredith
0

Dies ist tatsächlich möglich Object.create. Es funktioniert nicht für "nicht definierte" Eigenschaften. Aber für diejenigen, denen ein Standardwert zugewiesen wurde.

var defaults = {
    a: 'test1',
    b: 'test2'
};

Wenn Sie dann Ihr Eigenschaftenobjekt erstellen, tun Sie dies mit Object.create

properties = Object.create(defaults);

Jetzt haben Sie zwei Objekte, bei denen das erste Objekt leer ist, der Prototyp jedoch auf das defaultsObjekt zeigt. Zu testen:

console.log('Unchanged', properties);
properties.a = 'updated';
console.log('Updated', properties);
console.log('Defaults', Object.getPrototypeOf(properties));
Nils
quelle
0

Ein Ansatz wäre, ein Standardobjekt zu nehmen und es mit dem Zielobjekt zusammenzuführen. Das Zielobjekt würde Werte im Standardobjekt überschreiben.

jQuery hat die .extend()Methode, die dies tut. jQuery wird jedoch nicht benötigt, da es Vanilla JS-Implementierungen gibt, wie sie hier zu finden sind:

http://gomakethings.com/vanilla-javascript-version-of-jquery-extend/

Doug Amos
quelle
-1

Ich bin hierher gekommen, um nach einer Lösung zu suchen, weil der Header mit meiner Problembeschreibung übereinstimmt, aber nicht das ist, wonach ich gesucht habe, aber ich habe eine Lösung für mein Problem gefunden (ich wollte einen Standardwert für ein Attribut haben, das so etwas wie Datum dynamisch ist ).

let Blog = {
title  : String,
image  : String,
body   : String,
created: {type: Date, default: Date.now}
}

Der obige Code war die Lösung, für die ich mich endgültig entschieden habe.

Jibin Philipose
quelle