ES6 JavaScript Object Literals

Original author: Craig Buckler
  • Transfer
The material, the translation of which we present to your attention, is devoted to the study of the features of object literals in JavaScript, in particular, innovations that have appeared in recent versions of the ECMAScript standard.

JavaScript has the power and convenience of creating objects using object literals. The ES2015 (ES6) standard simplifies working with objects when creating applications for modern browsers (except IE) and for the Node.js platform.



The basics


Creating objects in some languages ​​can be expensive, by which we mean both the programmer’s working time and the computing resources of the systems. In particular, we are talking about the fact that, before creating objects, it is necessary to describe classes (say, using a keyword class). In JavaScript, objects can be created very quickly and easily, without the need to perform any preliminary actions. Consider an example:

// ES5var myObject = {
  prop1: 'hello',
  prop2: 'world',
  output: function() {
    console.log(this.prop1 + ' ' + this.prop2);
  }
};
myObject.output(); // hello world

In programming, “disposable” objects are often used. They store settings and other data, they are used as parameters of functions, as values ​​returned by functions, and in other situations. JavaScript object literals in such cases are very helpful, and ES6 expands their capabilities.

Initializing objects from variables


Properties of objects are often created from variables, assigning to them the same names that are already assigned to these variables. For example:

// ES5var
  a = 1, b = 2, c = 3;
  obj = {
    a: a,
    b: b,
    c: c
  };
// obj.a = 1, obj.b = 2, obj.c = 3

In ES6, you no longer need to repeat variable names:

// ES6
const
  a = 1, b = 2, c = 3;
  obj = {
    a,
    b,
    c
  };
// obj.a = 1, obj.b = 2, obj.c = 3

This technique can be useful for returned objects using the Revealing Module pattern , which allows you to create namespaces for different code fragments in order to avoid name conflicts. For example:

// ES6const lib = (() => {
  functionsum(a, b)  { return a + b; }
  functionmult(a, b) { return a * b; }
  return {
    sum,
    mult
  };
}());
console.log( lib.sum(2, 3) );  // 5console.log( lib.mult(2, 3) ); // 6

You may have seen how this technique is used in ES6 modules:

// lib.jsfunctionsum(a, b)  { return a + b; }
functionmult(a, b) { return a * b; }
export { sum, mult };

Short syntax for declaring object methods


When declaring object methods in ES5, you must use the keyword function:

// ES5var lib = {
  sum:  function(a, b) { return a + b; },
  mult: function(a, b) { return a * b; }
};
console.log( lib.sum(2, 3) );  // 5console.log( lib.mult(2, 3) ); // 6

Now, in ES6, this can no longer be done. Here we have the following abbreviated method of declaring methods:

// ES6const lib = {
  sum(a, b)  { return a + b; },
  mult(a, b) { return a * b; }
};
console.log( lib.sum(2, 3) );  // 5
console.log( lib.mult(2, 3) ); // 6

It should be noted that the ES6 ( =>) arrow functions cannot be used here , since the methods must have names. However, the arrow functions can be used if you explicitly assign names to methods (as in ES5). For example:

// ES6const lib = {
  sum:  (a, b) => a + b,
  mult: (a, b) => a * b
};
console.log( lib.sum(2, 3) );  // 5console.log( lib.mult(2, 3) ); // 6

Dynamic keys


In ES5, it was impossible to use variables as key names, although a key whose name is given to a variable could be added after the object was created. For example:

// ES5var
  key1 = 'one',
  obj = {
    two: 2,
    three: 3
  };
obj[key1] = 1;
// obj.one = 1, obj.two = 2, obj.three = 3

In ES6, keys can be assigned dynamically by putting an expression that defines the name in square brackets ( []). For example:

// ES6const
  key1 = 'one',
  obj = {
    [key1]: 1,
    two: 2,
    three: 3
  };
// obj.one = 1, obj.two = 2, obj.three = 3

To create a key, you can use any expression:

// ES6const
  i = 1,
  obj = {
    ['i' + i]: i
  };
console.log(obj.i1); // 1

Dynamic keys can be used for both methods and properties:

// ES6const
  i = 2,
  obj = {
    ['mult' + i]: x => x * i
  };
console.log( obj.mult2(5) ); // 10

Another question is whether it is necessary to create properties and methods with dynamically generated names. The readability of the code in which this technique is used may deteriorate. Perhaps if you are faced with situations in which dynamic names seem appropriate, it would be better to think about using factory functions or classes to create objects.

Destructuring


Destructuring is the extraction of properties of objects and assigning them to variables. Often during the development of applications, it is necessary to extract the value of an object property and write it into a variable. In ES5, it was necessary to describe this as follows, using property access commands:

// ES5var myObject = {
  one:   'a',
  two:   'b',
  three: 'c'
};
var
  one   = myObject.one, // 'a'
  two   = myObject.two, // 'b'
  three = myObject.three; // 'c'

ES6 supports destructuring. You can create a variable with the same name as the corresponding property of the object and do the following:

// ES6const myObject = {
  one:   'a',
  two:   'b',
  three: 'c'
};
const { one, two, three } = myObject;
// one = 'a', two = 'b', three = 'c'

Variables in which the values ​​of the object's properties fall can, in fact, have any names, but if they differ from the names of the properties, it is necessary to use the construction { propertyName: newVariable }:

