Categories
JavaScript Basics

JavaScript Concepts We Should Learn to Master React — Spread and Rest

Knowledge of the latest JavaScript syntax is essential to understand and master React.

It takes reading the manual much easier and writing React code much shorter and cleaner.

Once we master some new JavaScript syntax, writing React code would be a breeze.

In this article, we’ll look at the use of the spread and rest operators in React apps.

Spread Operator

The spread operator comes in handy when we need to pass in lots of props, but we don’t want to write them all out in the JSX code.

This operator can be used with arrays or objects. With objects, we can use it to make a copy of an object as follows:

const obj = {
  a: 1
};
const copy = {
  ...obj
};

In the code above, we used the spread operator to make a shallow copy of the obj object by copying the keys and values into the copy object.

A shallow copy means that only the top-level is copied, the rest are still referencing the original object.

Therefore, obj and copy are different objects with the same keys and values.

We can also use it to merge multiple objects together. For instance, we can use it as follows to merge multiple objects into one:

const obj1 = {
  a: 1
};
const obj2 = {
  b: 2
};
const obj3 = {
  a: 3,
  c: 3,
};
const merged = {
  ...obj1,
  ...obj2,
  ...obj3
};

In the code above, we have 3 objects, obj1 , obj2 , and obj3 which are merged together with the spread operator into one object, which is the merged object.

If there’re 2 values with the same corresponding key, then the one that’s merged in later overwrites the one that’s added earlier.

Therefore, merged is {a: 3, b: 2, c: 3} .

To use the spread operator with arrays, we can use it as follows:

const arr = [1, 2, 3];
const copy = [...arr];

In the code above, we have the copy array, which we copied from the arr array by using the spread operator.

It also makes a shallow copy, so nested objects and arrays are still referencing the items in the original array.

We can also merge in arrays with the spread operator. For instance, we can write:

const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
const merged = [...arr1, ...arr2];

Then we get the that the merged array is:

[1, 2, 3, 4, 5, 6]

The values are just appended in the order they’re spread.

For instance, a frequently used example of this is the following:

import React from "react";

const props = {
  greeting: "hi",
  firstName: "jane",
  lastName: "doe"
};

const Greeting = ({ greeting, firstName, lastName }) => (
  <p>
    {greeting}, {firstName} {lastName}
  </p>
);

const App = () => {
  return <Greeting {...props} />;
};
export default App;

In the code above, we have the Greeting component, which takes the greeting , firstName , and lastName props.

Then we have the App component, which uses the spread operator by writing:

<Greeting {...props} />

The expressions {...props} is for spreading the properties of props into props that are passed into the Greeting component.

Therefore, we’ll see that the props will be rendered with their values.

Rest Operator

The rest operator is used to keep parameters that haven’t been added as named parameters in an array if we’re using the rest parameter on parameters or an object if we’re using the rest operators on an object.

For instance, if we have some props that we don’t always want to reference. We can use the rest operator on it as follows:

import React from "react";

const props = {
  firstName: "jane",
  lastName: "doe",
  age: 20,
  gender: "female",
  job: "waitress"
};

const Person = ({ firstName, lastName, ...restProps }) => (
  <p>
    {firstName} {lastName}, {restProps.age}, {restProps.gender}, {restProps.job}
  </p>
);

const App = () => {
  return <Person {...props} />;
};
export default App;

In the code above, we used the rest operator to put the props that we didn’t destructure into one big object.

The props object we have above had the age , gender , and job properties were spread with the spread operator inside the App component, so they’re all passed in as props.

However, in the Person component, we have the restProps variable with the rest operator before it, which converts it to an object with the age , gender , and job properties inside it.

Therefore, we can reference those properties in the restProps variable like we did inside the Greeting component.

Conclusion

The spread and rest operators are very useful for React and general JavaScript code. The spread operator lets us make shallow copies of objects and arrays and also merge them together.

Also, the rest operator is useful for storing properties or array entries that haven’t been destructured or listed as arguments into an object or array respectively.

Categories
JavaScript Basics

