Categories
Modern JavaScript

Best of Modern JavaScript — Object-Oriented Programming

Since 2015, JavaScript has improved immensely.

It’s much more pleasant to use it now than ever.

In this article, we’ll look at the core features of JavaScript.

No More apply()

Before ES6, we’ve to use apply to call a function with an array spread as arguments.

With ES6 or later, we can use the spread operator instead of calling a function.

It’s easier since we don’t have to set the value of this .

For instance, instead of writing:

Math.max.apply(Math, [3, 2, 1, 6])

We write:

Math.max(...[3, 2, 1, 6])

It’s much easier and we don’t have to worry about an extra argument.

With the push method, we can push multiple items with the apply method with ES5 or later:

var arr1 = [1, 2];
var arr2 = [3, 4];

arr1.push.apply(arr1, arr2);

The first argument is the array instance, which we set as the value of this .

With ES6, we can instead write:

const arr1 = [1, 2];
const arr2 = [3, 4];
arr1.push(...arr2);

With the spread operator, we don’t have to do that anymore.

concat() can be Replace by Spread

We can replace concat with the spread operator with ES6.

Instead of writing in ES5:

var arr1 = [1, 2];
var arr2 = [3, 4];
var arr3 = [5, 6];

console.log(arr1.concat(arr2, arr3));

We write:

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

console.log([...arr1, ...arr2, ...arr3]));

We merged all 3 arrays together into with the spread operator with ES6 or later.

Both expressions return a new array with elements of all 3 arrays in it.

Function Expressions in Object Literals

We don’t to write out the function keyword for object methods anymore with ES6 or later.

It has a handy syntax for defining methods in objects.

In ES5 or earlier, we’ve to write:

var obj = {
  eat: function() {
    //...
  },
  drink: function() {
    this.eat();
  },
}

to define methods in an object.

With ES6 or later, we can shorten it to:

const obj = {
  eat() {
    //...
  },
  drink() {
    this.eat();
  },
}

We removed the function keyword and colon so it’s much shorter.

They do the same thing.

Constructors to Classes

ES6 provides us with a handy class syntax to create constructor functions.

In ES5, we’ve to write:

function Person(name) {
  this.name = name;
}
Person.prototype.greet = function() {
  return 'Hello ' + this.name;
};

Instance methods are defined by adding a property to the prototype property.

In ES6, we can use the class syntax by writing:

class Person {
  constructor(name) {
    this.name = name;
  }
  greet() {
    return `Hello ${this.name}`;
  }
}

The constructor has what’s inside the constructor function.

And greet is still the method in Person ‘s prototype property.

It’s just written differently.

Subclasses

Creating subclasses is complicated with ES5. If we have super constructors and properties, it’s even harder.

To create a parent and child class with ES5 or earlier, we write:

function Person(name) {
  this.name = name;
}
Person.prototype.greet = function() {
  return 'Hello ' + this.name;
};

function Student(name, number) {
  Person.call(this, name);
  this.number = number;
}
Student.prototype = Object.create(Person.prototype);
Student.prototype.constructor = Student;
Student.prototype.describe = function() {
  return Person.prototype.greet.call(this) + '-' + this.number;
};

We create the Person constructor the same way.

Then to create the Student subclass, we call the Person constructor with the call method with this and name to call the parent constructor.

Then we initialize the instance data of Student .

And then we create the prototype of Student by calling Object.create with Person ‘s prototype.

Then we set the constructor property to Student to get the right constructor.

Then we add our instance methods to the Student class.

It’s complex and hard to understand.

With the class syntax, we write:

class Person {
  constructor(name) {
    this.name = name;
  }
  greet() {
    return `Hello ${this.name}`;
  }
}

class Student extends Person {
  constructor(name, number) {
    super(name);
    this.number = number;
  }
  describe() {
    return `${Person.prototype.greet.call(this)}-'${this.number}`;
  }
}

All we had to do us to use the extends keyword to indicate that it’s a subclass.

And we call super to call the parent constructor.

Conclusion

