Destructuring in function parameters - javascript

Lets assume we have such function:
const func = (a, b, {c: {d}}) => {console.dir(c)}
How is this function should be called and what structure of 3rd parameter?
I tried a lot of variations, but always got an error: Cannot destructure propertydof 'undefined' or 'null'.
Thanks!

const func = (a, b, {c: {d}}) => {console.dir(d)}
func(null, null, {c: {d: document.location}});
This function has to be called with object that has key c, which has object with key d as value:
func(a, b, {c: {d: document.location }})
console.dir() takes any JS object as parameter.
{ c: {d}} is a syntax called object destructuring and its purpose in this context is unpacking fields from objects passed as function parameter.
{d} is shorter syntax for object with key d and value of variable d ({d: d}).
To unpack variable d from object under key c, that object have to have that key initialized! But when you further destructurize the object passed as argument, you don't have that object as a variable in the scope.
In example you have provided, you will not be able to access object c, as it has been destructurized and only object d is available. Either you have mistake in your code or you need something like Anurat Chapanond has posted.

Here is one example.
const func = (a, b, {c: {d}}) => {console.dir(c)},
c = { d: 'hello' }
func(1, 2, { c })
I define c as an object with a property d which is a string 'hello'.
when func is called, the third parameter I pass to the function is an object with a property c.
{ c } is a shorthand for { c: c }

Related

Why am I allowed to use variable as property name in javascript object creation without using ES6 bracket notation? [duplicate]

The question I have is best given by way of this jsfiddle, the code for which is below:
var a = 1, b = 'x', c = true;
var d = {a: a, b: b, c: c}; // <--- object literal
var e = [a, b, c]; // <--- array
var f = {a, b, c}; // <--- what exactly is this??
// these all give the same output:
alert(d.a + ', ' + d.b + ', ' + d.c );
alert(e[0] + ', ' + e[1] + ', ' + e[2]);
alert(f.a + ', ' + f.b + ', ' + f.c );
What sort of a data structure is f? Is it just a shorthand for d?
var f = {a, b, c};
It came with ES6 (ECMAScript 2015) and means exactly the same as:
var f = {a: a, b: b, c: c};
It is called Object Literal Property Value Shorthands (or simply property value shorthand, shorthand properties).
You can also combine shorthands with classical initialization:
var f = {a: 1, b, c};
For more information see Property definitions in Object initializer.
It is an Object Initializer Property Shorthand in ES6.
var f = {a, b, c, d:1}; // Will be equal to {a:a, b:b, c:c, d:1}
This works because the property value has the same name as the property identifier. This a new addition to the syntax of Object Initialiser (section 11.1.5) in the latest ECMAScript 6 draft Rev 13. And of course, just like the limitations set from ECMAScript 3, you can’t use a reserved word as your property name.
Such a shorthand won’t dramatically change your code, it only makes everything a little bit sweeter!
function createCar(name, brand, speed) {
return { type: 'Car', name: name, brand: brand, speed: speed };
}
// With the new shorthand form
function createSweetCar(name, brand, speed) {
return { type: 'Car', name, brand, speed }; // Yes it looks sweet.
}
Please see the compatibility table for support for these notations. In non-supporting environments, these notations will lead to syntax errors.
This shorthand notation offers object matching pretty nicely:
In ECMAScript5 what we used to do:
var tmp = getData();
var op = tmp.op;
var lhs = tmp.lhs;
var rhs = tmp.rhs;
Can be done in ECMAScript6 with a single line of code:
var { op, lhs, rhs } = getData();
var f = {a, b, c}; // <--- what exactly is this??
It defines an object in JavaScript using the new ECMAScript 2015 notation:
As per Mozilla Developer Network:
"Objects can be initialized using new Object(), Object.create(), or using the literal notation (initializer notation). An object initializer is a list of zero or more pairs of property names and associated values of an object, enclosed in curly braces ({})."
var a = "foo",
b = 42,
c = {};
// Shorthand property names (ES6)
var o = { a, b, c };
is equivalent to:
var a = "foo",
b = 42,
c = {};
var o = {
a: a,
b: b,
c: c
};

Specify only a subset of arguments in javascript function

