Why use destructuring here? - javascript

I asked this question on The Odin Project where I encountered it and was directed to research destructuring, which I did. I understand what is happening but I'm at a loss as to why it is being done this way. Simply using raw variable names with no destructuring braces gets the same result (see my jfiddle link where I removed the destructuring and got the same result). I find it hard to learn something when I'm directed to use more code, typing, and complexity to achieve the same outcome. what benefit is received here by using return {sayName} in const Person and const {sayName} = Person(name) in const Nerd? I used return sayName and const sayName in the jfiddle and got the same result.
Original code:
const Person = (name) => {
const sayName = () => console.log(`my name is ${name}`)
return {sayName}
}
const Nerd = (name) => {
// simply create a person and pull out the sayName function with destructuring assignment syntax!
const {sayName} = Person(name)
const doSomethingNerdy = () => console.log('nerd stuff')
return {sayName, doSomethingNerdy}
}
const jeff = Nerd('jeff')
jeff.sayName() //my name is jeff
jeff.doSomethingNerdy() // nerd stuff
jfiddle:
const Person = (name) => {
const sayName = () => console.log(`my name is ${name}`)
return sayName
}
const Nerd = (name) => {
// simply create a person and pull out the sayName function with destructuring assignment syntax!
const sayName = Person(name)
const doSomethingNerdy = () => console.log('nerd stuff')
return {sayName, doSomethingNerdy}
}
const jeff = Nerd('jeff')
jeff.sayName() //my name is jeff
jeff.doSomethingNerdy() // nerd stuff

The general consensus is that this is a bad example for destructuring, but I've gone too far deep and will still attempt to make sense of it.
By using destructuring, it becomes possible to add additional functions to Person.
For example, Person needs to jump:
const Person = (name) => {
const sayName = () => console.log(`my name is ${name}`)
const jump = () => console.log('I jumped') // new feature
return {sayName, jump} // jump can inserted into the object here and used externally
}
Then, as pointed out in the comments, a much better way to pass Person into Nerd would be to:
const Nerd = (name) => {
// simply create a person and pull out the sayName function with destructuring assignment syntax!
const doSomethingNerdy = () => console.log('nerd stuff')
return {...Person(name) , doSomethingNerdy}
}
Before finally, Nerd can be destructured, and all the functions inside Person and Nerd can be used.
I suppose that's what they're trying to get at.

Related

How to call/manipulate a deconstructed object when it's a function's parameters

I'm going through the lessons on freecodecamp for Javascript. In the ES6 tutorials there's: "use destructuring assignment to pass an object as a function's parameter"
While I understand how they converted it to this, I don't understand at all what the purpose is or how to call/use the values/whatever inside this function. I tried searching online, but couldn't find anything related to functions with deconstructed objects as their parameters. Basically I don't understand how I can call or manipulate, or what I'm to do with an object in this form
i.e.
let dog1 = {
name : "whitey",
colour: "white"
}
let dog2 = {
name : "blackey",
colour: "black"
}
//const {name,colour} = dog1
//let's me call each property like: console.log(name)="whitey"
var dogfunc = (dog1) =>{
const {name, colour} = dog1}
//which can be rewritten as:
var dogfunc = ({name, colour}) => {}
//Now I'm stuck, I don't understand how I would call each property of dog1 or dog2
//I thought the point would be to allow me call/edit either dog1 or dog2 properties.
The idea of destructuring assignment is to reduce the extra necessary code as well as to "only get what is needed".
Using your example, consider a function which receives a dog object and print its name:
const dogObject = {
name: "Doge",
breed: "Labrador"
}
function printDogName(dog){
console.log(dog.name);
}
// or using destructuring
function printDogNameWithInternalDestructuring(dog){
const { name } = dog;
console.log(name);
}
printDogName(dogObject); // Doge
printDogNameWithInternalDestructuring(dogObject); // Doge
If you read carefully, you can see that from the whole dog object (it can contain a lot of other properties), we are only interested in the name. So, in the body of our functions we needed to get this property from the received object.
Using destructuring assignment, we can move the destructuring to the function required arguments definition. The function definition specifies which kind of object we expect as argument and from this object which properties are required, in our case, the name.
function printDogNameWithDestructuring({name}){
console.log(name);
};
printDogNameWithDestructuring(dogObject); // Doge
From your example:
let dog1 = {
name : "whitey",
colour: "white"
}
let dog2 = {
name : "blackey",
colour: "black"
}
const {name,colour} = dog1
//let's me call each property like: console.log(name)="whitey"
console.log(name) // prints whitney
var dogfunc = (dog1) => {
const {name, colour} = dog1
console.log(name);
}
dogfunc(dog1); // prints whitney
dogfunc(dog2); // prints blackey
//which can be rewritten as:
var dogfunc = ({name, colour}) => {
console.log(name)
}
// or using a shorter syntax
var dogfunc = ({name, colour}) => console.log(name);
dogfunc(dog1); // prints whitney
dogfunc(dog2); // prints blackey