// ES6const myObject = {
  one:   'a',
  two:   'b',
  three: 'c'
};
const { one: first, two: second, three: third } = myObject;
// first = 'a', second = 'b', third = 'c'

Objects with a complex structure, in which arrays and other objects are nested, can also be used in destructive assignment operations:

// ES6
const meta = {
  title: 'EnhancedObjectLiterals',
  pageinfo: {
    url: 'https://www.sitepoint.com/',
    description: 'How to use objectliteralsinES2015 (ES6).',
    keywords: 'javascript, object, literal'
  }
};
const {
  title   : doc,
  pageinfo: { keywords: topic }
} = meta;
/*
  doc   = 'Enhanced Object Literals'
  topic = 'javascript, object, literal'
*/

At first, all this may seem complicated, however, it is not so difficult to understand this, the main thing is to remember the following:

  • The right side of the expression is the data source — an array or an object that stores the data to be extracted.
  • The left side of an expression is the target of a destructuring assignment — a structure that describes variables that will be assigned values ​​extracted from an array or an object.

When using restructuring, you may encounter some difficulties. Thus, an expression cannot be started with a curly bracket, since then it will look like a block of code. For example:

{ a, b, c } = myObject; // неправильно

This construction is normally perceived by the system when declaring variables:

const{ a, b, c } = myObject; // правильно

If the variables are already declared, you must enclose the expression in parentheses:

let a, b, c;
({ a, b, c } = myObject); // правильно

As a result, while dealing with destructuring, one should be attentive to the code and not mix the declared and undeclared variables.

Destructuring is a technique that can be useful in many situations.

Default Function Parameters


If a function needs a long list of arguments, it is usually easier to pass one object with parameters to it. For example:

prettyPrint( {
  title: 'Enhanced Object Literals',
  publisher: {
    name: 'SitePoint',
    url: 'https://www.sitepoint.com/'
  }
} );

In ES5, it was necessary to disassemble objects with parameters in order, if such objects do not contain what is needed, assign default values ​​to the corresponding parameters:

// ES5, назначение значений по умолчаниюfunctionprettyPrint(param){
  param = param || {};
  var
    pubTitle = param.title || 'No title',
    pubName = (param.publisher && param.publisher.name) || 'No publisher';
  return pubTitle + ', ' + pubName;
}

In ES6, any parameters can be assigned default values:

// ES6 - значения параметров по умолчаниюfunctionprettyPrint(param = {}){ ... }

You can then use restructuring to extract values ​​from the object, and, if necessary, to assign default values:

// ES6 деструктурированное значение по умолчаниюfunctionprettyPrint(
  {
    title: pubTitle = 'No title',
    publisher: { name: pubName = 'No publisher' }
  } = {}
) {
  return`${pubTitle}, ${pubName}`;
}

It is worth noting that such code may be harder to read than the more traditional one, although this is a matter of the programmer’s personal bias.

Parsing objects returned by functions


Functions can return only one value, but this value can be an object with hundreds of properties or methods. In ES5, it was necessary to first get the returned object, and after that it was possible to extract values ​​from it:

// ES5var
  obj = getObject(),
  one = obj.one,
  two = obj.two,
  three = obj.three;

Destructuring simplifies this process. Now all this can be done without having to save the object in a separate variable and then parse it:

// ES6const { one, two, three } = getObject();

You may have seen something similar in the programs for Node.js. For example, if you need only methods readFile()and a writeFile()module fs, you can get links to them like this:

// ES6 Node.js
const { readFile, writeFile } = require('fs');
readFile('file.txt', (err, data) => {
  console.log(err || data);
});
writeFile('new.txt', 'new content', err => {
  console.log(err || 'file written');
});

Syntax of the remaining parameters and the ES2018 extension operator (ES9)


= In ES2015, the syntax of the remaining parameters and the extension operator (both of which look like three dots ) were used only when working with arrays. In ES2018, similar functionality can be used to work with objects:

const myObject = {
  a: 1,
  b: 2,
  c: 3
};
const{ a, ...x } = myObject;
// a = 1// x = { b: 2, c: 3 }

A similar approach can be used to transfer some values ​​to the function:

functionrestParam({ a, ...x }){
  // a = 1
  // x = { b: 2, c: 3 }
}
restParam({
  a: 1,
  b: 2,
  c: 3
});

Note that in such situations you can use only one expression with three dots at the end of the list. In addition, for objects embedded in other objects, this does not work.

The extension operator can be used inside objects:

const
  obj1 = { a: 1, b: 2, c: 3 },
  obj2 = { ...obj1, z: 26 };
// obj2 is { a: 1, b: 2, c: 3, z: 26 }

The extension operator is permissible to use for cloning objects ( obj2 = { ...obj1 };), but here we must take into account the fact that this approach makes a small copy of the object. If the properties of the objects are other objects, the clone of the object will refer to the same nested objects.

The syntax of the remaining parameters and the extension operator still have not very broad support. At the moment, they, without additional efforts, can be used in Chrome and Firefox browsers, and when developing for the Node.js platform version 8.6 and higher.

Results


Object literals have always been a useful JavaScript feature. Innovations that appear in JavaScript starting with the ES2015 standard do not carry fundamental changes, but they save the programmer’s time and help to write cleaner and more concise code.

Dear readers! What methods of creating JS-objects do you use most often?


Also popular now: