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.

Categories
Modern JavaScript

Best of Modern JavaScript — Function 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 the core features of JavaScript.

Parameter Default Values

With ES6, we can add default values to function parameters.

This is great since we won’t have to worry about parameters being undefined again.

The old way would be to use the || operator to return a default value and assign it to the parameter:

function foo(x, y) {
  x = x || 0;
  y = y || 0;
}

With default parameters, we can write:

function foo(x = 0, y = 0) {
  //···
}

It’s much shorter and cleaner.

And default parameter values are only assigned when the parameter value is undefined .

Whereas with the || operator, any falsy value would return the default value, which we may not want.

Named Parameters

Named parameters are hard to deal with before ES6.

For instance, one way to do it is to use the || operator again:

function calcVolume(dimensions) {
  var length = dimensions.length || 1;
  var width = dimensions.width || 1;
  var height = dimensions.height || 1;
  //...
}

We get each property from the dimensions object and assign them to variables.

And we used the || operator to return the default value if the property is falsy.

With ES6, we can create named parameters much more easily, thanks to the destructuring syntax.

We can just write:

function calcVolume({
  length = 1,
  width = 1,
  height = 1
}) {
  //...
}

We have an object parameter with the length , width , and height properties destructured.

This way, we can have named properties without worrying about the order that they’re called.

We also assigned a default value to each property.

Making Parameter Optional

We can make parameters optional with the || operator before ES6.

For instance, we may write:

function calcVolume(dimensions) {
  var dimensions = dimensions || {};
  var length = dimensions.length || 1;
  var width = dimensions.width || 1;
  var height = dimensions.height || 1;
  //...
}

But with ES6, we don’t have to write this many lines of code anymore.

We can just write:

function calcVolume({
  length = 1,
  width = 1,
  height = 1
} = {}) {
  //...
}

We just assigned an array to the object parameter we’re destructuring to assign a default value for the object parameter.

arguments to Rest Parameters

arguments is the old way to get arguments from a function call before ES6.

It’s an iterable object but it’s not an array.

So most of the things we can do with arrays aren’t available.

On the other hand, rest parameters are returned as arrays, so we can get arguments in an array.

Instead of writing:

function logArgs() {
  for (var i = 0; i < arguments.length; i++) {
    console.log(arguments[i]);
  }
}

with ES5 or earlier syntax, we write:

function logArgs(...args) {
  for (const arg of args) {
    console.log(arg);
  }
}

We get the args array and looped through it with the for-of loop.

Conclusion

With default parameters values and rest parameters, we can deal with function parameters much more easily.

Categories
Modern JavaScript

Best of Modern JavaScript — Fills, Modules, and 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.

Array.prototype.fill

Since ES6, arrays have the fill instance method that lets us fill the array with the value we want.

For instance, we can write:

const arr = new Array(3).fill(undefined);

Then arr would be [undefined, undefined, undefined] .

Before ES6, there’s no good equivalent.

However, we can use apply as a hack to do the same thing:

var arr = Array.apply(null, new Array(3));

Then we get the same result.

However, there’s no way to fill it with any other value other than undefined .

fill treats holes as if they’re elements.

ES6 Modules

Modules are another great feature of modern JavaScript.

It’s introduced with ES6.

Before ES6, there were no native modules.

There’re various module systems like CommonJS.

In CommonJS, we can export multiple items in a module.

For instance, we can write:

math.js

function add(x, y) {  
  return x + y;  
}

function square(x) {
return x * x;
}

function diag(x, y) {  
  return sqrt(square(x) + square(y));  
}

module.exports = {
add: add,
square: square,
diag: diag,
};


We put our functions in the `module.exports` object to export them.

Then we can import them by writing:

var add = require(‘math’).add;
var diag = require(‘math’).diag;


We import the `math` module and get the properties from the module.

Also, we can require the whole module:

var meth = require(‘math’)


With ES6, we can use the `export` and `import` keywords to export and import modules items respectively.

For example, we can write:

`math.js`

export function add(x, y) {
return x + y;
}

export function square(x) {  
  return x \* x;  
}