I want to supply a function with only some of its parameters, and let the rest revert to their defaults. For example, I have a function like this:
function doSomething(a = 'apple', b, c){
//Do something with a, b and c
}
Now I want to call this function, but only supply arguments b and c (so that 'a' defaults to 'apple'). What is the best way to do this?
Normally, the defaults should be at the very end of your function (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Default_parameters):
function doSomething(b, c, a = 'apple'){
//Do something with a, b and c
}
There is no absolute best way of doing it. But, there are few ways that can simplify and bring in more meaning to the code written with default parameters.
You can define all the default parameter in the starting of the arguments and for rest of the parameters which doesn't have a default value, make use of the rest operator.
Eg -
function doSomething(a='apple',b='banana',...args){
console.log(a);
console.log(b);
console.log(args);
}
let x,y;
doSomething(x,y,"hello","hey","hi");
output -
Go through the following link to get more understanding of the rest operator.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/rest_parameters
Though a little unrelated. By default javascript, parameters are undefined if they are not passed any value or reference. You can check that the parameters are not undefined inside your code block.
A useful pattern (used widely in the JavaScript world) is to allow callers to pass an options object, if entries are missing they will fall back to default values.
It's particularly useful if you're dealing with a lot of parameters.
function doSomething(a, b, options) {
// Specify your defaults here...
let defaults = { c: 3, d: "bar" };
options = Object.assign({}, defaults, options);
let { c, d } = options;
console.log("doSomething: (a,b,c,d):", a, b, c, d);
}
doSomething(1,2, { c: 5, d: "foo" });
doSomething(1,2);
hope this will be clear if value of a param should always be same :
function doSomething(b, c, a = 'apple'){
console.log({b:b},{c:c},{a:a})
}
doSomething('hi','hello','a is not apple here--')
function doSomething1(b, c, a){
a = 'apple'
console.log({b:b},{c:c},{a:a})
}
doSomething1('hi','hello','a is apple here--')

JS: Destructure a dictionary and pass it to a function [duplicate]

