Categories
Modern JavaScript

Best of Modern JavaScript — Callable Entities and Array Like Objects

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 spread operator and functions in JavaScript.

Converting Iterable or Array-Like Objects to Arrays

We can convert iterable or array-like objects into arrays.

We can do this with the spread operator.

For example, we can write:

const set = new Set([1, 2, 6]);
const arr = [...set];

Then arr is [1, 2, 6] .

We can turn other iterable objects into an array the same way.

For instance, we can write:

const obj = {
  *[Symbol.iterator]() {
    yield 1;
    yield 2;
    yield 3;
  }
};
const arr = [...obj];

Then arr is [1, 2, 3] .

If we have something that’s not iterable, but array-like, we can convert it to an array with the Array.from method.

For example, we can write:

const arrayLike = {
  '0': 'a',
  '1': 'b',
  '2': 'c',
  length: 3
};

`const` `arr` `=` `Array.from(arrayLike);`

Then arr is [“a”, “b”, “c”] .

We can’t use the spread operator to spread arrayLike into an array.

If we try, then we get a TypeError.

Callable Entities in ES6

There are several types of callable entities with ES6 or later.

They’re functions, methods, and constructors.

A function isn’t part of any object, so we call them by writing foo(1) .

Methods are functions that are properties of an object.

For example, we can call them by writing obj.foo(81) .

Constructors are functions that we can an object that’s an instance of the constructor.

For example, we can write new Constr(8) to call it.

super calls are restricted to specific locations.

We can call super method calls by writing:

super.method('abc')

And we can call the super constructor by writing:

super(8)

We can do this if a class is a subclass created by using the extended keyword.

For example, we can write:

class Person {
  constructor(name) {
    this.name = name;
  }

  greet() {
    console.log(`hello ${name}`)
  }
}

class Employee extends Person {
  constructor(name, title) {
    super(name);
    this.title = title;
  }

  greet() {
    super.greet()
  }
}

We called the super constructor to call the Person constructor from the child Employee class.

And we called the Person instance’s greet method with super.greet() .

Prefer Arrow Functions as Callbacks

Callbacks usually don’t need their own values of this , so we can use arrow functions to define them.

It’s also more compact so we type less.

Some APIs use this as an implicit parameter for their callbacks.

This prevents us from using arrow functions as callbacks.

For example, addEventListener may use this in their callback:

document.addEventListener('click', function() {
  console.log(this);
})

this is the document object.

However, we can change this to document so that we can use arrow functions for our callback:

document.addEventListener('click', () => {
  console.log(document);
})

Prefer Function Declarations as Standalone Functions

If we have standalone functions, we should use function declarations so that we can use them anywhere.

So we can write:

function foo(arg1, arg2) {
  // ···
}

They can be used anywhere and they look like generator functions.

However, we usually don’t need this for standalone functions.

If we don’t need their own this inside the function, then we can set the arrow function to a variable.

For example, we can write:

`const` `foo` `=` `(arg1,` `arg2)` `=>` `{`
  //...
`};`

Then we don’t have to worry about the value of this inside the function.

Conclusion

We can convert non-iterable array-like objects to arrays.

Also, there’re various callable entities with modern JavaScript.

Categories
Modern JavaScript

Best of Modern JavaScript — Callable Entities

Since 2015, JavaScript has improved immensely.

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

In this article, we’ll look at functions and classes in JavaScript.

Generator Functions

Generator functions are new to ES6.

They’re denoted by the function* keyword.

The value of this in a generator function is handled the same way as in traditional functions.

We can’t call a generator function as a constructor.

If we do, we’ll get a TypeError .

We can define them as function expressions:

const foo = function*(x) {
  //...
};

Or we can define them as function declarations:

function* foo(x) {
  //...
};

Generator functions only return generator objects.

Method Definitions

Method definitions are functions that are defined in object literals.

For instance, we can write:

const obj = {
  add(x, y) {
    return x + y;
  },

  sub(x, y) {
    return x - y;
  },
};

We defined the add and sub methods in our obj object.

Also, we can define methods in classes.

For example, we can write:

class Calc {
  add(x, y) {
    return x + y;
  }

  sub(x, y) {
    return x - y;
  }
}

There’re no commas after each method in a class.

We can call super or methods of super in class methods.

Generator Method Definitions

Generators can be added as methods in an object or a class.

The syntax is the same as regular methods.

For instance, we can write:

const obj = {
  * gen() {
    //...
  },
};

In classes, we can write:

class Gen{
  * gen() {
    //...
  }
}

We can use this and super like any other class methods.

Arrow Functions