Using the Spread Operator in JavaScript

The spread syntax allows us to break up a collection of objects, like arrays, into individual arguments or insert them into a different iterable object, like an array.

With the 2018 version of JavaScript, we can also spread properties of an object into another object, with keys and values spread into another object. The spread syntax is denoted by three periods before your object.

For example, we can write:

...arr

The spread syntax works by copying the values of the original array and then inserting them into another array, or putting them in the order they appeared in the array as the list of arguments in a function in the same order.

When the spread operator is used with objects, the key-value pairs appear in the same order they appeared in in the original object.

We can use the spread syntax to spread an array of values as arguments of a function. For example, we can write:

const arr = [1,2,3];
const add = (a,b,c) => a+b+c;
add(...arr) // returns 6

In the example above, the spread operator spreads the variables into the argument in the same order they appeared in in the array. So 1 is passed into a, 2 is passed into b, and 3 is passed into c.


Spread Arrays

For arrays, we can also use the spread syntax to insert one array’s values into another array. For example, we can write:

const arr = [1,2,3];
const arr2 = ['a','b','c',...arr,'d']; // arr2 is ['a','b','c',1,2,3,'d']

As we can see, the spread operator inserts the values exactly where we spread the array, in the same order they appeared in the array.

So, 1 is inserted between a and d, then 2 is inserted between 1 and d, and 3 is inserted between 2 and d. The result is that we copied an array’s values into another array with the spread operator in the same order they appeared in, and exactly where you put the array spread expression.

Without the spread operator, we have to write loops to insert them into the position we want. We slice the array into two and then call concat on the three parts, then assign the result to the array you inserted the stuff into. It sounds painful just thinking about it.

Note that with the spread operator, only the first level of the array is spread. If we have nested or multi-dimensional arrays, it’s going to copy the references as-is. It will not do anything to nested items.

With ES2018, we can do the same thing with objects, like the following:

const obj = {a: 1, b: 2};
let objClone = { ...obj }; // objClone is {a: 1, b: 2}

This creates a shallow copy of the object. It means that only the first level of the object is copied.

For nested objects, it’s going to copy the references as-is. It will not do anything to nested items. The top-level keys and values of the object will be copied to objClone.

So, if we have nested objects, we get:

const obj = {
  a: 1,
  b: {
    c: 2
  }
};
let objClone = {
  ...obj
};
console.log(objClone)

In objClone, we get:

{
  a: 1,
  b: {
    c: 2
  }
}

So, nested objects will reference the same ones as the original.

The spread operator can be used as an alternative to other functions that existed before.

For example, we can use it to replace the apply function for passing in arguments to a function. The apply function takes an array of arguments for the function it’s called on as the second argument.

With the apply function, we call it as follows:

const arr = [1,2,3]
const sum = (a,b,c)=> a+b+c;
sum.apply(null, arr); // 6

With the spread syntax, we can write the following instead:

const arr = [1,2,3]
const sum = (a,b,c)=> a+b+c;
sum(...arr)

The spread operator also work with strings. We apply the spread operator to strings, we get an array with the individual characters of the string.

For example, if we write:

const str = 'abcd';
const chars = [...str];

We get [“a”, “b”, “c”, “d”] as the value of chars.


Using Spread Operator Multiple Times

We can use the spread syntax multiple times in one place. For example, we can have the following:

const arr = [1,2,3];
const arr2 = [4,5];
const sum = (a,b,c,d,e,f)=> a+b+c+d+e+f;
sum(...arr, ...arr2, 6)

As usual, the spread syntax will spread the array of numbers into arguments of the array in the order as they appeared in.

So, sum(…arr, …arr2, 6) is the same as sum(1,2,3,4,5,6).

1, 2, and 3 are the first three arguments, which are the entries of arr in the same order, and 4 and 5 are the fourth and fifth arguments, which are spread after 1, 2, and 3.

Then, in the end, we have 6 as the last argument. We can also see the spread syntax work with the normal function call syntax.


Use It in Constructors

We can use the spread operator as arguments for object constructors. For example, if we want to create a new Date object, we can write:

