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.

Categories
Modern JavaScript

Best of Modern JavaScript — Destructuring and Defaults

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

Array Patterns Work with Iterable Object

We can use array patterns like the rest operator with iterable objects.

For example, we can write:

const [x, ...y] = 'foo';

The x is 'f' and y is ['o', 'o'] .

Since strings are iterable, we can destructure them.

Destructuring also works with string code points.

For example, we can write:

const [x, y, z] = 'auD83DuDCA9c';

The x is 'a' , b is 'uD83DuDCA9' and z is 'c' .

Default Values

We can set default values when destructuring.

For example, we can write:

const [x = 13, y] = [];

Then x is 13 and y is undefined since the array is empty.

Since 13 is the default value of x , it’s assigned to it.

Therefore undefined triggers default values to be assigned.

Default values are computed on demand.

If they aren’t needed, then they won’t be used.

For example, if we have:

const {
  prop: y = fn()
} = foo;

Then that’s the same as:

let y;
if (foo.prop === undefined) {
  y = fn();
} else {
  y = foo.prop;
}

Default values can refer to other variables in the pattern.

For instance, we can write:

const [x = 13, y = x] = [];

Then x and y are both 13.

If we have:

const [x = 3, y = x] = [17];

Then x and y are both 17.

And if we write:

const [x = 3, y = x] = [7, 4];

Then x is 7 and y is 4.

The default value is only used when there’s no corresponding value on the right side.

We can also have default object values when destructuring.

For example, we can write:

const [{
  prop: x
} = {}] = [];

Then x is an empty object since there’s nothing on the right side.

If we have:

const {
  prop: x
} = {};

we get the same thing.

If we have:

const [{
  prop: x
} = {
  prop: 'foo'
}] = [];

Then x is foo since we assigned an object to the left side with the default value.

However, if we have:

const [{
  prop: x
} = {
  prop: 'foo'
}] = [{}];

Then x us undefined since we have an object in the array on the right side, so the default value won’t be assigned on the left side.

If we want to trigger the default value assignment, then we’ve to assign a default value to the property itself:

const [{
  prop: x = 'foo'
} = {}] = [{}];

Then x would be 'foo' since prop isn’t found on the right side.

Property Value Shorthands

Property value shorthand works like we expect with destructuring as we can see from the previous examples.

So:

const {
  x,
  y
} = {
  x: 1,
  y: 2
};

is the same as:

const {
  x: x,
  y: y
} = {
  x: 1,
  y: 2
};

Conclusion

Destructuring can be done with objects and arrays.

There’re many tricks with default values.

Categories
Modern JavaScript

Best of Modern JavaScript — 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 destructuring.

Array Destructuring

We can destructure arrays into variables by their position.

For example, if we have:

const arr = ['a', 'b'];
const [x, y] = arr;

Then x is 'a' and y is 'b' .

Returned values are also processed.

For example, we can write:

const [all, areaCode, officeCode, number] =
/^(ddd)-(ddd)-(dddd)$/
.exec('555-555-1212');

We destructure the matched patterns into their own variables.

exec returns an array with the whole string and the parts matched by patterns so we can assign to variables.

Where can Destructuring be Used?

Destructuring can be used in many places.

For instance, they can be used in arrays:

const [x] = ['a'];
let [x] = ['a'];
var [x] = ['a'];

We assign the array entry into the x variable.

We can also destructure in parameters.

For example, we can write:

`function` `f([x])` `{`
  //...
`}`
`f(['a']);`

And x is 'a' .

Also, we can destructure with the for-of loop:

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

We call arr.entries to get an array with an array of indexes and the element as the entries.

Patterns for Destructuring

If we want to destructure an object or area, we need the source and the target.

The right-hand side is the source and the left-hand side has the target.

For example, we can write:

const obj = {
  a: [{
    foo: 'bar',
    bar: 'abc'
  }, {}],
  b: true
};
const {
  a: [{
    foo: f
  }]
} = obj;

We restructured the nested object with the a array with an object with the foo property inside.

bar doesn’t exist on the right side so we set it to 'abc' as the default value.

b is set to true since it doesn’t exist on the right side.

We don’t have to assign everything to its own variable.

We can just pick what we need.

For example, we can write:

const { x } = { x: 1, y: 3 };

We just destructure x .

So x is 1.

We can do the same with an array:

const [x, y] = ['a', 'b', 'c'];

Then x is 'a' and y is 'b' .

Object Destructuring Coerce Values to Objects

We can use destructuring with primitive values.

For example, we can write:

const {
  length: len
} = 'foo';

And len is 3 since it gets the length property of the string.

We can also destructure numbers by writing:

const {
  toString: s
} = 123;

Then we get the toString method of Number.prototype as the value of s .

Failing to Object-Destructure a Value

A value may not be destructed in some cases.

If the value can’t be converted to an object with ToObject , then it can’t be destructured.

For instance, if we have:

const { prop: x } = undefined;
const { prop: y } = null;

Then we get TypeError in both cases since those properties on the left side don’t exist.

Conclusion

Destructuring can be used in many places.