Object-oriented programming is easier with ES6 or later.

Categories
Modern JavaScript

Best of Modern JavaScript — Numbers

Since 2015, JavaScript has improved immensely.

It’s much more pleasant to use it now than ever.

In this article, we’ll look at the core features of JavaScript.

New Number Properties

ES6 introduced some new number properties.

New static properties of Number includes Number.EPISLON for comparing floating-point number rounding tolerance.

Number.isInteger checks whether num is an integer.

If we write:

Number.isInteger(2.05)

then it returns false .

If we write:

Number.isInteger(2)

then it returns true .

This also works for negative numbers.

There’s also the isSafeInteger method that determines whether a JavaScript integer is safe.

A safe integer is one that can be represented within the signed 53-bit range without losing precision.

For instance, if we write:

Number.isSafeInteger(2)

then it returns true .

There’s also the Number.MIN_SAFE_INTEGER that has the min value for the safe integer.

And there’s the Number.MAX_SAFE_INTEGER value to get the max value of a safe integer.

ES6 also comes with the Number.isNaN method to check whether num is the value NaN .

Unlike isNaN , it doesn’t coerce its argument into a number before doing the check.

So if we write:

isNaN('foo')

It returns true .

On the other hand, if we write:

Number.isNaN('foo')

Then it returns false .

The Number object also has the Number.isFinite , Number.parseFloat , and Number.parseInt methods.

Number.isFinite checks whether a number is finite.

Number.parseFloat converts a non-number to a floating-point number.

And Number.parseInt converts a non-number to an integer.

These are mostly the same as their global equivalents.

Math Methods

ES6 also added new methods to the Math object.

The Math.sign method returns the sign of a number.

It returns -1 if the number is negative.

It returns 0 if the number is 0.

And it returns 1 if the number is positive.

For instance, we can write:

Math.sign(-10)

and get -1.

If we write:

Math.sign(0)

we get 0.

And if we have:

Math.sign(3)

We get 1.

The Math.trunc method removes the decimal portion of a number.

So if we have:

Math.trunc(2.1)

or

Math.trunc(2.9)

We get 2.

And if we have:

Math.trunc(-2.1)

or:

Math.trunc(-2.9)

We get -2.

The Math.log10 method computes the log with base 10 of a number.

For example, we can write:

Math.log10(1000)

and get 3.

Math.hypot calculates the square root of the number of squares of its arguments.

For example, if we have:

Math.hypot(1, 1)

We get 1.4142135623730951 .

Integer Literals

ES5 introduced hex integers.

So we can write:

0x1F

and get 31 in decimal.

ES6 introduced more kinds of numbers.

Binary literals are one of them.

For example, if we write:

0b111

then we get 7 in decimal.

It also introduced octal literals.

For instance, we can write:

0o101

and get 65 in decimal.

We can use the toString method with numbers with bases that aren’t 10.

For example, we can write:

8..toString(8)

And we get 10 in decimal.

The argument is the radix, or the base to treat the number as.

To call a method with a number literal, we’ve to include an extra dot.

Conclusion

Numbers have can be represented in variables and converted with ES6 or later.

Categories
Modern JavaScript

Best of Modern JavaScript — Numbers and Strings

Since 2015, JavaScript has improved immensely.

It’s much more pleasant to use it now than ever.

In this article, we’ll look at the core features of JavaScript.

New Number Methods

Number.parseInt can be used with the new integer literals.

We can pass in the radix, or the base to treat the number as in the 2nd argument.

For instance, we can write:

Number.parseInt('0o8', 8)

and get 0.

Number.parseInt also works with hexadecimal numbers.

For example, we can write:

Number.parseInt('0xF', 16)

and get 15.

Using 0 Instead of 1 with Exponentiation and Logarithm

The Math object comes with new methods to let us compute natural logarithms.

We can use the Math.expm1(x) method calculate Math.exp(x) — 1 .

So we can write:

Math.expm1(10)

and get 22025.465794806718.

There’s also a method that’s the inverse of Math.expm1 , which is Math.log1p(x) .