Check and modify functions default argument values

I have the following function:
const setDefaults = (func, defArgs) => {
//should return the func with default arguments modified
// so that in case that an argument is not passed to the function,
// but it is provided in default Argument, the default argumnt is used
}
func: is a function that needs to have default parameters assigned from the defArgs
defArgs: set of default arguments
For example:
const greet = name => `Hi, ${name}!`
setDefaults(greet, {name: 'friend'})
greet(); // Hi, friend!
So far I have started diving into func.toString() and thinking about modifying the original function as a string and then eval the output, but that seems a bit verbose, so I was wondering if there is any better way to do this.
greet(); // Hi, friend!
You can't modify the original function because it's a const. If you mean to do something like this:
const greet = name => `Hi, ${name}!`
const parasiticGreet = setDefaults(greet, {name: 'friend'})
parasiticGreet(); // Hi, friend!
It is possible but I would simplify it like this:
const greet = name => `Hi, ${name}!`
const setDefaults = (func, defArgs = []) => (...args) => func(...defArgs.map((x, i) => args[i] === undefined ? x : args[i]));
const parasiticGreet = setDefaults(greet, ['friend']);
console.log(parasiticGreet()); // Hi, friend!

What do the square brackets in the following React test do?

I'm reading "Mastering React Test-Driven Development", and one of the refactorings the book recommends is extracting a common test into a helper function, by changing this:
it('saves existing first name when submitted', async () => {
expect.hasAssertions();
render(<CustomerForm {...{firstName: 'Ashley'}} onSubmit={(customer) =>
expect(customer.firstName).toEqual('Ashley')} />);
await ReactTestUtils.Simulate.submit(form('customer'));
});
it('saves existing last name when submitted', async () => {
expect.hasAssertions();
render(<CustomerForm {...{lastName: 'Jones'}} onSubmit={(customer) =>
expect(customer.lastName).toEqual('Jones')} />);
await ReactTestUtils.Simulate.submit(form('customer'));
});
...to this:
const itSavesExistingValueWhenSubmitted = (fieldName, fieldValue) => {
it('saves existing value when submitted', async () => {
expect.hasAssertions();
render(<CustomerForm {...{[fieldName]: fieldValue}} onSubmit={(props) =>
expect(props[fieldName]).toEqual(fieldValue)} />);
await ReactTestUtils.Simulate.submit(form('customer'));
});
}
itSavesExistingValueWhenSubmitted('firstName', 'Ashley');
itSavesExistingValueWhenSubmitted('lastName', 'Jones');
My question is about the code snippet {...{[fieldName]: fieldValue}} in the refactored test. I get that the ... is a spread attribute for the subsequent {} object. But why does fieldName need to be wrapped in square brackets? What is the grammar here?
{...{[fieldName]: fieldValue}}
Here [fieldName] is a computed property name. Computed property name is a feature that allows to use a value of a variable as a property name.
So when you pass fieldName as "firstName", "firstName" will be used as the property name, whereas if you omit [], the property name will literally be "fieldName" not the value of fieldName.
This is a feature of ES6. Refer this for more details
The square brackets is used to evaluate the object key in ES6.
You can do this way, for example:
var person = {};
var key = "name";
person[key] = "John";
console.log(person); // should print Object { name="John"}
But if you are using ES6 you can do the following to set the object:
var key = "name";
var person = {[key]:"John"};
console.log(person); // should print Object { name="John"}

.map() Function With ES6

