Problem with default parameter and object - javascript

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).

Related

Destructure a function parameter subproperty

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);

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);
}
}

Has been called with object assertion

Function I'm spying on, receives object as an argument. I need to assert that the function been called with certain properties of the object.
e.g: my SUT has:
function kaboom() {
fn({
foo: 'foo',
bar: 'bar',
zap: function() { ... },
dap: true
});
}
and in my test I can do this:
fnStub = sinon.stub();
kaboom();
expect(fnStub).to.have.been.called;
and that works (it's good to know that fn's been called). Now I need to make sure that the right object has been passed into the function. I care about only foo and bar properties, i.e. I have to set match for specific properties of the argument. How?
upd: sinon.match() seems to work for simple objects. Let's raise the bar, shall we?
What if I want to include zap function into assertion? How do I make that work?
Assuming you're using sinon-chai, you can use calledWith along with sinon.match to achieve this
expect(fnStub).to.have.been.calledWith(sinon.match({
foo: 'foo',
bar: 'bar'
}));
To achieve called.with partial object check, you can also use chai-spies-augment package (https://www.npmjs.com/package/chai-spies-augment) :
Usage (please, notice to .default after importing):
const chaiSpies = require('chai-spies');
const chaiSpiesAugment = require('chai-spies-augment').default;
chai.use(chaiSpies);
chai.use(chaiSpiesAugment);
usage :
const myStub = { myFunc: () => true };
const spy1 = chai.spy.on(myStub, 'myFunc');
myStub.myFunc({ a: 1, b: 2 });
expect(spy1).to.have.been.called.with.objectContaining({ a: 1 });

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"
}

Node.js adding multiple properties to an object

I'm wondering if there's a better way of doing this:
module.exports.foo = "bar";
module.exports.bar = "baz";
module.exports.foobar = "foobaz";
Now, I've heard about 'with' (and its dangers) but it doesn't work here because it can't add new properties to an object, it can only change existing properties. In fact if you try to add new properties you end up clobbering the enclosing scope which isn't very nice.
Something like this would be nice:
module.exports += {
foo: "bar",
bar: "baz",
foobar: "foobaz"
}
But that converts both sides to strings before concatenating them which is not what I want.
Does such syntactic sugar exist in Node.JS-flavoured JavaScript? Maybe a function on 'module' to add lots of properties at once?
You could write a simple function to loop over them and add them to module.exports or you can try node.extend which tries to mimic jquery's $.extend()
Use Object.assign()
Object.assign(module.exports, {
foo: "bar",
bar: "baz",
foobar: "foobaz"
});
Consider using _.extend
So in your example:
module.exports += {
foo: "bar",
bar: "baz",
foobar: "foobaz"
}
Would be:
var _ = require('underscore');
_.extend(module.exports,
{
foo: "bar",
bar: "baz",
foobar: "foobaz"
}
);
Or, if you do not want any dependencies, using raw javascript:
function myExtend(dest, obj) {
for (key in obj) {
var val = obj[key];
dest[key] = val;
}
}
myExtend(module.exports,
{
foo: "bar",
bar: "baz",
foobar: "foobaz"
}
);
If you are using the latter method, make sure to check for edge cases, which I haven't here!

Categories

Resources