Destructure a function parameter subproperty - javascript

I've got a Function that I want to be able to call in 2 ways - and it should behave the same.
Is there any ES6 syntax that will allow me to call the function doBar below using both ways with the same result?
Consider a function like this:
const doBar = ({ foo = 'bar' }) => {
console.log(foo) // should log 'baz'
}
I'm using a framework that binds events like so:
<x-component on-some-event="doBar"></x-component>
which will essentially cause an invocation like so:
// where e = { detail: { foo: 'baz' } }
doBar(e)
.. but I'd like to be able to both call my Function explicitly as well, albeit with a proper call signature like so:
doBar({ foo: 'baz' })

You can use a default parameter. If foo is not found, it will use the value of detail.foo.
const doBar = ({ detail = {}, foo = detail.foo }) => {
console.log(foo) // should log 'baz'
}
doBar({ foo: 'baz' })
doBar({
detail: {
foo: 'baz'
}
});

You can't do this properly in the parameter declaration. Just put your destructuring assignment in the function body:
function doBar(e) {
const { foo = "bar", qux } = e.detail || e;
consoel.log(foo, qux);
}
I'd like to be able to both call my function explicitly as well
I would recommend to not do that. A function should have one signature only and not be overloaded to different types of arguments. Just be explicit about when to use what:
function doBar({ foo = "bar", qux }) {
console.log(foo);
}
function withDetail(fn) {
return e => fn(e.detail)
}
<x-component on-some-event="withDetail(doBar)"></x-component>
doBar({ foo: 'baz' })

No. Not unless you consider this to be an adequate alternative:
const thing = {
detail: {
foo: 'baz'
}
};
doBar(thing.detail);

Related

Javascript object initialized with several functions: what syntax is it?

I am reviewing some Javascript code and stumbled upon a syntax that I didn't knew. The application is a React and Redux one, though I think this is plain Javascript.
The syntax I'm concerned with is the { f1(), f2(), ... } argument of combineReducers().
This is the syntax:
combineReducers({
Reducer1,
Reducer2,
...
});
ReducerN is a function, i.e.:
const Reducer1 = (state = INITIAL_STATE, action) => {
// ...
};
I get { f1(), ... } creates an object where the function name is the key and the function itself is the value, so in a browser console I tried the following:
a = () => { console.log(1) }
b = () => { console.log(2) }
o = {a, b}
and if I print o:
{a: ƒ, b: ƒ}
a: () => { console.log(1) }
b: () => { console.log(2) }
__proto__: Object
But if I try to initialize o in a single operation:
o = { () => return 1 }
or
o = { function y() { return 1 }}
they both give a syntax error.
It's the first time I see an object created with that syntax: What kind is that? Where can I find its reference?
As said previously,
combineReducers({
Reducer1,
Reducer2,
...
});
is equivalent to this in plain ES5:
combineReducers({
Reducer1: Reducer1,
Reducer2: Reducer2,
...
});
and combineReducers is concerned only with the values of the object passed in. The first form is just a shorthand for defining properties with the same name as the value. This is the reason you cannot use anonymous functions in this form. To define function members on classes and objects, you can use the following form:
class Foo {
foo() { console.log('foo'); }
bar = () => console.log('bar')
}
const a = new Foo();
a.foo();
a.bar();
const b = {
foo() { console.log('foo'); }
bar: () => console.log('bar')
};
b.foo();
b.bar();
When transpiling to plain ES5, this will generate the following:
"use strict";
var Foo = /** #class */ (function () {
function Foo() {
this.bar = function () { return console.log('bar'); };
}
Foo.prototype.foo = function () { console.log('foo'); };
return Foo;
}());
var a = new Foo();
a.foo();
a.bar();
var b = {
foo: function () { console.log('foo'); },
bar: function () { return console.log('bar'); }
};
b.foo();
b.bar();
{ f1() } is very different than { f1 }.
The latter is a shorthand of { f1: f1 } which is an object having the key 'f1' (a string) associated to the value f1 (a function). The function is not executed.
In the first example f1() is a function call. The function f1 is executed and the value it returns is used instead. But because you didn't provide a key to associate the value with and because f1() is a value that does not have a name (it is an expression that needs to be evaluated in order to get its value), JS cannot produce an object out of it.
{ f1 } can be evaluated at the compile time and turned into { f1: f1 }.
{ f1() } cannot be evaluated at the compile time. The value of f1() is available only at the run time.
This is why { f1() } is invalid code.
If you need to call f1 and use the value it returns to create an object you can do it this way:
const x = { f1: f1() }
This is the same thing as:
const v = f1();
const x = { f1: v }

Problem with default parameter and object