How would I go about using the map function via ES6?
This is what I have so far.
var names = ["Will", "Rick", "Blake"];
var formalGreeting = (name) => {
names.map("Hello " + name);
}
You are missing a return in formalGreeting(). Can get rid of the {} block in arrow function and anything after => will be returned.
Also missing the function callback for map()
var names = ["Will", "Rick", "Blake"];
var formalGreeting = (arr) => arr.map(name => "Hello " + name);
console.log(formalGreeting(names))
Map takes a function so you just need to add the name => and return the value to push to the new array. This will get you started:
const namesArray = ["Will", "Rick", "Blake"];
const formalGreeting = names => names.map(name => `Hello ${name}`);
console.log(formalGreeting(namesArray));
ES6 considerations:
You don't need to wrap your code with {} for arrow functions that return one line of code
You should be using const and let to declare variables rather than var
String Templates are awesome, check them out!

Why are Higher Order Functions hiding my ES6 Class properties?

EDIT: I've added updated screenshots (at the end), for further clarification.
I'm attempting to use high order functions to compose subclasses/mixins but noticed I can only access the properties of the very first class I extend and can only access the properties of any subsequent classes after I've called the class. Here is a contrived example of what I mean:
These are functions that will apply subclasses to the parent class:
export const middleNameClass = middlename => BaseClass => {
return class extends BaseClass {
constructor(args) {
super(args);
this.middlename = middlename;
}
};
};
export const lastNameClass = lastname => BaseClass => {
return class extends BaseClass {
constructor(args) {
super(args);
this.lastname = lastname;
}
};
};
Here is firstNameClass, which will be extended directly by the parent class, Person:
class firstNameClass {
constructor(args) {
this.firstname = args;
}
}
This is Person, which extends firstNameClass:
class Person extends firstNameClass {
constructor(args) {
super(args);
this.getNames = this.getNames.bind(this);
// I'm using to log class properties to the console
this.getNames();
}
getNames() {
console.log("this inside getNames(): ", this);
console.log("firstnames inside getNames(): ", this.firstname);
console.log("middlenames inside getNames(): ", this.middlename);
console.log("lastnames inside getNames(): ", this.lastname);
}
}
and finally, here is where I apply my higher order functions and create my class:
const enhancedClass = compose(
middleNameClass("Allan"),
lastNameClass("Poe")
)(Person);
const namedPerson = new enhancedClass("Edgar");
However, I see the following when I check my console.log:
this.firstnames inside getNames(): Edgar
this.middlenames inside getNames(): undefined
this.lastnames inside getNames(): undefined
Could someone explain what I'm doing wrong?
EDIT:
Here are the contents of my Person class:
and here is what is output to the console, after I create the class:
At new enhancedClass('Edgar'), this happens:
lastNameClass's constructor calls super
middleNameClass's constructor calls super
Person's constructor calls super
firstNameClass does this.firstName = 'Edgar'
Return to Person, which calls getNames
Return to middleNameClass, which does this.middleName = 'Allan'
Return to lastNameClass, which does this.lastName = 'Poe'
Calling getNames afterwards should work. Same thing would've happened if you used extend every time.
This isn't an answer to your question but maybe it's a solution to your problem
JavaScript doesn't have multiple inheritance but luckily for you functional programming has nothing to do JavaScript's class system, object prototypes, or other object-oriented concepts. Functional programming is all about functions!
We begin writing our inheritable modules with some functions
// module 1
const hasFirstName = (firstName = "") => o =>
{
field (o, 'firstName', firstName)
}
// module 2
const hasMiddleName = (middleName = "") => o =>
{
field (o, 'middleName', middleName)
}
// module 3
const hasLastName = (lastName = "") => o =>
{
field (o, 'lastName', lastName)
}
We haven't defined field yet, but don't worry. Let's look at a somewhat more involved module next
// module 4
const nameable = (first, middle, last) => o =>
{
inherit (o, hasFirstName (first))
inherit (o, hasMiddleName (middle))
inherit (o, hasLastName (last))
method (o, 'getFullName', (self) => `${self.firstName} ${self.middleName} ${self.lastName}`)
method (o, 'sayHelloTo', (self, other) => `Hello ${other.getFullName ()}, my name is ${self.getFullName ()}`)
}
OK, so now we can see how some modules can be composed of other modules. Before we look at inherit and method, let's see how we'd use our module
const Person = (first, middle, last) =>
Obj (self => {
inherit (self, nameable (first, middle, last))
})
So maybe you're annoyed that I keep making stuff up in each new code paste, but this is a very powerful practice called wishful thinking
"Wishful Thinking" is a very powerful programming practice:
Before implementing a component you write some of the code that actually uses it. This way you discover what functions with what parameters you really need, which leads to a very good interface. You will also have some good test code for your component.
The idea is based on the fact that an interface's purpose is to simplify the code that uses the component, not to simplify the code that implements it.
Exercising this practice, we wished up this imaginary object system based on what we need it to do - not based on what JavaScript's object system is capable of.
Of course we expect that using our Person will be straightforward
const p1 =
Person ('Augusta', 'Ada', 'King-Noel', 166)
const p2 =
Person ('Gerald', 'Jay', 'Sussman', 71)
console.log (p1.sayHelloTo (p2))
// Hello Gerald Jay Sussman, my name is Augusta Ada King-Noel
And here's the dependencies: What I want you to see here is that no class or even this is used. So even if JavaScript didn't have a native object system, this demonstrates you could make your own
const Obj = (f, instance = {}) =>
(f (instance), instance)
const inherit = (o, f) =>
Object.assign (o, f (o))
const field = (o, name, value) =>
Object.assign (o, { [name]: value })
const method = (o, name, f) =>
Object.assign (o, { [name]: (...xs) => f (o, ...xs) })
Full program demonstration
// DIY class system
const Obj = (f, instance = {}) =>
(f (instance), instance)
const inherit = (o, f) =>
Object.assign (o, f (o))
const field = (o, name, value) =>
Object.assign (o, { [name]: value })
const method = (o, name, f) =>
Object.assign (o, { [name]: (...xs) => f (o, ...xs) })
// module 1
const hasFirstName = (firstName = "") => o =>
{
field (o, 'firstName', firstName)
}
// module 2
const hasMiddleName = (middleName = "") => o =>
{
field (o, 'middleName', middleName)
}
// module 3
const hasLastName = (lastName = "") => o =>
{
field (o, 'lastName', lastName)
}
// module 4
const nameable = (first, middle, last) => o =>
{
inherit (o, hasFirstName (first))
inherit (o, hasMiddleName (middle))
inherit (o, hasLastName (last))
method (o, 'getFullName', (self) => `${self.firstName} ${self.middleName} ${self.lastName}`)
method (o, 'sayHelloTo', (self, other) => `Hello ${other.getFullName ()}, my name is ${self.getFullName ()}`)
}
// Person class
const Person = (first, middle, last) =>
Obj (self => {
inherit (self, nameable (first, middle, last))
})
// demo
const p1 =
Person ('Augusta', 'Ada', 'King-Noel')
const p2 =
Person ('Gerald', 'Jay', 'Sussman')
console.log (p1.sayHelloTo (p2))
// Hello Gerald Jay Sussman, my name is Augusta Ada King-Noel
Our Person class can obviously define its own fields and methods as well
const dateDiff = (d1, d2) =>
Math.abs (d1 - d2) / 1000 / 60 / 60 / 24 / 365 >> 0
const Person = (first, middle, last, birthdate = new Date) =>
Obj (self => {
inherit (self, nameable (first, middle, last))
field (self, 'birthdate', birthdate)
method (self, 'calculateAge', (self) => dateDiff (new Date, self.birthdate))
method (self, 'sayAge', (self) => `I am ${self.calculateAge()} years old`)
})
const p2 =
Person ('Gerald', 'Jay', 'Sussman', new Date ('February 8, 1947'))
console.log (p2.sayAge ())
// I am 71 years old
Get creative and invent any other features you want
Maybe you want to make an overrideMethod that gives you the ability to define a new method with the same name, but still give the caller access to both methods
Maybe you want privateMethod or classMethod helpers
Maybe field could be enhanced to emit events when values changed
Maybe field could be changed so that values cannot be set, where mutableField could be used for fields that can change
Write it how you want it then make your wishes come true. Any limitations are you own.

Categories