Categories
Modern JavaScript

Best of Modern JavaScript — Arrow Functions and New OOP 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 arrow functions and new OOP features in JavaScript.

Return Object Literals with Arrow Functions

If we want to return object literals with a single statement arrow function, we’ve to wrap the object we return with parentheses.

This way, it won’t be confused with blocks.

For example, we can write:

`const` `foo` `=` `x` `=>` `({` `bar:` 'baz' `});`

Then when we call foo() , we get { bar: 'baz' } returned.

Immediately-Invoked Arrow Functions

We can define immediately invoked arrow functions.

For example, we can write:

`(()` `=>` `{`
  `return` 'foo'
`})();`

to define an arrow function and call it.

We still use semicolons to determine immediately invoked arrow functions.

We’ve to wrap the arrow function with parentheses.

Parenthesizing Arrow Function with Expression Bodies

We wrap our arrow function body with an expression by writing:

`const` `value` `=` `()` `=>` `(foo());`

This is the same as writing:

`const` `value` `=` `()` `=>` `foo();`

Arrow Functions and bind

We can use arrow functions instead of bind so that we don’t have to set our own this .

This is useful if we just want to reference this from the outside.

For example, instead of writing:

`obj.on('click',` `this.handleEvent.bind(this));`

We can write:

obj.on('anEvent', event => this.handleEvent(event));

Then we take the value of this from the outside.

We can also use arrow functions as callbacks.

For instance, we can write:

const set1 = new Set([1, 2, 3]);
const set2 = new Set([3, 9, 2]);
const intersection = [...set1].filter(a => set2.has(a));

We computed the intersection by spreading set1 into an array and then calling filter to return the items that are also in set2 .

Then intersection would be [2, 3] .

Partial Evaluation

bind lets us do partial evaluation of a function and return it.

With arrow functions, we can write:

`const` add`1` `=` `y` `=>` `add(1,` `y);`

instead of writing:

function add(x, y) {
  return x + y;
}
const add1 = add.bind(undefined, 1);

to partially apply the function to add 2 numbers.

Arrow Functions vs Normal Functions

There’re significant differences between arrow functions and normal functions.

Arrow functions don’t have their own value of arguments, super, this, or new.target .

They also can’t be used as constructors.

It doesn’t have the hidden [[Construct]] property and the prototype property.

So we can’t use the new operator.

New OOP Features

There’re many new OOP features with modern JavaScript.

ES6 introduced the method definition shorthand:

const obj = {
  foo(x, y) {
    //...
  }
};

We can also add the property value shorthand.

For instance, we can write:

const firstName = 'mary';
const lastName = 'smith';

const obj = {
  firstName,
  lastName
};

The code

const obj = {
  firstName,
  lastName
};

is the shorthand for:

const obj = {
  firstName: firstName,
  lastName: lastName
};

We can use the shorthand from now on.

Also, we have the computed property keys syntax.

For example, we can write:

const prop = 'bar';
const obj = {
  [prop]: true,
  ['b' + 'ar']: 123
};

We pass in the key value into the square brackets to add them.

Conclusion

Arrow functions are useful in various contexts.

Modern JavaScript also has useful new features to make our lives easier.

Categories
Modern JavaScript

Best of Modern JavaScript — Variables and Destructuring

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 and destructuring.

The Global Object

The global object is in Node.js and browser-side JavaScript is more of a bug than a feature.

Its performance is poor.

Global objects are global variables.

In the global scope, var declarations declare global object properties.

Function declarations also declare global object properties.

let , const and class declarations don’t add properties to the global object when we create them in the global scope.

Function Declarations and Class Declarations

Function declaration are block-scoped like let .

They create properties of the global object within the global scope like var .

They’re also hosted so they’re independent of where their location in the code with regards to availability.

For instance, if we have:

{
  console.log(foo());
  function foo() {
    return 'hello';
  }
}

then we can call foo without errors since foo is hoisted.

Class declarations are block-scoped, don’t create properties on the global object, and aren’t hoisted.

They create functions underneath the hood, but they still aren’t hoisted.

For instance, if we have:

{
  const identity = x => x;
  const inst = new Foo();
  class Foo extends identity(Object) {}
}

Then we’ll get an ReferenceError because Foo isn’t defined before it’s invoked.

const versus let versus var

The best ways to declare variables are with const first.

They can’t be changed by reassignment so it’s harder to make mistakes with them.

However, we can still change the content inside whatever it’s assigned to it.

For example, we can write:

`const` `foo` `=` `{};`
`foo.prop` `=` `123;`

We can use const in loops:

for (const x of ['a', 'b']) {
   console.log(x);
 }

Since x is created for each iteration of the loop, we can use const with the loop.

Otherwise, we can use let when we need to change a variable later.

For example, we can write:

let counter = 0;
counter++;

let obj = {};
obj = { foo: 'bar' };

We should never use var and they should only appear in legacy code.

Destructuring

Destructuring is a convenient way to extract multiple values from arrays and objects.

The arrays and objects can be nested.

Object Destructuring

We can destructure objects by writing:

const obj = {
  firstName: 'james',
  lastName: 'smith'
};
const {
  firstName: f,
  lastName: l
} = obj;

We created an object obj and then we assigned it to the expression on the second statement.

This will get the firstName property and assign it to f .

And it’ll do the same with lastName and assign it to l .

Destructuring is also helpful with processing return values.

For example, we can write:

const obj = {
  foo: 'bar'
};

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

console.log(writable, configurable);

We called the Object.getOwnPropertyDescriptor method, which takes an object and a property key.

And it returns an object with writable and configurable properties.

So we can use destructuring to get the properties and assign them to variables.

Conclusion

We can follow some best practices with declaring variables.

Also, destructuring is handy for is getting variables from objects and arrays.

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.