let `dateFields = [2001, 0, 1];
`let `date = new Date(...dateFields);`

The items in the dateFields array are passed into the constructors as arguments in the order they appeared in. The alternative way to write that would be much longer, something like:

let `dateFields = [2001, 0, 1];
const year = dateFields[0];
const month = dateFields[1];
const day = dateFields[2];
`let `date = new Date(year, month, day);`

Copying Items

The spread syntax can also be used to make a shallow copy of an array or an object as it works by creating copies of the top-level elements of an array or key-value pairs of an object, and then inserting them into the place you used the spread operator with.

For copying arrays, we can write:

const arr = [1, 2, 3];
const arr2 = [...arr, 4, 5];

The above example, arr2, is [1,2,3,4,5], while arr1 is still [1,2,3].

arr1 is not referenced by arr2 because the spread operator actually makes a copy of the array and then inserts the values. Note that this doesn’t work with multi-dimensional arrays as it only makes copies of the top-level elements.

We can apply the spread syntax multiple times in one array or object. An example for array would be:

let `arr = [1, 2, 3];
`let `arr2 = [4, 5];
`let `arr3 = [...arr2, ...arr];`

In the above example, we get [4,5,1,2,3]. arr1 and arr2 are unaffected as a copy of the values from arr1 and arr2 are inserted into arr3.


Spread Operator and Objects

With ES2018, the spread operator works with object literals. Then, key-value pairs of an object can be inserted into another object with the spread operator.

If there are two objects with the same key that the spread operator is applied to in the same object, the one that’s inserted later will overwrite the one that’s inserted earlier.

For example, if we have the following:

let obj1 = {foo: 'bar', a: 1};
let obj2 = {foo: 'baz', b: 1};
let obj3 = {...obj1, ...obj2 }

Then we get {foo: “baz”, a: 1, b: 1} as the value of obj3 because obj1 is spread before obj2.

They both have foo as a key in the object. First, foo: 'bar' is inserted by the spread operator to obj3. Then, foo: 'baz' overwrites the value of foo after obj2 is merged in, as it has the same key foo but inserted later.

This is great for merging objects as we don’t have to loop through the keys and put in the values, which is much more than one line of code.

One thing to note is that we can’t mix the spread operator between regular objects and iterable objects. For example, we will get TypeError if we write the following:

let `obj = {foo: 'bar'};
`let `array = [...obj];`

Conclusion

As we can see, the spread syntax is a great convenience feature of JavaScript. It lets us combine different arrays into one.

Also, it lets us pass arrays into a function as arguments with just one line of code. With ES2018, we can also use the same operator to spread key-value pairs into other objects to populate one object’s key-value pairs into another object.

The spread operator works by copying the top-level items and populating them in the place you use the spread operator, so we can also use it to make shallow copies of arrays and objects.

Categories
JavaScript Basics

The Destructuring Assignment in JavaScript

JavaScript is the main language for front-end development. It’s also used a lot for back-end development.

The language has gotten much better in the last few years.

In this article, we’ll look at one of the better features of JavaScript, which is the destructuring assignment syntax.


Destructuring Syntax on Arrays

The destructuring syntax is used for assigning object and array entries to their own variables.

For instance, we can write the following code to assign array entries to individual variables:

const [a, b] = [1, 2];

Then, the destructuring assignment syntax assigns the entries to the variables located in the same position.

Therefore, a is 1 and b is 2.

We don’t have to assign all of them to variables, so we can write something like:

const [a] = [1, 2];

Then a is 1.

Also, we can assign variables that haven’t been assigned a variable to an array.

For instance, we can write:

const [a, ...b] = [1, 2, 3];

The three dots specify that we are assigning the array entries that haven’t been assigned to a variable to an array.

Therefore, a is 1 and b is [2, 3].

We can also set a default value for variables of the destructuring assignment. For instance, we can write:

const [a, b, c = 3] = [1, 2];

Then c is 3 because we didn’t assign anything to it, so the default value of 3 is assigned to it.