Arrow functions don’t have their own value for the following variables:

  • arguments
  • super
  • this
  • new.target

They take the value of them from the surrounding scope.

They can’t be used as constructor.

If we try to use it with the new keyword, we’ll get a TypeError.

Classes

Classes is easier to use the syntax for constructor functions.

We can define them with methods by writing:

class Point {
  constructor(x, y) {
    this.x = x;
    this.y = y;
  }

  toString() {
    return `${this.x}, ${this.y}`;
  }
}

We create the Point class with the toString method and a constructor.

To create a child class of the Point class, we can use the extends keyword:

class ThreeDPoint extends Point {
  constructor(x, y, z) {
    super(x, y);
    this.z = z;
  }

  toString() {
    return `${super.toString()}, ${this.z}`;
  }
}

We called super in the constructor to call the parent Point constructor.

And we called super.toString() to call the Point instance’s toString method.

Classes can’t be called as functions or methods.

Constructor follows subclassing syntax.

The base class has its own this .

And subclasses has a this with the properties of the parent class instance and its own instance.

We’ve to call super before we can access this .

Dispatched and Direct Method Calls

We can call dispatch method call by calling a method on the instance.

For instance, calling arr.splice(1, 1) is calling splice on the arr array instance.

A direct call is calling a function with call or apply .

For instance, we can write:

Array.prototype.splice.call(arr, 1, 1)

or:

Array.prototype.splice.apply(arr, [1, 1])

to call splice with arr as the value of this and the arguments either as arguments or in an array.

Conclusion

There’re many kinds of callable entities in JavaScript. They include functions, methods, generator functions, and classes.

Categories
Modern JavaScript

Best of Modern JavaScript — Arrow Function Syntax

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 in JavaScript.

Arrow Function Syntax

We can create arrow functions by using the following syntax:

() => {
  //...
}

or

x => {
  //...
}

or

(x, y) => {
  //...
}

If we have one parameter, then we don’t need the parentheses.

We can specify the body by writing:

x => {
  return x * x
}

or:

x => x * x

Arrow functions are great for reducing the verbosity of our code.

For example, we can reduce:

const squares = [1, 2, 3].map(function (x) { return x * x });

to:

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

Omitting Parentheses Around Single Parameters

We can remove the parentheses if we have a single parameter that doesn’t have a default value.

For instance, we can write:

[1, 2, 3].map(x => 4 * x)

If we have anything else, we need the parentheses.

We can define default parameters by writing:

[1, undefined, 3].map((x = 2) => 4 * x)

Propagating Variable Values

Variable values can be propagated statically or dynamically.

Static propagation is getting variable values from outside the function:

const x = 'foo';

function foo(y) {
  return x;
}

Dynamic propagation is getting variable value from parameters:

`function` `bar(arg)` `{`
    `return` `arg;` `_// value received dynamically_`
`}`

Arrow functions don’t bind to their own special values.

These special values include:

  • arguments
  • super
  • this
  • new.target

Arrow Functions Bind Loosely

Arrow functions bind loosely.

If we have:

`const` `foo` `=` `x` `=>` `(x` `%` 5`)` `===` `0` `?` `x` `:` `0;`

Then x => (x % 5) === 0 is considered to be part of the function.

To keep the whole expression together as the function, we can wrap them in the parentheses:

`const` `foo` `=` `x` `=>` (`(x` `%` 5`)` `===` `0` `?` `x` `:` `0);`

We should wrap whatever we want in the function wither parentheses.

No Line Break After Arrow Function Parameters

We can’t add a line break after the arrow function parameters.

For instance, we can’t write:

`const` foo `=` `(x,` `y)`
`=>` `{`
  `return` `x` `+` `y;`
`};`

We’ll get a syntax error if we write that.

Instead, we write:

`const` foo `=` `(x,` `y)` `=>` `{`
  `return` `x` `+` `y;`
`};`

Then it’ll run.

This way, if our arrow function has only one parameter, we can omit the parentheses and still keep it valid.

No Statements as Expression Bodies

Expressions are code that returns a value.

For instance:

`3` `+` 39
`foo(1)`
`'abc'.length`

all return something and are expressions.

Statements do things. The following are examples of statements:

if `(true)` `{`
  //...
`}`

`return` `123;`

Most expressions can be used as statements, just that they don’t do anything.

We can put expressions into arrow functions if it’s one line long and the value will be returned.

For example, we can write:

Promise.resolve(1).then(x => console.log(x));

But we’ve to put statements in braces, so we’ve to write:

Promise.resolve(1).then(x => {
  throw x
});

Conclusion

Arrow functions can be defined and used in various ways.

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.