Okay so there's a lot of similar questions here but none that actually address this.
What if I want to save the default parameters of a function on a separated object, in a separated file and then I want the function to use this default parameters only if not specified when calling it?
Example:
export const defaultParameters = { a: "foo", b: "bar"}
And then in another file:
import {defaultParameters} from ./utils.js
myFunction = function(defaultParameters = {}){
const {a, b} = defaultParameters;
console.log(a,b);
}
What I want for it to do is that calling
myFunction({a: "hello"})
should print
"hello", "bar"
But that's not how that works and I'm trying to figure this out. Anyone knows how to achieve this?
You won't be able to do it directly in the parameter, but you can inside the function..
eg..
import {defaultParameters} from ./utils.js
myFunction = function(params = {}){
const {a, b} = {...defaultParameters, ...params};
console.log(a,b);
}
Related
I'm desperate for someone to give me just some concise information about when I should use which brackets where and why in JS ES6. I know the basics but when we start talking about arrow syntax I just lose it and then can't see why we're wrapping braces in brackets etc... I feel like in order to truly understand why we lay things out the way we do I need to first understand what all of the use cases are for both {} and ().
For example. I'm really struggling to work out syntax like this:
const func = (obj) => {
console.log(obj.a)
}
func({a: "blue"})
It's the func({a: "blue"}) part I'm struggling with here.
Here's another example:
makeSound({
a: "bark",
b: 2,
c: "hiss"
})
function makeSound(options)
console.log("the" + options.a + "was a " + options.c)
I don't know what to make of this. What are we doing at the top with makeSound? I can see we're making an object but then why aren't we just declaring it as a variable with standard let makeSound = {}. What are we actually doing here? Is makeSound nothing untill we make it into a function further down the code?
It's the func({a: "blue"}) part I'm struggling with here.
{a: "blue"} is an object literal. The resulting object is passed as an argument to func(...).
I can see we're making an object but then why aren't we just declaring it as a variable with standard let makeSound = {}.
Because it is only needed once.
let details = {
a: "bark",
b: 2,
c: "hiss"
};
makeSound(details);
… would give the same result, but now you've got a details variable you don't need any more.
Is makeSound nothing untill we make it into a function further down the code?
function declarations are hoisted so it is a function even though the declaration appears later on.
I understand your confusion as there are quite a lot of curly braces indeed!
First, objects.
You define an object using brackets like this.
const obj = { a: 1 };
But you can also define an object inline, directly in a function argument list, using an object literal like this:
myFunc({ a: 1 }); // same as myFunc(obj);
Then you have arrow functions.
Their bodies is defined using curly braces too, just as regular functions, in which case you have to use the return keyword if you want to return a value from your function:
const myFunc = (arg) => { return 'hello ' + arg; }
However, arrow function also support implicit return, if you omit the curly braces, the return value will be implicit:
const myFunc = (arg) => 'hello ' + arg;
Now, you can also use the curly braces for desctructuring assignment.
For example:
const { a } = { a: 1 };
Here the desctructuring happens to the left of = and it allows you to extract properties from objects and assign them to variables.
You can also use object destructuring in function arguments, to access specific properties, like so:
const myFunc = ({ name }) => 'Hello ' + name;
This is equivalent to:
const myFunc = (person) => 'Hello ' + person.name;
And you could call this function with an object literal like this:
myFunc({ name: 'Jo' });
const func = (obj) => {
console.log(obj.a)
}
(obj) is basically saying func function takes obj as an argument.
You can also write it as such if you are passing only one argument;
const func = obj => {
console.log(obj.a)
}
What the parenthesis does basically giving you the ability to add multiple arguments. So like below;
const func = (obj1, obj2) => {
console.log(obj1.a, obj2.a)
}
func({a: "blue"})
Next func({a: "blue"})
Basically here you are calling func function with an object as an argument as a short hand.
So you can call it also like this
const argument = {a: "blue"}
func(argument)
Also you might see a lot of this kind of code
const func = (obj1, obj2) => console.log(obj1.a, obj2.a)
See there aren't anymore the curly braces around the console.log(). You can omit curly braces when you have only one line in the function. When you have multiple lines you will need to use curly braces to wrap the function body like so
func = (obj) => {
if (obj.a === "blue") {
return true
}
return false
}
In my JS component class, I have the following code at the top;
const {
getOwner
} = Ember;
What exactly does it mean ? Is the purpose to avoid code repetition ?
This is a new feature of JS present in ES6.
This is known as object destructure.
If you have an object like this.
const obj = { name: 'Stack Overflow' };
const { name } = obj;
console.log(name);
You can take a reference ->
Here for Destructure
And yeah about the const keyword.
The const keyword is used to declare a constant variable which is going to be same for the whole
and for const -> Here
try{
const a = 10;
a =12;
}catch(e){
console.log(e);
}
Is the purpose to avoid code repetition.
I don't really think so.
It is just a destructing assignment to assign Ember object's getOwner property to variable getOwner in your code. And const is there to make sure it is not overwritten by some other assignment
This is a bit of a tricky question regarding ES6 destructuring with default usage in a javascript object constructor.
I would like to receive a destructured parameters object with default values for my object constructor
so what i did was this
function bla({a=3,b=6}={}){
this.a=a;
this.b=b;
console.log(`this.a::'${this.a}' this.b::'${this.b}' a::'${a}' b::'${b}'`);
}
let myObject= new bla({a:1});
console.log(`myObject.a::'${myObject.a}' myObject.b::'${myObject.b}'`); // only a got overriden with value "1" and b remained its defauly value "6"
I know that what i did works. However, you can see that this is a bit of a code smell, because every time i need to add a new parameter to the constructor (for example {newParameter=3}) i also need to go down and add a matching line like this in the constructor body
this.newParameter=newParameter;
Is there any more elegant way to add a destructured parameter with default value which automatically is attached to "this."
I personally think your current approach is the most readable, but you can technically also do
function bla(obj = {}){
({
a: this.a = 3,
b: this.b = 6,
} = obj);
console.log(`this.a::'${this.a}' this.b::'${this.b}'`);
}
Don't know if this is best, but you could use Object.assign.
function bla(props={}) {
Object.assign(this, {a:3,b:6}, props);
console.log(`this.a::'${this.a}' this.b::'${this.b}'`);
}
let myObject= new bla({a:1});
console.log(`myObject.a::'${myObject.a}' myObject.b::'${myObject.b}'`); // only a got overriden with value "1" and b remained its defauly value "6"
You lose the parameter names, but it could be argued that this is better so that you don't accidentally mutate the parameter when you meant to mutate the object property.
I follow more a functional style to avoid the new on object creation and this keyword.
Explanation
You could simply write a function that retuns an object and takes several arguments with default values.
Thanks of the object literal property value shorthand you have less to write.
Code
function returnObject(a = 1, b = 2, c = 3) {
return {
a,
b,
c
}
}
console.log(returnObject())
If the objective is to use the variable names exactly once then this is what I'd do:
const foo = new Foo({ a: 1 });
console.log(`foo { a: ${foo.a}, b: ${foo.b} }`);
function Foo(config = {}) {
const defaults = { a: 3, b: 6 };
for (const [key, val] of Object.entries(defaults))
({ [key]: this[key] = val } = config);
}
Now, you only need to update the defaults object and you're done.
Actually, let's make a constructor constructor by abstracting this pattern:
const Foo = defcons({ a: 3, b: 6 });
const foo = new Foo({ a: 1 });
console.log(`foo { a: ${foo.a}, b: ${foo.b} }`);
function defcons(defaults) {
return function (config = {}) {
for (const [key, val] of Object.entries(defaults))
({ [key]: this[key] = val } = config);
};
}
Now, you can easily create as many such constructors as you want.
I am currently using the object destructuring pattern with default parameters described in that answer to ES6 Object Destructuring Default Parameters.
(function test({a = "foo", b = "bar"} = {}) {
console.log(a + " " + b);
})();
I would like to be able to access the object parameter without assigning it to a variable in the function body and explicitly listing each key.
(function test({a = "foo", b = "bar"} = {}) {
const options = {a, b};
console.log(options);
})();
I tried naming the object argument, but the function looses the ability to resolve missing keys to their default value.
(function test(options = {a = "foo", b = "bar"} = {}) {
console.log(options);
})();
It seems to be ignoring the default parameters when destructuring into a named argument.
Is this part of the ES6 spec? Is there a way to achieve the desired behavior without additional code in the function body?
Edit: I removed a superfluous example that did not add context to the question.
Honestly, I think you're overcomplicating this. Default parameters are not compulsory -- in this case your code can be cleaner without it.
I would simply take the object options as the parameter and do the destructuring within the body of the function, after assigning default values.
function test(options) {
options = Object.assign({a: 'foo', b: 'bar'}, options);
let {a, b} = options;
console.log(options, a, b);
}
test(); // foo bar
test({a: 'baz'}); // baz bar
test({b: 'fuz'}); // foo fuz
test({c: 'fiz'}); // foo bar
With particular regard to your final snippet:
(function test(options = {a: "foo", b: "bar"}) {
console.log(options);
})({a: "baz"});
The problem is that a default parameter is used when the value passed is undefined. Here, the value passed is {a: "baz"}. That is not undefined, so the default parameter is ignored. Objects are not merged automatically.
More broadly in answer to your question: there is no way of getting both an object and destructuring some of its properties in the parameters of a method. Frankly, I'm grateful, because function signatures can be hard enough to read at first glance as it is.
I have 2 files:
testrequire.js
let a = {};
function foo() {
a = 'test';
};
module.exports.foo = foo;
module.exports.a = a;
test.js
let a = require('./testrequire');
a.foo();
console.log(a);
When I run test.js, this is the result:
{ foo: [Function: foo], a: {} }
But I expect it to be like this:
{ foo: [Fuction: foo], a: 'test' }
However, when I change testrequire.js like this:
let a = {};
function foo() {
a.b = 'test';
};
module.exports.foo = foo;
module.exports.a = a;
The result is:
{ foo: [Function: foo], a: { b: 'test' } }
And it is perfectly like what I expected.
The question here is: Why function foo() can modify a's properties while it cannot modify a?
P/S: I did try var instead of let and the result is still the same. So it is definitely not ES6 let fault.
It's a pointer thing. It's the same in C/C++, Java etc. We've gotten so used to closures that we've sort of expect regular pointers to work the same. But pointers/references are simple indirections.
Let's walk through your code:
let a = {};
Create an object ({}) and point the variable a to that object.
function foo() {
a = 'test';
};
Declare a function foo() that overwrites the value of a with a string. Now, if you remember your C/assembly then you'd remember that the value of a pointer is the address of the thing it points to. So the original value of a is not {} but the address to that object. When you overwrite a with a string that object still exist and can be garbage collected unless something else points to it.
module.exports.foo = foo;
module.exports.a = a;
Export two properties, 1. foo which points to a function and 2. a which points to the same object that a is pointing to. Remember, just like in C/Java this does not mean that module.exports.a points to a but that it points to {}. Now you have two variables pointing to the same object {}.
Now, when you do:
a.foo();
All you're doing is changing the enclosed variable a to point to a string instead of the original object. You haven't done anything to a.a at all. It's still pointing to {}.
Workarounds
There are two ways to get what you want. First, the OO way. Don't create a closure for a, make it a regular object property:
function foo() {
this.a = 'test';
};
module.exports.foo = foo;
module.exports.a = {};
This will work as expected because modules in node.js are proper singletons so they can be treated as regular objects.
The second way to do this to use a getter to get the enclosed a. Remember that closures only work with functions, not objects. So just assigning the variable to a property like you did results in a regular pointer operation not a closure. The workaround is this:
let a = {};
function foo() {
a = 'test';
};
function getA() {
return a; // this works because we've created a closure
}
module.exports.foo = foo;
module.exports.getA = getA;
Now you can do:
a.foo();
a.getA(); // should return 'test'
foo can modify the variable a to point to something else.
But this has no effect on the object exported. When the require is done, the calling module receives whatever a pointed to at the time. After that, it does not care about what happens to (the variable) a.
In your second example, you are not assigning a to a new object, but you are modifying the existing object (by adding a new field). That will of course be visible to anyone who got hold of that object before.
This is (very roughly) analogous to
function callerDoesNotSeeThis(a){ a = 1 }
function callerSeesThis(a){ a.foo = 1 }