Nested values also work. For instance, we can write:

const [{
  foo
}, b] = [{
  foo: 1
}, 2];

Then the JavaScript interpreter will match the shape of the object and the position in the array and do the destructuring assignment accordingly.

Therefore, the value of foo on the right is assigned to foo on the left and we get that foo is 1.


Destructuring Syntax on Objects

We can use the destructuring syntax on objects to assign property values to a variable with the same name as the corresponding property name.

For instance, we can write:

Then a is 1 and b is 2 since a is on the left side and a is 1, so the JavaScript interpreter matches the property name and variable name and makes the assignment by that name.

The same is done for property b .

We can set it to a different variable name also. To do this, we write:

Then we assign the 2 to c by matching the property b on the right side to b on the left side.

Like the array destructuring syntax, we can assign a default value to the variable on the left side.

For instance, we can write:

Then c is set to 3. when there’s no c property in the object. Therefore, c would be 3 in the code above.

The destructuring assignment also works with nested values. For example, we can write:

The example will set b to 3 since the JavaScript interpreter matched the structure of the objects on the left and right and then set b on the left to 3.

Values that aren’t assigned to a variable can be assigned to another object as follows:

The rest will have:

{b: 2, c: 3}

As the value since we used the ... operator.


Swapping Variables

A great application of the destructuring syntax is swapping variable values.

For instance, if we have:

let a = 1,
  b = 2;
[a, b] = [b, a];

Then a is 2 and b is 1 after the application of the destructuring syntax.

It’s much less taxing on our brains since we don’t have to have temporary placeholder variables or add and subtract values to swap values.


For…of Loop

The for...of loop works with the destructuring syntax.

For instance, we can write:

Then we get:

Jane Smith
Don Smith

From the console.log output. It’s very convenient for extracting content out of the object that’s being iterated on.


Conclusion

We can use the JavaScript destructuring syntax to assign property values and object entries to variables.

It works with nested objects. We can assign extra entries to arrays with the ... operator.

The for...of loop also works with the destructuring syntax.

To write clean code, we should use this now.

Categories
JavaScript Basics

Using the Destructuring Assignment Syntax in JavaScript

The destructuring assignment syntax is a JavaScript syntax feature that was introduced in the 2015 version of JavaScript and lets us unpack a list of values of an array or key-value pairs of an object into individual variables.

It’s very handy for retrieving entries from arrays or objects and setting them as values of individual variables. This is very handy because the alternative was to get an entry from an array from an index and then setting them as values of variables for arrays.

For objects, we have the value from the key and set them as values of variables.


Array Destructuring

We can use the destructuring assignment syntax easily in our code. For arrays, we can write:

const [a,b] = [1,2];

Then, we get 1 as the value of a and 2 as the value of b because the destructing syntax unpacked the entries of an array into individual variables.

Note that the number of items in the array does not have to equal the number of variables. For example, we can write:

const [a,b] = [1,2,3]

Then a is still 1 and b is still 2 because the syntax only sets the variables that are listed in the same order as the numbers appeared in the array. So, 1 is set to a, 2 is set to b, and 3 is ignored.

We can also use the rest operator to get the remaining variables that weren’t set to variables. For example, we can have:

const [a,b,...rest] = [1,2,3,4,5,6]

Then, rest would be [3,4,5,6] while we have a set to 1 and b set to 2. This lets us get the remaining array entries into a variable without setting them all to their own variables.

We can use the destructuring assignment syntax for objects as well. For example, we can write:

const {a,b} = {a:1, b:2};

In the code above, a is set to 1 and b is set to 2 as the key is matched to the name of the variable when assigning the values to variables.

Because we have a as the key and 1 as the corresponding value, the variable a is set to 1 as the key name matches the variable name. It is the same with b. We have a key named b with a value of 2, because we have the variable named b, we can set b to 2.

We can declare variables before assigning them with values with the destructuring assignment syntax. For example, we can write:

let a, b;
([a, b] = [1, 2]);

Then, we have a set to 1 and b set to 2 because a and b that were declared are the same ones that are assigned.