export function diag(x, y) {
return sqrt(square(x) + square(y));
}


We just put the `export` keyword in front of our functions to export them.

To import them items, we can write:

import { add, diag } from 'math';


We import the `add` and `diag` functions from `math.js` with the the `import` keyword and curly brackets.

The imports with curly brackets are called named imports.

### Single Exports

With CommonJS, we do single exports by assign a value to `module.exports` :

`foo.js`

module.exports = function() {
//···
};


Then we can require it by writing:

var foo = require(‘foo’);


With ES6, we can use the `export default` keywords to do a single export.

For example, we can write:

`foo.js`

export default function() {
//···
}


Then we can import it by writing:

import foo from ‘foo‘;


### Numbers and Math

ES5 introduced hexadecimal numbers.

For instance, we can write:

0x1F


to write the hex form of decimal 31.

With ES6, binary numbers are introduced.

For example, we can write:

0b11


to write the binary number 3.

We can also write octal numbers by writing:

0o11


to write the decimal number 9 in octal form.

### Conclusion

ES6 or later have a native module system, which previous versions lack.

It also introduced useful array methods and new types of numbers.
Categories
Modern JavaScript

Best of Modern JavaScript — Errors, Strings, and Arrays

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 Custom Error Constructors

We don’t have to write our own error constructors anymore with ES6 or later.

Instead, we just create a subclass of Error so that we can create errors with consistent information like a stack trace.

For instance, instead of writing:

function FancyError() {
  var parent = Error.apply(null, arguments);
  copyProperties(this, parent);
}
FancyError.prototype = Object.create(Error.prototype);
FancyError.prototype.constructor = FancyError;

function copyProperties(target, source) {
  Object.getOwnPropertyNames(source)
    .forEach(function(propKey) {
      var desc = Object.getOwnPropertyDescriptor(source, propKey);
      Object.defineProperty(target, propKey, desc);
    });
};

to create a child constructor of Error , we write:

class FancyError extends Error {
}

Instead of creating the FancyError constructor by calling the Error constructor with some arguments, we just use extends to create the subclass.

The constructor has no properties of Error , so we’ve to copy them with the copyProperties function as we did in the first example.

We get the property descriptors with the Error object and we call Object.defineProperty to create the properties.

With the second example, we don’t have to do anything.

The properties will be inherited automatically.

Objects to Maps

We can use maps to store key-value pairs with ES6 or later.

Before ES6, all we have are plain objects,.

In ES6 or later, we can use the Map constructor to create our map by writing:

const map = new Map();
map.set('foo', 'bar');
map.set('count', 1);

We call the set method with the key and value as the first and second arguments to add or update their key-value pair.

String Methods

ES6 comes with new string methods that we can use.

The startsWith method lets us check if a string starts with a given substring.

For example, we can write:

str.startsWith('x')

Likewise, strings have the endsWith method to let us check if a string ends with a given substring.

For example, we can write:

str.endsWith('x')

They both return true if the start or end with the given substring.

The includes method lets us check if a string includes a given substring.

For instance, we can write:

str.includes('x')

We call includes on str to check if 'x' is included.

It returns true if it’s included and false otherwise.

We can use the repeat method to repeat a string.

For instance, we can write:

'foo'.repeat(3)

to repeat 'foo' 3 times.

Array Methods

ES6 comes with new array methods to make manipulating arrays easier.

We can use the findIndex method to return the index of an array entry.

It takes a callback with the condition we’re looking for.

For instance, we can write:

const arr = ['foo', NaN, 'bar'];
arr.findIndex(x => Number.isNaN(x));

We passed in a callback to check if NaN is in the arr array.

This is more useful than indexOf since it doesn’t take a callback to check for.

The Array.from or the spread operator lets us convert iterable objects to arrays.

Before ES6, all we have is the slice method.

Instead of writing:

var arr1 = Array.prototype.slice.call(arguments);

in ES5 or earlier, we can write:

const args = Array.from(arguments);

or

const args = [...arguments];

with ES6 or later.

Conclusion

ES6 comes with many useful enhancements for strings, arrays, and errors.

It’s hard to live without them.