Function arguments destructuring is an amazing feature in ES6.
Assume we want a function
named f
to accept an Object
which has an a
key
function f({ a }) {
return a;
}
We have default values for the case when parameter wasn't provided to a function in order to avoid Type Error
function f({ a } = {}) {
return a;
}
This will help in the following case
const a = f(); // undefined
Though, it will fail on
const a = f(null); // TypeError: Cannot match against 'undefined' or 'null'.
You can see how Babel transpiles the function to ES5 here.
It can be avoided by arguments validation and preprocessing. In Python
I could use decorator, but in JS we don't have them standardized, so it's not a good idea to use them yet.
Though, assume we have a decorator checkInputObject
, which makes needed checks and provides default values using given items list (or tree for the case of nested destructuring).
We could use it in the following way without @
notation
const f = checkInputObject(['a'])(({ a }) => a);
It could look like this with @
notation
@checkInputObject(['a'])
function f({ a }) {
return a;
}
Also I can make all needed actions in the function itself and only then use destructuring, but in this case I lose all advantages of function arguments destructuring (I'll not use it at all)
function f(param) {
if (!param) {
return;
}
const { a } = param;
return a;
}
I can even implement some common function like checkInputObject
in order to use it inside of the function
const fChecker = checkInputObject(['a']);
function f(param) {
const { a } = fChecker(param);
return a;
}
Though, using of additional code doesn't look elegant to me. I'd like to have not existent entities be decomposed to undefined
.
Assume we have
function f({a: [, c]) {
return c;
}
It would be nice to get undefined
in the case of f()
.
Do you know any elegant and convenient way to make [nested] destructuring resistant to nonexistent nested keys?
My concern is following: seems like this feature is unsafe for using in public methods and I need to make a validation by myself before using it. That's why it seems useless until used in private methods.
Destructuring means to break down a complex structure into simpler parts. With the syntax of destructuring, you can extract smaller fragments from objects and arrays. It can be used for assignments and declaration of a variable.
To destroy the structure of something. To dismantle.
Destructuring in Arrays. To destructure an array in JavaScript, we use the square brackets [] to store the variable name which will be assigned to the name of the array storing the element. const [var1, var2, ...]
Destructuring is a JavaScript expression that allows us to extract data from arrays, objects, and maps and set them into new, distinct variables. Destructuring allows us to extract multiple properties, or items, from an array at a time.
You could use try...catch
and use that in a generic decorator function:
function safe(f) {
return function (...args) {
try {
return f(...args);
} catch (e) {}; // return undefined
};
}
function f({ a } = {}) {
return a;
}
f = safe(f);
console.log(f(null)); // -> undefined
console.log(f( { a:3 } )); // -> 3
The proper way to handle this in ES6 is to respect the way params are being processed by the language and shape API to fit it better, not in the opposite way.
The semantics behind destructured argument in fn(undefined)
is that the argument will be replaced with default value, in the case of fn(null)
means that nully argument won't be replaced with default value.
If data comes from the outside and should be conditioned/preprocessed/validated, this should be handled explicitly, not by destructuring:
function fn({ foo, bar}) { ... }
fn(processData(data));
or
function fn(data) {
const { foo, bar } = processData(data);
...
}
fn(data);
The place where processData
is supposed to be called is totally on developer's discretion.
Since proposed ECMAScript decorators are just helper functions with specific signature, same helper function can be used both with @
syntax and usual function calls, depending on the project.
function processDataDecorator(target, key) {
const origFn = target[key];
return target[key] = (...args) => origFn.apply(target, processData(...args));
}
class Foo {
constructor() {
this.method = processDataDecorator(this, 'method');
}
method(data) {...}
}
class Bar {
@processDataDecorator
method(data) {...}
}
And the way that the language offers since ES5 for default property values is Object.assign
. It doesn't matters if data
is undefined
, null
or other primitive, the result will always be an object:
function processData(data) {
return Object.assign({ foo: ..., bar: ...}, data);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With