As long as the variable names are the same, the JavaScript interpreter is smart enough to do the assignment regardless of whether they’re declared beforehand or not.

We need the parentheses on the line so that the assignment will be interpreted as one line and not individual blocks with an equal sign in between, because two blocks on the same line aren’t valid syntax.

This is only required when the variable declarations happen before the destructuring assignment is made.

We can also set default values for destructuring assignments. For instance:

let a,b;
([a=1,b=2] = [0])

This is valid syntax. In the code above, we get that a is 0 because we assigned 0 to it. b is 2 because we didn’t assign anything to it.

The destructuring assignment syntax can also be used for swapping variables, so we can write:

let a = 1;
let b = 2;
([a,b] = [b,a])

b would become 1 and a would become 2 after the last line of the code above. We no longer have to assign things to temporary variables to swap them, and we also don’t have to add or subtract things to assign variables.

The destructuring assignment syntax also works for assigning returned values of a function to variables.

So, if a function returns an array or object, we can assign them to variables with the destructuring assignment syntax. For example, if we have:

const fn = () =>[1,2]

We can write:

const [a,b] = fn();

To get 1 as a and 2 as b with the destructuring syntax because the returned array is assigned to variables with the syntax.

Similarly, for objects, we can write:

const fn = () => {a:1, b:2}
const {a,b} = fn();

We can ignore variables in the middle by skipping the variable name in the middle of the destructuring assignment. For example, we can write:

const fn = () => [1,2,3];
let [a,,b] = fn();

We get a with the value of 1 and b with the value of 3, skipping the middle value.

It’s important to know that if we use the rest operator with a destructuring assignment syntax, we cannot have a trailing comma on the left side, so this:

let [a, ...b,] = [1, 2, 3];

Will result in a SyntaxError.


Object Destructuring

We can use the destructuring assignment syntax for objects as well. For example, we can write:

const {a,b} = {a:1, b:2};

In the code above, a is set to 1 and b is set to 2 because the key is matched to the name of the variable when assigning the values to variables.

As we have a as the key and 1 as the corresponding value, the variable a is set to 1 because the key name matches the variable name. It is the same with b. We have a key named b with a value of 2, because we have the variable named b, we can set b to 2.

We can also assign it to different variable names, so we don’t have to set the key-value entries to different variable names. We just have to add the name of the variable we want on the value part of the object on the left side, which is the one we want to assign it to, like the following:

const {a: foo, b: bar} = {a:1, b:2};

In the code above, we assigned the value of the key a to foo and the value of the key b to the variable bar. We still need a and b as the keys on the left side so they can be matched to the same key names on the right side for the destructuring assignment.

However, a and b aren’t actually defined as variables. It’s just used to match the key-value pairs on the right side so that they can be set to variables foo and bar.

Destructuring assignments with objects can also have default values. For example, we can write:

let {a = 1, b = 2} = {a: 3};

Then, we have a set to 3 and b set to 2 which is the default value as we didn’t have a key-value pair with a key named b on the right side.

Default values can also be provided if we use the destructuring syntax to assign values to variables that are named differently from the keys of the originating object. So, we can write:

const {a: foo=3, b: bar=4} = {a:1};

In this case, foo would be 1 and bar would be 4 because we assigned the left bar with the default value, but assigned foo to 1 with the destructuring assignment.

The destructuring assignment also works with nested objects. For example, if we have the following object:

let user = {
  id: 42,
  userName: 'dsmith',
  name: {
    firstName: 'Dave',
    lastName: 'Smith'
  }
};

We can write:

let `{`userName`,` name`: { firstName }} =` user;

To set displayName to 'dsmith' , and firstName to 'Dave'. The lookup is done for the whole object and, so, if the structure of the left object is the same as the right object and the keys exist, the destructuring assignment syntax will work.

We can also use the syntax for unpacking values into individual variables while passing objects in as arguments.

To do this, we put what we want to assign the values, which is the stuff that’s on the left side of the destructuring assignment expression, as the parameter of the function.