In python I can pass a dict whose keys match parameters' names with the ** (double-splat) operator:
def foo(a, b):
print (a - b)
args = {'b': 7, 'a': 10}
foo(**args) # prints 3
How to do the same in ES6? This doesn't work:
function foo(a, b) {
console.log(a - b)
}
args = {b: 7, a: 10}
foo(...args)
NB: I'm looking for a solution that wouldn't involve changing the signature of foo because I want it to be used either way (with and without destructuring). So the following should work:
foo(<magic>args);
foo(123, 456);
Bonus question: why is the error message "undefined is not a function"? What exactly is undefined here?
(As answered by #Nina Scholz in the comments, this is because ... requires its argument to have Symbol.iterator, which is not defined for objects).
How to do the same in ES6?
There are no named arguments in JS, only positional ones. So the answer is: you can not.
What you can do is either emulate named arguments via object passing, as #Andy suggested.
function foo({ a, b }) {
console.log(a - b);
}
let args = { b: 7, a: 10 };
foo(args);
Or you could make args to be an array, so you can destruct it into positional arguments.
function foo(a, b) {
console.log(a - b);
}
let args = [10, 7];
foo(...args);
Okay-okay, just for the sake of the argument: it is possible to write a function that will extract parameters of foo and yield properties of args in required order.
function * yolo(args, fn) {
const names = fn.toString().match(/\(.+\)/)[0]
.slice(1, -1).split(',')
.map(x => x.trim());
while (names.length) {
yield args[names.shift()];
}
}
function foo(a, b) {
console.log(a - b);
}
const args = { b: 7, a: 10 };
foo(...yolo(args, foo));
I would not dare to use it in production though.
You need to wrap your args in curly braces, and again in the argument list for the function.
function foo({a, b}) {
console.log(a - b)
}
let args = {b: 7, a: 10}
foo({...args})

Can we set persistent default parameters which remain set until explicitly changed?

The below is a function fn where expected result is for a, b, c to defined at every call of fn, whether an object parameter is passed or not. If object is passed which sets property, property should be set only for that object.
const fn = (opts = {a:1, b:2, c:3}) => console.log(opts);
when called without parameters the result is
fn() // {a: 1, b: 2, c: 3}
when called with parameter, for example {b:7}, the expected result is
fn({b:7}) // {a: 1, b: 7, c: 3}
however, the actual result is
fn({b:7}) // {b: 7}
Was able to get expected result by defining an object outside of function and using Object.assign() within function body
const settings = {a: 1, b: 2, c: 3};
const fn = opts => {opts = Object.assign({}, settings, opts); console.log(opts)}
fn({b: 7}) // {a: 1, b: 7, c: 3}
fn(); // {a: 1, b: 2, c: 3}
/*
// does not log error; does not return expected result
const fn = (opts = Object.assign({}, settings, opts)) => console.log(opts)
*/
Can the above result be achieved solely utilizing default parameters, without defining an object to reference outside of function parameters or within function body?
Maybe I misunderstood the question, but you seem to be looking for default initialisers for each separate property. For that, you have to use destructuring:
const fn = ({a = 1, b = 2, c = 3} = {}) => console.log({a, b, c});
If you want to keep arbitrary properties, not just those that you know of up front, you might be interested in the object rest/spread properties proposal that allows you to write
const fn = ({a = 1, b = 2, c = 3, ...opts} = {}) => console.log({a, b, c, ...opts});
Can an opts variable as the single object reference be achieved solely utilizing default parameters, without defining an object to reference outside of function parameters or within function body?
No. Parameter declarations are only able to initialise variables with (parts of) the arguments, and possibly (as syntactic sugar) with default values when no or undefined argument (parts) are passed. They are not able to carry out unconditional computations and create variables inialised from the results - which is what you attempt to achieve here.
You are supposed to use the function body for that.
No
The best that can be done is either your own answer or this:
const fn = (default_parameters) => {
default_parameters = Object.assign({}, {a: 1, b: 2, c: 3},default_parameters);
console.log('These are the parameters:');
console.log(default_parameters);
}
fn();
fn({b: 7});
fn({g: 9, x: 10});
The default parameter block is only executed if the value is not set, so your own answer is the best that is on offer ie use two parameters
You can convince yourself of this by creating a code block that will fail if executed and testing that passing a parameter works (to show that the code block is not executed) and testing that not passing a parameter fails (showing that the code block is only executed when no parameter is passed).
This should demonstrate clearly that any paramter passed will prevent the default parameter from being evaluated at all.
const fn = (default_parameters = (default_parameters = Object.assign({}, {a: 1, b: 2, c: 3},default_parameters))) => {
console.log('These are the parameters:');
console.log(default_parameters);
}
fn({b: 7});
fn();
fn({g: 9, x: 10});
We can set fn as a variable which returns an arrow function expression. When called set a, b, c and rest parameters reference using spread element at new object, which is returned when the function is invoked.
const fn = ((...opts) => ({a:1,b:2,c:3, ...opts.pop()}));
let opts = fn();
console.log(opts);
opts = fn({b: 7});
console.log(opts);
opts = fn({g: 9, x: 10});
console.log(opts);
Using rest element, Object.assign(), spread element, Array.prototype.map(), setting element that is not an object as value of property reflecting index of element in array.
const fn = ((...opts) => Object.assign({a:1,b:2,c:3}, ...opts.map((prop, index) =>
prop && typeof prop === "object" && !Array.isArray(prop)
? prop
: {[index]:prop}))
);
let opts = fn([2,3], ...[44, "a", {b:7}, {g:8, z: 9}, null, void 0]);
console.log(opts);
Though code at OP uses single default parameter, until we locate or develop a procedure for using only single parameter, we can utilize setting two default parameters to achieve expected result.
The first parameter defaults to a plain object, at second default parameter we pass parameter identifier from first parameter to Object.assign() following pattern at Question.
We reference second parameter identifier of function fn to get the default parameters when called without parameters; when called with first parameter having properties set to properties of object passed at first parameter and default parameters, the former overwriting the latter at the resulting object.
const fn = (__ = {}, opts = Object.assign({}, {a: 1, b: 2, c: 3}, __)) =>
console.log(opts);
fn();
fn({b: 7});
fn({g: 9, x: 10});

ES6 Computed Property in Destructure - Destructure Whole Object

I have this variable:
var a = {b:1, c:2, d:'rawr', rawr:10};
I want to destructure these into the local scope so I do:
var {b, c, d} = a;
This works fine but I need to get rawr out. I tried this:
var {b, c, d, [a.d]} = a;
But this gives me missing : after property id so I tried:
So I then though to do a two step:
var {b, c, d} = a;
var {[d]} = a;
However this gives me SyntaxError: missing : after property id. Is there anyway to do this in ES6?
Expected result is that:
var b = 1;
var c = 2;
var d = 'rawr';
var rawr = 10;
You can destructure it like this:
var {b, c, d, [d]: q} = a;
So then whatever was in a property with a name d is assigned to q.
UPD:
What you asked in the update is not possible.*
You need to give it a variable to assign to. You cannot use "computed" variable names1, as that would go against the rules of static scope resolution for identifiers. So you should do
var {b, c, d, [a.d]: rawr} = a;
to get the value of the a.d-named property in the rawr variable.
1: Well, you can use eval or a with statement in sloppy mode to do this, but both are despised exactly because of this.
You could use Object.keys(), Function.prototype.bind() though caveat is that value of variable is within an array; have not yet determined how to return only variable.
var a = {
b: 1,
c: 2,
d: 'rawr',
rawr: 10
};
function de(val, key) {
var [val] = window[val] = [a[val]];
}
Object.keys(a).forEach(de.bind(this));
console.log(b, c, d, rawr);
jsfiddle https://jsfiddle.net/ravkgzjb/2/

Categories

Resources