It’s the same as Math.log(1 + x) .

For instance, if we have:

Math.log1p(0)

Then we get 0.

Logarithms to Base 2 and 10

We have the Math.log2(x) which calculates the logarithm with base 2.

For example, we can write:

Math.log2(8)

which returns 3.

The Math.log10(x) method returns the log of x with base 10.

For example, if we write:

Math.log10(1000)

then we get 3.

String Features

ES6 comes with some handy string features.

It comes with template literals, which lets us interpolate expressions into a string instead of concatenating strings with other expressions.

For example, we can write:

const firstName = 'james';
const lastName = 'smith';
console.log(`Hello ${firstName} ${lastName}!`);

Then we get 'Hello james smith!’ .

They’re delimited by backticks instead of single or double-quotes.

They can also span multiple lines unlike regular string literals.

For example, we can write:

const multiLine = `
<!DOCTYPE html>
<html lang="en">

<head>
 <meta charset="utf-8">
 <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
 <meta name="theme-color" content="#000000">
 <link rel="manifest" href="%PUBLIC_URL%/manifest.json">
 <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
 <title>React App</title>
</head>

<body>
 <div id="root"></div>
</body>

</html>`;

And the HTML string will span multiple lines.

Code Point Escapes

ES6 lets us escape any code points, even though they are beyond 16 bits.

For example, we can write:

console.log('u{1F680}');

to escape 1F680 , which is beyond the 16-bit limit.

Iterating Over Strings

String are iterable objects.

Therefore, we can loop through them with the for-of loop.

For example, we can write:

for (const ch of 'abc123') {
  console.log(ch);
}

Then we get the characters logged as they’re being looped through.

The for-of loop splits the string along code point boundaries, so the string it returns can return 1 or 2 characters.

For example, if we have:

for (const ch of 'xuD83DuDC69') {
  console.log(ch.length);
}

Then ch will be 1 for the first character and 2 for the 2nd.

We can use the spread operator to split the string along the code points into an array and use the length property to get the length.

For example, we can write:

[...'xuD83DuDC69'].length

and we get 2.

Conclusion

ES6 introduced many number and string features that we may have missed.

Categories
Modern JavaScript

Best of Modern JavaScript — More Core Features

Since 2015, JavaScript has improved immensely.

It’s much more pleasant to use it now than ever.

In this article, we’ll look at the core features of JavaScript.

Function Expressions to Arrow Functions

Since ES6, arrow functions let us create functions that are shorter and don’t bind to its own this .

This makes creating new functions easier and cleaner.

For instance, instead of writing:

function Button() {
  var _this = this;
  var button = document.getElementById('myButton');
  button.addEventListener('click', function() {
    console.log('clicked');
    _this.handleClick();
  });
}

Button.prototype.handleClick = function () {
  //···
};

We create the _this variable and set it to this outside the function so that we can use it in the click listener.

This isn’t ideal since we can easily confuse different values of this from different levels.

With arrow functions, we can write:

function Button() {
  const button = document.getElementById('button');
  button.addEventListener('click', () => {
    console.log('clicked');
    this.handleClick();
  });
}

We just pass in arrow function and we can use the this value from the Button function.

We don’t have to set the Button ‘s this to a variable before using it.

Arrow functions are great for callbacks that return an expression.

For instance, instead of writing:

var arr = [1, 2, 3];
var cubes = arr.map(function (x) { return Math.pow(x, 3) });

We write:

const arr = [1, 2, 3];
const cubes = `arr.map(x` `=>` `x` `** 3);`

It’s much shorter and cleaner.

Multiple Return Values

With modern JavaScript, we can handle multiple return values with ease.

We can return an object or array and destructure the entries into variables.

Before ES6, there’s no destructuring assignment, So we’ve to write something like:

var matchObj =
  /^(ddd)-(ddd)-(dddd)$/
  .exec('222-112-2222');
var areaCode = matchObj[1];
var officeCode = matchObj[2];
var stationCode = matchObj[3];

With ES6 or later, we can write:

