Nona Blog

Shape-Shifting Cats — Intro to ES6

When I joined the Nona team I knew little to nothing about JavaScript. I had a background in Java, a language in which we define our variable’s type — a dog is a dog; it can’t suddenly shape-shift and become a cat. This is not the case in JavaScript (JS) — a variable could be assigned a string value in one line and to a Boolean in the next (it’s not good practice but it is possible).

When I joined the Nona team I knew little to nothing about JavaScript. I had a background in Java, a language in which we define our variable’s type — a dog is a dog; it can’t suddenly shape-shift and become a cat. This is not the case in JavaScript (JS) — a variable could be assigned a string value in one line and to a Boolean in the next (it’s not good practice but it is possible).

var x; // x is undefined
var x = 42; // Now x is a Number (or the meaning of life - Hitch Hikers Guide to the Galaxy)
var x = true; // Now x is a Boolean

If this makes you feel a little uneasy or as through the fundamental laws of the universe aren’t what you thought they were, don’t stress, I’ve got you. We’ll also go through ES6 syntax. So if you’re unfamiliar with it, this post is also for you.

ES6: let & const

JS is dynamically typed, not statically typed like Java/C++. Here you can only have let and const (in ES5 and below we don’t differentiate between these two, we just declare everything as var). You use const if you plan on never re-assigning this “variable” (effectively turning it into a constant) and let if you plan to re-assign it.

In JS you can actually define a function as a const and it can take input parameters and give you a different output. WTF right? However, if you think about it a bit more and perhaps squint a little, you might realise that it’s just the function that is constant not the output but, more on this later.

Read more about let here

Read more about const here

ES6: Arrow Functions

Arrow functions, introduced in ES6, are a different way of creating functions in JavaScript. Besides shorter syntax, they offer advantages when it comes to keeping the scope of the this keyword.

Let’s start with looking at how a function was (and can still be) declared in JS.

function callMe(name){
    console.log(name);
}

Quick aside for those still hanging onto their strongly typed language heritage — Input parameters for a function (thing between the brackets) don’t need to be initialised with let or const.

The above function could also be written differently — remember how I said a function could also be a constant, in how it operates:

const callMe = function(name){
    console.log(name);
}

Now with an arrow function! Seems simple enough, right?

const callMe = (name) => {
    console.log(name);
}

Important:

When having no arguments, you have to use empty parentheses in the function declaration:

const callMe = () => {
    console.log(‘Douglas Adams’);
}

When having exactly one argument, you may leave out the parentheses:

const callMe = name => {
    console.log(name);
}

That’s equal to:

const returnMe = name => {
    return name;
}

Read more about arrow functions here

Exports & Imports

In React projects (and in all modern JS projects), it is good practice to split your code across multiple JavaScript files — so-called modules. You do this, to keep each file/module focused and manageable.

To access functionality in another file, you need statements that: export (make it available) and import (pull it in/access it). Export statements are typically found at the bottom of a file and import statements at the top.

Exports

You got two different types of exports:

default (unnamed): export default …;

named: export const someData = …;

A file can only contain one default export but an unlimited amount of named exports.

Imports

You can import default exports like this:

import someNameOfYourChoice from ‘./path/to/file.js’;

Surprisingly, someNameOfYourChoice is totally up to you. However, named exports have to be imported by the name given to them in the exporting file:

Surprisingly, someNameOfYourChoice is totally up to you. However, named exports have to be imported by the name given to them in the exporting file:

import { someData } from ‘./path/to/file.js’;

When importing named exports, you can import all named exports at once, with the following star(*) syntax:

import * as upToYou from ‘./path/to/file.js’;

upToYou is — well — up to you, and simply bundles all exported variables and/or functions in one JS object.

For example, if you export const someData; found in (./path/to/file.js) you can access it with the import statement above and then the named export like this: upToYou.someData

Classes

Classes are a feature which basically replace constructor functions and prototypes. You can define blueprints for JavaScript objects with them. (If you come from the world of drinking coffee without scripts, you might recognise that these look similar to POJO (Plain Old Java Objects))

class Person {
    constructor () {
        this.name = ‘Douglas Adams’;
    }
}
const person = new Person();
console.log(person.name); //logs: ‘Douglas Adams’

In the above example, not only the class but also a property of that class (name) is defined. This is the “old” syntax for defining properties. In modern JS projects, you can use a more convenient way of defining class properties:

class Person {
    name = ‘Douglas Adams’;
}
const person = new Person();
console.log(person.name); //logs: ’Douglas Adams’

You can also define methods like this:

class Person{ 
    name = ‘Douglas Adams’;
    printAuthorsName () {
        console.log(this.name); // this is required to refer to the class!
    }
}
const person = newPerson();
person.printAuthorsName();

Or like this:

class Person{
    name = ‘Douglas Adams’;
    printAuthorsName = () => {
    console.log(this.name);
    }
}
const person = new Person();
person.printAuthorsName();

The second approach has the same advantage as all arrow functions — The this keyword doesn’t change its reference.

Inheritance (Bonus Round)

You can also use inheritance when using classes:

class Human{
    species = ‘human’;
}