I've got a problem with default parameters in Javascript.
I have a function like this:
function search(filterOptions = {
foo: 'bar',
foo2: 'bar2'
}) {
...
}
When I call search() without arguments, filterOptions is set to {foo: 'bar', foo2: 'bar'},
but when I call search({ foo: 'something' }), foo2 is undefined.
I cannot separate filterOptions into several arguments because options are independent.
How can i make foo2 take its default value anyway (and cleanly)?
(I'm on nodejs)
Thank you!
You could define the defaults within the function and use the spread syntax to combine the two objects, which will override the defaults where applicable.
function search(filterOptions) {
const defaults = { foo: 'foo', foo2: 'bar' };
filterOptions = {...defaults,...filterOptions};
console.log(filterOptions);
}
search({foo: 'something'});
You can provide default values in the parameter list:
function search({ foo = "bar", foo2 = "bar2"} = {}) {
console.log("foo is " + foo + ", foo2 is " + foo2);
}
console.log(search());
console.log(search({ foo: "broccoli" }));
console.log(search({ foo: "my foo", foo2: "my foo2" }));
The = {} at the end is to handle the case when the function is called with no parameters.
You can define the default variable with an if statement:
function search(arr) {
if(arr.bar === undefined) {
arr.bar = "bar1";
}
//do whatever
}
The use of === is to make sure that the if does not run if bar is set to "undefined" (a string).

Can you use shorthand property assignment using 'this'?

I can do this:
class Temp {
constructor() {
this.foo = 'foo'
this.bar = 'bar'
}
getObj() {
let boo = 'boo'
return {
boo
}
}
}
console.log(new Temp().getObj())
//prints { boo: 'boo' }
So how can I do this:
class Temp {
constructor() {
this.foo = 'foo'
this.bar = 'bar'
}
getObj() {
return {
this.foo
}
}
}
console.log(new Temp().getObj())
Is there special syntax or is it not supported?
I guess what you actually look for is:
return { ...this };
or if you want to omit some properties:
const { bar, ...take } = this;
return take;
No, this is not yet supported. You will have to go by {foo: this.foo}.
However, there is a stage 1 proposal for shorthand property definition improvements that would allow you to write {this.foo} as an object literal.
If 'foo' is a public property you can do something like this:
class Foo {
constructor() {
this.foo = 'foo';
}
}
const { foo } = (new Foo());
console.log(foo); // 'foo'
https://repl.it/repls/FastForcefulMice
If you want to use destructuring, you'll need to extract the property to a const, and then use shorthand property names:
class Temp {
constructor() {
this.foo = 'foo'
this.bar = 'bar'
}
getObj() {
const { foo } = this;
return {
foo
}
}
}
console.log(new Temp().getObj())

Named default ES6 parameter without destructuration

I'm trying to default options in ES7 using babel. Here is what I can do:
class Foo {
constructor({key='value', foo='bar', answer=42}) {
this.key = key;
this.foo = foo;
this.number = number;
}
}
This might work for this example, but I would like to know how can I assign for very large config objects; here is an example of what I wanna do:
class Foo {
constructor(opts = {key='value', foo='bar', answer=42}) {
this.opts = opts;
}
}
However this does not compile. I tried to do it like this:
class Foo {
constructor(opts = {key:'value', foo:'bar', answer:42}) {
this.opts = opts;
}
}
But then it replaces the whole object, like this:
let foo = new Foo({key: 'foobar'});
console.log(foo.opts);
// {key: 'foobar'} is what is displayed
// When I want {key: 'foobar', foo:'bar', answer:42}
I don't think you can do this with ES6 optional parameters (object as a parameter with optional keys), because when you call the constructor, it's a new object with a new reference. That's because it's being replaced.
But, as a suggestion, if you want to handle a large options object, one common approach is store somewhere a default options Object and merge the object with the one passed when you instantiate it.
Something like that:
class Foo {
constructor(opts) {
this.opts = Object.assign({}, Foo.defaultOptions, opts)
console.log(this.opts)
}
}
Foo.defaultOptions = {
key: 'value',
foo: 'bar',
answer: 42
}
let foo = new Foo({key: 'another value'})
//{ key: 'another value', foo: 'bar', answer: 42 }
You can merge with Object.assign (be aware that it does not perform deep merging - nested objects are replaced).
Or, if you want to declare your default options Object as a class variable (not at the end, after class declaration, or inside constructor), as you're using babel, you can use this plugin and do this:
class Foo {
defaultOptions = {
key: 'value',
foo: 'bar',
answer: 42
}
constructor(opts) {
this.opts = Object.assign({}, this.defaultOptions, opts)
console.log(this.opts)
}
}
It's more readable.
It is
class Foo {
constructor({key='value', foo='bar', answer=42} = {}) {
...
}
}
It is ES6 destructuring feature, not specific to ECMAScript 7 (ECMAScript Next) proposals.
Without destructuring it is usually done with object cloning/merging, Object.assign comes to help:
class Foo {
constructor(opts = {}) {
this.opts = Object.assign({
key: 'value',
foo: 'bar',
answer: 42
}, opts);
}
}

Javascript: Confuse about object property access

Here is a object I build:
var Obj = {
foo: {
foo: function () {
return this.bar;
},
bar: "bar"
},
bar: "BAR"
}
console.log(Obj.foo.foo());
The Obj.foo.foo method can only access the Obj.foo.bar property's value, or the property of its owner Obj.foo. Can it access the Obj.bar's value? How?
Scope scope and scope
Javascript is a fun language. And one of the realy cool things is the binding of this.
this is defined on function call. So depending on how you call a method. this can change. You can even change it on the fly. Given the following objects:
var Obj = {
foo: {
myFunc: function () {
return this.bar;
},
bar: "bar"
},
bar: "BAR"
}
var Obj2 = {
foo: {
bar: "foobar"
},
bar: "FOOBAR"
}
we can have some fun:
Obj.foo.myFunc(); // "bar"
but we could use call the method binding a different scope
Obj.foo.myFunc.call(Obj); // "BAR"
Here Obj is bound to this.
We can even bind completly different objects:
Obj.foo.myFunc.call(Obj2); // "FOOBAR"
Obj.foo.myFunc.call(Obj2.foo); // "foobar"
or call it ouside of it's scope:
var bar = "something completly different";
var func = Obj.foo.myFunc;
func(); // "something completly different"
so much fun :)
You could introduce a function scope holding the needed reference:
var Obj = (function() {
var that = this;
return {
foo: {
foo: function () {
return that.bar;
},
bar: "bar"
},
bar: "BAR"
})();
I think this should work..
No, there is no way to access "parent" reference - there is no such concept in Javascript. The only way to access Obj.bar from Obj.foo is by direct reference.
var Obj = {
foo: {
foo: function () {
return Obj.bar;
},
bar: "bar"
},
bar: "BAR"
}

Categories

Resources