Is there a way to set the default attribute of a Javascript object such that:
let emptyObj = {};
// do some magic
emptyObj.nonExistingAttribute // => defaultValue
Is there a way to set the default attribute of a Javascript object such that:
let emptyObj = {};
// do some magic
emptyObj.nonExistingAttribute // => defaultValue
Since I asked the question several years ago things have progressed nicely.
Proxies are part of ES6. The following example works in Chrome, Firefox, Safari and Edge:
let handler = {
get: function(target, name) {
return target.hasOwnProperty(name) ? target[name] : 42;
}
};
let emptyObj = {};
let p = new Proxy(emptyObj, handler);
p.answerToTheUltimateQuestionOfLife; //=> 42
Read more in Mozilla's documentation on Proxies.
Object.getEntries
can't be called on a Proxy :( –
Wellbred Object.entries
, you can modify the handler to set properties when they are accessed. Change return target.hasOwnProperty(name) ? target[name] : 42;
to if (!target.hasOwnProperty(name)) target[name] = 42; return target[name];
. –
Blanding p.answerToTheUltimateQuestionOfLife ?? 42
. This is usefull if you have a data object with multiple properties. –
Obscene Use destructuring (new in ES6)
There is great documentation by Mozila as well as a fantastic blog post that explains the syntax better than I can.
var emptyObj = {};
const { nonExistingAttribute = defaultValue } = emptyObj;
console.log(nonExistingAttribute); // defaultValue
Can I rename this variable? Sure!
const { nonExistingAttribute: coolerName = 15} = emptyObj;
console.log(coolerName); // 15
What about nested data? Bring it on!
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
There isn't a way to set this in Javascript - returning undefined
for non-existent properties is a part of the core Javascript spec. See the discussion for this similar question. As I suggested there, one approach (though I can't really recommend it) would be to define a global getProperty
function:
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"
But this would lead to a bunch of non-standard code that would be difficult for others to read, and it might have unintended consequences in areas where you'd expect or want an undefined value. Better to just check as you go:
var someVar = o.someVar || "my default";
var someVar = o.someVar || "my default";
will have potentially unexpected results when o.someVar is populated but evaluates to false (e.g. null, 0, ""). someVar = o.someVar === undefined ? o.someVar : "my default";
would be better. I typically use ||
alone when the default also evaluates to false. (e.g.
o.someVar || 0`) –
Rasla null
or false
in the same way as an unset property, in which case this does double duty. The warning is fair, though. –
Southdown someVar = o.someVar === undefined ? "my default" : o.someVar;
, only a minor issue but it threw me for a little bit when I first tried your code ;-) –
Banket This seems to me the most simple and readable way of doing so:
let options = {name:"James"}
const default_options = {name:"John", surname:"Doe"}
options = Object.assign({}, default_options, options)
my code is:
function(s){
s = {
top: s.top || 100, // default value or s.top
left: s.left || 300, // default value or s.left
}
alert(s.top)
}
function foo( array $kwargs = array() ) { // Fill in defaults for optional keyworded arguments. $kwargs += array( 'show_user' => true, 'show_links' => false, ); ...
–
Outbalance function(s = {})
which would fix the not filling it in at all. But you could also add this on the first line of the function: s = s || {}
to fix not filling it and also fix null
or undefined
as parameters. You could also inline that, but I that's definitely not recommended: top: (s || {}).top || 100
–
Mignonne The way I achieve this is with the object.assign
function
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" }
overwriteProperties
the correct way is: const newObj = Object.assign({}, defaultProperties, overwriteProperties)
–
Integer This sure sounds like the typical use of protoype-based objects:
// 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
attribute1: defaultValue1
is on the prototype and console.log only enumerates items set on the top level object, not on the prototype. But, the value is there as my console.log(emptyObj.attribute1)
shows. –
Stemma JSON.stringify(emptyobj)
. I was forced to create a method that returns all the attributes in response to this problem –
Senile Or you can try this
dict = {
'somekey': 'somevalue'
};
val = dict['anotherkey'] || 'anotherval';
String()
fix that? As in: val = dict[String('anotherkey')] || 'anotherval';
–
Coalfish d = {x: 1, y: 0}
then d['y'] || 100
and not it erroneously gives 100. Then try your idea which is d[String('y')] || 100
-- it still improperly gives 100 when it should give 0. –
Orientalize I think the simplest approach is using Object.assign
.
If you have this Class:
class MyHelper {
constructor(options) {
this.options = Object.assign({
name: "John",
surname: "Doe",
birthDate: "1980-08-08"
}, options);
}
}
You can use it like this:
let helper = new MyHelper({ name: "Mark" });
console.log(helper.options.surname); // this will output "Doe"
Documentation (with polyfill): https://developer.mozilla.org/it/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
Simplest of all Solutions:
dict = {'first': 1,
'second': 2,
'third': 3}
Now,
dict['last'] || 'Excluded'
will return 'Excluded', which is the default value.
dict = {'first': 0, 'second': 1, 'third': 2}
–
Hesiod a.b.c.d
. If a, d, or c are undefined you hit an error, but you can just do (((a || {}).b || {}).c || {}).d) || "default"
–
Brioche dict[key] || dict.default
:-) –
Abacist If you only have an object that is a single level deep (nested object properties will not merge as expected since it directly destructures from the first level), you can use the following destructuring syntax:
const options = {
somevar: 1234,
admin: true
};
const defaults = {
test: false,
admin: false,
};
var mergedOptions = {...defaults, ...options};
Of which the output would be:
console.log(options);
// { somevar: 1234, admin: true }
console.log(mergedOptions);
// { test: false, admin: true, somevar: 1234 }
Or even formatted as a single statement (this is slightly unreadable though):
const options = {...{
// Defaults
test: false,
admin: false,
}, ...{
// Overrides
somevar: 1234,
admin: true
}};
I saw an article yesterday that mentions an Object.__noSuchMethod__
property: JavascriptTips I've not had a chance to play around with it, so I don't know about browser support, but maybe you could use that in some way?
I'm surprised nobody has mentioned ternary operator yet.
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
In this way, even if the value of 'c' is 0, it will still get the correct value.
var obj = {
a: 2,
b: 4
};
console.log(obj);
--> {a: 2, b: 4}
function applyDefaults(obj) {
obj.a ||= 10;
obj.b ||= 10;
obj.c ||= 10;
}
// do some magic
applyDefaults(obj);
console.log(obj);
--> {a: 2, b: 4, c: 10}
This works because
undefined || "1111111" --> "1111111"
"0000000" || "1111111" --> "0000000"
as null
, undefined
, NaN
, 0
, ""
(Empty String), false
itself, are all considered to be equivalent to false (falsy). Anything else is true
(truthy).
Note that this is not uniformly supported across browsers and nodejs versions (confirm for yourself).
So two troublesome cases are the empty String ""
and 0
(zero). If it is important not to override those, you might need to rewrite this as:
if (typeof obj.d == "undefined") obj.d = "default"
This will be better supported across browsers also.
Alternatively you could write this as:
obj.d ??= "default"
This is the nullish assignment which applies only to values that are null
or undefined
(nullish) - of which the empty string is not part. However, this has again a diminished cross-browser support.
See also on the official Mozilla Website - Assigning a default value to a variable.
This is actually possible to do with Object.create
. It will not work for "non defined" properties. But for the ones that has been given a default value.
var defaults = {
a: 'test1',
b: 'test2'
};
Then when you create your properties object you do it with Object.create
properties = Object.create(defaults);
Now you will have two object where the first object is empty, but the prototype points to the defaults
object. To test:
console.log('Unchanged', properties);
properties.a = 'updated';
console.log('Updated', properties);
console.log('Defaults', Object.getPrototypeOf(properties));
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
With the addition of the Logical nullish assignment operator, you can now do something like this
const obj = {}
obj.a ??= "default";
In the case where you have an empty list as the default value and want to push to it, you could do
const obj = {}
(obj.a ??= []).push("some value")
One approach would be to take a defaults object and merge it with the target object. The target object would override values in the defaults object.
jQuery has the .extend()
method that does this. jQuery is not needed however as there are vanilla JS implementations such as can be found here:
http://gomakethings.com/vanilla-javascript-version-of-jquery-extend/
If you need to construct a copy of a given object with missing fields filled by default values, you can use spread syntax:
let a = {
foo: "foo value",
bar: "bar value"
};
let result = {
foo: "default foo value",
bar: "default bar value",
bazz: "default bazz value",
... a
}
The input object should come after the defaults, so it will replace the defaults by the values of its properties if they are defined, but leave intact the default values of missing properties. So the result will be:
{
foo: "foo value",
bar: "bar value",
bazz: "default bazz value"
}
Note that the default values will only appear for missing properties. If a property exists, but has a null
or undefined
value, it will not be replaced by a default value, so null
-s and undefined
-s will appear in the result.
If using Node.js, you can use the popular object.defaults
library:
Usage
var defaults = require('object.defaults');
var obj = {a: 'c'}; defaults(obj, {a: 'bbb', d: 'c'}); console.log(obj); //=> {a: 'c', d: 'c'}
Or immutable defaulting:
var defaults = require('object.defaults/immutable'); var obj = {a: 'c'}; var defaulted = defaults(obj, {a: 'bbb', d: 'c'}); console.log(obj !== defaulted); //=> true
© 2022 - 2024 — McMap. All rights reserved.
let o = new Proxy({}, { get: (o, k) => k in o ? o[k] : 'some default value' }; console.log(o.key1)
– Ephebe