So, if we want to destructure user into its parts as variables, we can write a function like the following:

const who = (`{`userName`,` name``: { firstName }}) => `${``userName`}'s first name is ${firstName}`;

who(user)

So, we get userName and firstName, which will be set as 'dsmith' and 'Dave' respectively as we applied the destructuring assignment syntax to the argument of the who function, which is the user object we defined before.

Likewise, we can set default parameters as with destructuring in parameters like we did with regular assignment expressions. So, we can write:

const who = (`{`userName = 'djones'`,` name``: { firstName }}) => `${``userName`}'s first name is ${firstName}``

If we have user set to:

let user = {
  id: 42,
  name: {
    firstName: 'Dave',
    lastName: 'Smith'
  }
};

Then we when call who(user), we get 'djones's first name is Dave' as we set 'djones' as the default value for userName.

We can use the destructuring assignment syntax when we are iterating through iterable objects. For example, we can write:

const people = [{
    firstName: 'Dave',
    lastName: 'Smith'
  },
  {
    firstName: 'Jane',
    lastName: 'Smith'
  },
  {
    firstName: 'Don',
    lastName: 'Smith'
  },
]

for (let {
    firstName,
    lastName
  } of people) {
  console.log(firstName, lastName);
}

We get:

Dave Smith
Jane Smith
Don Smith

Logged, as the destructuring syntax works in for...of loops because the variable after the let is the entry of the array.

Computed object properties can also be on the left side of the destructuring assignment expressions. So, we can have something like:

let key = 'a';
let {[key]: bar} = {a: 1};

This will set bar to 1 because [key] is set to a and then the JavaScript interpreter can match the keys on both sides and do the destructuring assignment to the variable bar.

This also means that the key on the left side does not have to be a valid property or variable name. However, the variable name after the colon on the left side has to be a valid property or variable name.

For instance, we can write:

`const obj = { 'abc 123': 1};
const { 'abc 123': abc123 } = obj;

console.log(abc123); //` 1

As long as the key name is the same on both sides, we can have any key in a string to do a destructuring assignment to variables.

Another thing to note is that the destructuring assignment is smart enough to look for the keys on the same level of the prototype chain, so if we have:

var obj = {a: 1};
obj.__proto__.b = 2;
const {a, b} = obj;

We still get a set to 1 and b set to 2 as the JavaScript interpreter looks for b in the prototype inheritance chain and sets the values given by the key b.

As we can see, the destructuring assignment is a very powerful syntax. It saves lots of time writing code to assign array entries to variables or object values into their own variables.

It also lets us swap variables without temporary variables and makes the code much simpler and less confusing. It also works through inheritance so the property does not have to be in the object itself, but works even if the property is in its prototypes.

const fn = () => {a:1, b:2}
const {a,b} = fn();
Categories
JavaScript Basics

Using Classes in JavaScript

Classes in JavaScript are a special syntax for its prototypical inheritance model that is a comparable inheritance in class-based object oriented languages. Classes are just special functions added to ES6 that are meant to mimic the class keyword from these other languages. In JavaScript, we can have class declarations and class expressions, because they are just functions. So like all other functions, there are function declarations and function expressions.

Classes serve as templates to create new objects.

The most important thing to remember: Classes are just normal JavaScript functions and could be completely replicated without using the class syntax. It is special syntactic sugar added in ES6 to make it easier to declare and inherit complex objects.

Defining Classes

To declare a class, we use the class keyword. For example, to declare a simple class, we can write:

class Person{
  constructor(firstName, lastName) {
    this.firstName= firstName;
    this.lastName = lastName;
  }
}

Class declarations aren’t hoisted so they can’t be used before they are defined in the code, as the JavaScript interpreter will not automatically pull them up to the top. So the class above won’t work before it’s defined in the code like the following:

const person = new Person('John', 'Smith');class Person{
  constructor(firstName, lastName) {
    this.firstName = firstName;
    this.lastName = lastName;
  }
}

We will get a ReferenceError if we run the code above.