const person = new Person();
class Person extends Human{
    name = ‘Douglas Adams’;
    printAuthorsName = () => {
        console.log(this.name);
    }

const person = new Person();
person.printAuthorsName(); // prints 'Douglas Adams'
console.log(person.species); // prints ’human’

Spread & Rest Operator

The spread and rest operators actually use the same syntax:

Yes, that is the operator — just three dots. It’s usage determines whether you’re using it as the spread or rest operator.

Spread Operator:

Both arrays and objects are reference types (and not primitives), copying them safely (i.e. preventing future mutation of the copied original) can be tricky. With the spread operator you have an easy way of creating a (shallow!) clone of the object or array.

The spread operator allows you to pull elements out of an array (split the array into a list of its elements) or pull the properties out of an object.

On an array:

const oldArray = [1, 2, 3];
const newArray = […oldArray, 4, 5]; // This now is[1, 2, 3, 4, 5];

On an object:

const oldObject = {
    name: ‘Douglas Adams’
};
const newObject = {
    …oldObject,
    age: 42
};

newObject would then be:

{
    name: ‘Douglas Adams’,
    age: 42
}

Rest Parameters (Bonus Round)

A rest operator allows for multiple arguments to be inputted into a function.

function numOfArgs(...theArgs) {
    console.log(theArgs.length);
}

numOfArgs();  //logs: 0
numOfArgs(5); //logs: 1
numOfArgs(5, 6, 7); //logs: 3

Array Operators

Let’s start this section with something that you should have encountered before: a for loop iterating through an array.

const arr = [1, 2, 3, 4];
let plus5 = [];
for(var i = 0; i < arr.length; i++) {
    plus5[i] = arr[i] + 5;
}

//OR WITH FOREACH SYNTAX

arr.forEach((element, index) => {
    plus5[index]= element + 5
});

//Result: plus5 = [6,7,8,9]

This code results in a new array where each value is 5 more than it was in the old array. While this code works, there is a much easier way to achieve the same result — using the map() function.

Map Definition & Syntax

The map() method is used to apply a function on every element in an array and a new array is then returned.

let newArr = oldArr.map((element, index, arr) => {
    // return element to new Array
});
  • newArr — the new array that is returned
  • oldArr — the array to run the map function on
  • element — the current value being processed
  • index — the current index of the value being processed
  • arr — the original array

Example

let plus5 = arr.map((element) => {
    return element + 5;
});
// Result: plus5 = [6,7,8,9]

In JS function arguments are optional. So if you only need the element in your function (an arrow function in this case) you only need to pass in the element argument. However, if you need the index you’ll need to pass in element then index e.g.(element, index)even if you don’t need element. JS only knows that index is index because it is the second argument. The same would go for arr — you would need to pass in all 3 arguments to get it.

For more on map() read this

Filter

Let’s say we wanted a new array that only contains the even numbers from another array. Written with a for loop it would look like this:

let arr = [1, 2, 3, 4, 5, 6];
let even = [];
for(var i = 0; i < arr.length; i++) {
    if (arr[i] % 2 === 0) even.push(arr[i]);
}
// Result: even = [2,4,6]

But there is a lovely operator that does this for us, enter filter()

Filter Definition & Syntax

The filter() method returns a new array created from all elements that pass a certain test preformed on an original array.

let newArr = oldArr.filter(callback);
  • newArr — the new array that is returned
  • oldArr — the array to run the filter function on
  • callback — the function used to test each element of the oldArr. Returning true keeps the element, returning false to not keep it.

Example:

let arr = [1,2,3,4,5,6];
let even = arr.filter(element => {
    return element % 2 === 0;
});
// Result: even = [2,4,6]

For more on filter() read this

One other useful operator is reduce() this won’t be covered in this post but here’s a great article.

Destructuring (Bonus Round)

Destructuring allows you to easily access the values of arrays or objects and assign them to variables.

Here’s an example for an array:

const array = [1,2,3];
const [a, b] = array;
console.log(a); // logs: 1
console.log(b); // logs:  2
console.log(array); // logs: [1,2,3]

And here for an object:

const myObj = {
    name: ‘Douglas Adams’,
    age: 42
}
const {name} = myObj;
console.log(name); // logs: ‘Douglas Adams’
console.log(age);// logs: undefined
console.log(myObj);// logs: {name:’Douglas Adams’, age:42}

Destructuring is very useful when working with function arguments. Consider this example:

const printAuthorsName = (personObj) => {
    console.log(personObj.name);
}
printName({name : ’Douglas Adams’, age : 42}); // logs: ‘Douglas Adams’

Here, we only want to print the name in the function but we pass a complete person object to the function. This isn’t a big issue but we have to then call personObj.name inside of our function (Line 2 above). We can condense this code with destructuring:

const printAuthorsName = ({name}) => {
    console.log(name);
}
printAuthorsName({name : ’Douglas Adams’, age : 42}); // logs: ’Douglas Adams’)

By destructuring, we simply pull out the name property and store it in a variable/argument named name which we then can use in the function body.

Because no tutorial would be complete without stating our goal of WEB DOMINATION!
Adrian Bunge

Adrian Bunge

Add comment