const matchObj =
  /^(ddd)-(ddd)-(dddd)$/
  .exec('222-112-2222');
const [_, areaCode, officeCode, stationCode] = matchObj;

We used destructuring to assign each entry to a variable.

This lets us saves many lines of code.

Multiple Return Values via Objects

We can also return an object and destructure the properties into variables.

For instance, we can write:

const obj = {
  foo: 'bar'
};

const {
  writable,
  configurable
} =
Object.getOwnPropertyDescriptor(obj, 'foo');

We get the writable and configurable properties from the getOwnPropetyDescriptor method and assigned to variables.

From for and forEach() to for-of

The for-of loop can loop through any iterable object.

It’s less versatile than for , but faster and more versatile than forEach .

forEach are only available with arrays and Nodelists.

To use a for loop, we write:

var arr = ['a', 'b', 'c'];
for (var i = 0; i < arr.length; i++) {
  var letter = arr[i];
  console.log(letter);
}

With forEach , we can write:

var arr = ['a', 'b', 'c'];
arr.forEach(function(letter) {
  console.log(letter);
});

With for-of, we can write:

const arr = ['a', 'b', 'c'];
for (const [index, letter] of arr.entries()) {
  console.log(index, letter);
}

As we can see, we can use destructuring with the for-of loop.

This isn’t available with other loops or forEach .

Since arr.entries() returns an array with each entry being an array with the index and entry, we can get them both with destructuring.

Conclusion

Modern JavaScript has great features including destructuring and the for-of loop.

Categories
Modern JavaScript

Best of Modern JavaScript — Loops and Parameters

Since 2015, JavaScript has improved immensely.

It’s much more pleasant to use it now than ever.

In this article, we’ll look at JavaScript variables.

let and const in Loop Heads

We can use let and const in loop heads with the for, for-in, and for-of loops.

With the for loop, we can write:

const arr = [];
for (let i = 0; i < 3; i++) {
  arr.push(i);
}

We can write use let to make i only available in the block.

This is better than var which make the loop available inside the whole function:

const arr = [];
for (var i = 0; i < 3; i++) {
  arr.push(i);
}

const works like var , but we can’t change the value of i .

For example, we can’t write:

for (const i = 0; i < 3; i++) {
  console.log(i);
}

since we change the value of i .

We can also use the for-of loop to get the item from a loop.

For instance, we can write:

const arr = [];
for (let i of [0, 1, 2]) {
  arr.push(i);
}

to get the value.

We can also use const since we don’t change the array entry variable:

const arr = [];
for (const i of [0, 1, 2]) {
  arr.push(i);
}

The var variable will make the variable available outside the loop:

const arr = [];
for (var i of [0, 1, 2]) {
  arr.push(() => i);
}

The for-in loop works like the for-of loop.

Parameters as Variables

We can write parameters we let variables inside blocks if they don’t overlap with parameters.

For example, we can’t write:

function func(arg) {
  let arg;
}

since both args have the same scope.

We’ll get the ‘Uncaught SyntaxError: Identifier ‘arg’ has already been declared’ error.

However, we can write:

function func(arg) {
  {
    let arg;
  }
}

since arg is in the block.

However, with var , both are valid:

function func(arg) {
  var arg;
}

function func(arg) {
  {
    var arg;
  }
}

They both do nothing and they arg in the parameter and the block share the same scope.

If we have default parameters, then they’re treated as parameters.

For instance, we can write:

function foo(x = 1, y = x) {
  return [x, y];
}

since x is defined before y .

But we can’t write:

function bar(x = y, y = 2) {
  return [x, y];
}

since y isn’t defined when we assigned it to x .

Therefore, we treat default parameters as let variables.

They don’t see the scope of the body.

For instance, if we have:

const foo = 'bar';

function bar(func = x => foo) {
  const foo = 'baz';
  console.log(func());
}

bar()

Then we get bar .

The function in the function parameter takes foo from the outside and return it.

The foo inside bar has its own scope.

Conclusion

We can use let and const in loop heads and functions.

Default parameters are also treated as let variables.