We can also define a class by a class expression, which is an alternative syntax. They can be named or unnamed. We can also assign a class to a variable like we do with functions. If we do that, we can reference the class by its name. For example, we can define:

let Person = class {
  constructor(firstName, lastName) {
    this.firstName = firstName;
    this.lastName = lastName;
  }
}

To get the name of the unnamed classes above, we can get the name with the name property, like so:

console.log(Person.name);

We can also define a named class like the following:

let Person = class Person2 {
  constructor(firstName, lastName) {
    this.firstName = firstName;
    this.lastName = lastName;
  }
}

Then to get the name of the class, we can use the name property again. So we if we write:

console.log(Person.name)

We get Person2 logged.

The class body is defined with curly brackets. We define the class members inside the brackets. The body of the class is executed in strict mode, so everything defined in strict mode applies to the definition of a class, so we can’t define variables without keyword before it like var , let or const, and many other rules apply when you define a class.

Classes in JavaScript also have a constructor method that lets us set fields when the object is instantiated with a class. Each class can only have one constructor method in it. If there’s more than one, then SyntaxError will be thrown. A constructor can also call the super method to call the constructor of the super class if the class extends a parent class.

Methods that aren’t declared static constitutes the prototypical methods of the class. They are called after an object has been created by using the new keyword. For example, the following class has only prototypical methods:

class Person{
  constructor(firstName, lastName) {
    this.firstName = firstName;
    this.lastName = lastName;
  } get fullName(){
    return `${this.firstName} ${this.lastName}`
  } sayHi(){
    return `Hi, ${this.firstName} ${this.lastName}`
  }
}

In the Person class above, fullName and sayHi are prototypical methods. They are called like this:

const person = new Person('Jane', 'Smith');
person.fullName() // 'Jane Smith'

Static methods are methods that can be called without creating an object from the class using the new keyword. For instance, we can have something like the following:

class Person {
  constructor(firstName, lastName) {
    this.firstName = firstName;
    this.lastName = lastName;
  }
  get fullName() {
    return `${this.firstName} ${this.lastName}`
  }
  sayHi() {
    return `Hi, ${this.firstName} ${this.lastName}`
  }
  static personCount() {
    return 3;
  }
}

We can call the personCount function without using the new keyword to create an instance of the class. So if we write:

Person.personCount

We get 3 returned.

The this value inside prototypical methods will be the value of the object. For static methods the value of this has the class that the static method is in as the value.

<img alt="Image for post" class="s t u dc ai" src="https://miro.medium.com/max/11426/0BvO6QcBmjOGtcHnL" width="5713" height="3809" srcSet="https://miro.medium.com/max/552/0BvO6QcBmjOGtcHnL 276w, https://miro.medium.com/max/1104/0BvO6QcBmjOGtcHnL 552w, https://miro.medium.com/max/1280/0BvO6QcBmjOGtcHnL 640w, https://miro.medium.com/max/1400/0*BvO6QcBmjOGtcHnL 700w" sizes="700px"/>

Photo by Thomas Kelley on Unsplash

Getters and Setters

JavaScript classes can have getter and setter functions. Getters, as the name suggests, is a method that lets us get some data from a class. Setters are methods that gives us the ability to set some fields of the class. We denote getter functions with the get keyword and setters with the set keyword. For example, we can write a class that has getters and setters like the following:

class Person {
  constructor(firstName, lastName) {
    this._firstName = firstName;
    this._lastName = lastName;
  }
  get fullName() {
    return `${this.firstName} ${this.lastName}`
  }
  get firstName() {
    return this._firstName
  }
  get lastName() {
    return this._lastName
  }
  sayHi() {
    return `Hi, ${this.firstName} ${this.lastName}`
  }
  set firstName(firstName) {
    this._firstName = firstName;
  }
  set lastName(lastName) {
    this._lastName = lastName;
  }
}

Then when we use the new keyword to construct a Person object, we can use them in the following way:

const person = new Person('Jane', 'Smith');
person.firstName = 'John';
person.lastName = 'Doe';
console.log(person.firstName, person.lastName)

Since we have the getter and setter functions, we can use them to set the data directly to set the data for firstName and lastName of the Person class. In the setter functions, which start with the keyword set, when we assign to a value them, it gest passed into the parameters and set in the member of the class. In the getter functions, which are denoted by get we return the member values which triggers the associated get function for the value.

JavaScript Inheritance

In JavaScript, we can create classes where the properties can be included in the properties of a child class.

So, we can have a high-level class that contains the properties that are common to all the child classes, and the child class can have its own special properties that are not in any other classes.

For example, if we have an Animal class with the common properties and methods, like name and the eat method, then the Bird class can just inherit the common properties in the Animal class. They don’t have to be defined in the Bird class again.

We can write the following to do inheritance in JavaScript:

class Animal {
  constructor(name) {
    this.name = name;
  }
  eat() {
    console.log('eat');
  }
}class Bird extends Animal {
  constructor(name, numWings) {
    super(name);
    this.numWings = numWings;
  }
}const bird = new Bird('Joe', 2);
console.log(bird.name)
bird.eat();

In the example above, we have the parent class, Animal, that has the eat method, which all classes that extends from Animal will have, so they don’t have to define eat again.

We have the Bird class which extends the Animal class. Note that in the constructor of the Bird class, we have the super() function call to call the parent’s class constructor to populate the properties of the parent class in addition to the properties of the child class.

Classes cannot extend regular objects, which cannot be constructed with the new keyword. If we want to inherit from a regular object, we have to use the Object.setPrototypeOf function to set a class to inherit from a regular object. For example:

const Animal = {
  eat() {
    console.log(`${this.name} eats`);
  }
};class Cat{
  constructor(name) {
    this.name = name;
  }
}class Chicken{
  constructor(name) {
    this.name = name;
  }
}Object.setPrototypeOf(Cat.prototype, Animal);
Object.setPrototypeOf(Chicken.prototype, Animal);let cat = new Cat('Bob');
let chicken = new Chicken('Joe');
cat.eat();
chicken.eat();

If we run the example code above, we can see Bob eats and Joe eats logged because we have inherited the eat function from the Animal object.

this Keyword

The this keyword allows us to access the current object’s properties inside the object, unless you’re using arrow functions.

As we can see from the above example, we can get the properties of the instance of the child and the parent class in the object.

Mixins

We can use mixins to do multiple inheritance in JavaScript. Mixins are templates for creating classes. We need mixins to do multiple inheritance because JavaScript classes can only inherit from one super class, so multiple inheritance isn’t possible.

For example, if we have a base class, we can define mixins to incorporate the members from multiple classes into one by composing the mixins by calling one and then pass the returned result into the next one as the argument, and so on, like the following:

class Base {
  baseFn() {
    console.log('baseFn called');
  }
}let classAMixin = Base => class extends Base {
  a() {
    console.log('classAMixin called');
  }
};let classBMixin = Base => class extends Base {
  b() {
    console.log('classBMixin called');
  }
};class Bar extends classAMixin(classBMixin(Base)) {}
const bar = new Bar();
bar.baseFn()
bar.a()
bar.b()

In the code above, we have the Base class which we pass into the classBMixin to get the b function into the Base class, then we call the classAMixin by passing in the result of classBMixin(Base) into the argument of the classAMixin to return the a function from classAMixin into the Base class and then return the whole class with all the functions from all the classes incorporated into one.

If we call all the functions above like we did by creating an instance of the Bar object and then call the baseFn, a, and b functions, we get:

baseFn called
classAMixin called
classBMixin called

This means that we have all the functions from the mixins incorporated into the new Bar class.

In JavaScript, classes are just syntactic sugar to make the prototypical inheritance of JavaScript clearer by letting us structure the code in a way that’s more like typical inheritance in class-based object oriented inheritance pattern. This means that we write classes to use the new keyword to create objects from the classes, but underneath the syntactic sugar, we are still using prototypical inheritance to extend objects. We can extend classes from objects and we can also use mixins to do multiple inheritance in JavaScript classes.