ES6 in Action: Enhanced Object Literals

Share this article

Enhanced Object Literals
This article looks at what’s possible with object literals in JavaScript, especially in the light of recent ECMAScript updates.
The ability to create JavaScript objects using literal notation is powerful. New features introduced from ES2015 (ES6) make object handling even easier in all modern browsers (not IE) and Node.js. Creating objects in some languages can be expensive in terms of development time and processing power when a class must be declared before anything can be achieved. In JavaScript, it’s easy to create objects on the fly. For example:
// ES5-compatible code
var myObject = {
  prop1: 'hello',
  prop2: 'world',
  output: function() {
    console.log(this.prop1 + ' ' + this.prop2);
  }
};

myObject.output(); // hello world
Single-use objects are used extensively. Examples include configuration settings, module definitions, method parameters, return values from functions, etc. ES2015 (ES6) added a range of features to enhance object literals.

Object Initialization From Variables

Objects’ properties are often created from variables with the same name. For example:
// ES5 code
var
  a = 1, b = 2, c = 3;
  obj = {
    a: a,
    b: b,
    c: c
  };

// obj.a = 1, obj.b = 2, obj.c = 3
There’s no need for nasty repetition in ES6!…
// ES6 code
const
  a = 1, b = 2, c = 3;
  obj = {
    a
    b
    c
  };

// obj.a = 1, obj.b = 2, obj.c = 3
This could be useful for returned objects when using a revealing module pattern, which (effectively) namespaces code in order to avoid naming conflicts. For example:
// ES6 code
const lib = (() => {

  function sum(a, b)  { return a + b; }
  function mult(a, b) { return a * b; }

  return {
    sum,
    mult
  };

}());

console.log( lib.sum(2, 3) );  // 5
console.log( lib.mult(2, 3) ); // 6
You’ve possibly seen it used in ES6 modules:
// lib.js
function sum(a, b)  { return a + b; }
function mult(a, b) { return a * b; }

export { sum, mult };

Object Method Definition Shorthand

Object methods in ES5 require the function statement. For example:
// ES5 code
var lib = {
  sum:  function(a, b) { return a + b; },
  mult: function(a, b) { return a * b; }
};

console.log( lib.sum(2, 3) );  // 5
console.log( lib.mult(2, 3) ); // 6
This is no longer necessary in ES6; it permits the following shorthand syntax:
// ES6 code
const 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’s not possible to use ES6 fat arrow => function syntax here, because the method requires a name. That said, you can use arrow functions if you name each method directly (like ES5). For example:
// ES6 code
const lib = {
  sum:  (a, b) => a + b,
  mult: (a, b) => a * b
};

console.log( lib.sum(2, 3) );  // 5
console.log( lib.mult(2, 3) ); // 6

Dynamic Property Keys

In ES5, it wasn’t possible to use a variable for a key name, although it could be added after the object had been created. For example:
// ES5 code
var
  key1 = 'one',
  obj = {
    two: 2,
    three: 3
  };

obj[key1] = 1;

// obj.one = 1, obj.two = 2, obj.three = 3
Object keys can be dynamically assigned in ES6 by placing an expression in [
square brackets ]. For example:
// ES6 code
const
  key1 = 'one',
  obj = {
    [key1]: 1,
    two: 2,
    three: 3
  };

// obj.one = 1, obj.two = 2, obj.three = 3
Any expression can be used to create a key. For example:
// ES6 code
const
  i = 1,
  obj = {
    ['i' + i]: i
  };

console.log(obj.i1); // 1
A dynamic key can be used for methods as well as properties. For example:
// ES6 code
const
  i = 2,
  obj = {
    ['mult' + i]: x => x * i
  };

console.log( obj.mult2(5) ); // 10
Whether you should create dynamic properties and methods is another matter. The code can be difficult to read, and it may be preferable to create object factories or classes.

Destructuring (Variables From Object Properties)

It’s often necessary to extract a property value from an object into another variable. This had to be explicitly declared in ES5. For example:
// ES5 code
var 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 an equivalent object property. For example:
// ES6 code
const myObject = {
  one:   'a',
  two:   'b',
  three: 'c'
};

const { one, two, three } = myObject;
// one = 'a', two = 'b', three = 'c'
It’s also possible to assign properties to variables with any name using the notation { propertyName: newVariable }. For example:
// ES6 code
const myObject = {
  one:   'a',
  two:   'b',
  three: 'c'
};

const { one: first, two: second, three: third } = myObject;
// first = 'a', second = 'b', third = 'c'
More complex objects with nested arrays and sub-objects can also be referenced in destructuring assignments. For example:
// ES6 code
const meta = {
  title: 'Enhanced Object Literals',
  pageinfo: {
    url: 'https://www.sitepoint.com/',
    description: 'How to use object literals in ES2015 (ES6).',
    keywords: 'javascript, object, literal'
  }
};

const {
  title   : doc,
  pageinfo: { keywords: topic }
} = meta;

/*
  doc   = 'Enhanced Object Literals'
  topic = 'javascript, object, literal'
*/
This initially appears complicated, but remember that in all destructuring assignments:
  • the left-hand side of the assignment is the destructuring source — the array or object which holds the data being extracted
  • the right-hand side of the assignment is the destructuring target — the pattern which defines the variable being assigned.
There are a number of caveats. You can’t start a statement with a curly brace, because it looks like a code block. For example:
{ a, b, c } = myObject; // FAILS
You must either declare the variables — for example:
const { a, b, c } = myObject; // WORKS
or use parentheses if variables have already been declared — for example:
let a, b, c;
({ a, b, c } = myObject); // WORKS
You should therefore be careful not to mix declared and undeclared variables. There are a number of situations where object destructuring is useful.

Default Function Parameters

It’s often easier to pass a single object to a function than use a long list of arguments. For example:
prettyPrint( {
  title: 'Enhanced Object Literals',
  publisher: {
    name: 'SitePoint',
    url: 'https://www.sitepoint.com/'
  }
} );
In ES5, it’s necessary to parse the object to ensure appropriate defaults are set. For example:
// ES5 assign defaults
function prettyPrint(param) {

  param = param || {};
  var
    pubTitle = param.title || 'No title',
    pubName = (param.publisher && param.publisher.name) || 'No publisher';

  return pubTitle + ', ' + pubName;

}
In ES6, we can assign a default value to any parameter. For example:
// ES6 default value
function prettyPrint(param = {}) { ... }
We can then use destructuring to extract values and assign defaults where necessary:
// ES6 destructured default value
function prettyPrint(
  {
    title: pubTitle = 'No title',
    publisher: { name: pubName = 'No publisher' }
  } = {}
) {

  return `${pubTitle}, ${pubName}`;

}
Whether you find this code easier to read is another matter!

Parsing Returned Objects

Functions can only return one value, but that could be an object with hundreds of properties and/or methods. In ES5, it’s necessary to obtain the returned object, then extract values accordingly. For example:
// ES5 code
var
  obj = getObject(),
  one = obj.one,
  two = obj.two,
  three = obj.three;
ES6 destructuring makes this process simpler, and there’s no need to retain the object as a variable:
// ES6 code
const { one, two, three } = getObject();
You may have seen similar assignments in Node.js code. For example, if you only required the File System (fs) methods readFile and writeFile, you could reference them directly. For example:
// 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');
});

ES2018 (ES9) Rest/Spread Properties

In ES2015, rest parameter and spread operator three-dot (...) notation applied to arrays only. ES2018 enables similar rest/spread functionality for objects. A basic example:
const myObject = {
  a: 1,
  b: 2,
  c: 3
};

const { a, ...x } = myObject;
// a = 1
// x = { b: 2, c: 3 }
You can use the technique to pass values to a function:
restParam({
  a: 1,
  b: 2,
  c: 3
});

function restParam({ a, ...x }) {
  // a = 1
  // x = { b: 2, c: 3 }
}
You can only use a single rest property at the end of the declaration. In addition, it only works on the top level of each object and not sub-objects. The spread operator can be used within other objects. For example:
const
  obj1 = { a: 1, b: 2, c: 3 },
  obj2 = { ...obj1, z: 26 };

// obj2 is { a: 1, b: 2, c: 3, z: 26 }
You could use the spread operator to clone objects (obj2 = { ...obj1 };), but be aware you only get shallow copies. If a property holds another object, the clone will refer to the same object. ES2018 (ES9) rest/spread property support is patchy, but it’s available in Chrome, Firefox and Node.js 8.6+. Object literals have always been useful. The new features introduced from ES2015 did not fundamentally change how JavaScript works, but they save typing effort and lead to clearer, more concise code.

Frequently Asked Questions (FAQs) about ES6 Enhanced Object Literals

What are the key features of ES6 Enhanced Object Literals?

ES6 Enhanced Object Literals introduce several key features that simplify the process of working with objects in JavaScript. These include shorthand property names, which allow you to define properties without repeating the property name, and shorthand method names, which simplify the syntax for defining methods. ES6 also introduces computed property names, which allow you to dynamically create property names based on variable values.

How do shorthand property names work in ES6 Enhanced Object Literals?

Shorthand property names in ES6 Enhanced Object Literals allow you to define properties in a more concise way. Instead of writing {x: x, y: y}, you can simply write {x, y}. This feature is particularly useful when you are working with large objects, as it can significantly reduce the amount of code you need to write.

How do shorthand method names work in ES6 Enhanced Object Literals?

Shorthand method names in ES6 Enhanced Object Literals simplify the syntax for defining methods. Instead of writing {myMethod: function() {...}}, you can write {myMethod() {...}}. This makes your code cleaner and easier to read.

What are computed property names in ES6 Enhanced Object Literals?

Computed property names in ES6 Enhanced Object Literals allow you to dynamically create property names based on variable values. For example, you can write let propName = 'myProp'; let obj = {[propName]: 'value'}; to create a property named ‘myProp’ with the value ‘value’. This feature is useful when you need to create property names dynamically, for example, when working with user input.

How can I use ES6 Enhanced Object Literals in my code?

To use ES6 Enhanced Object Literals in your code, you simply need to use the new syntax when defining your objects. For example, instead of writing {x: x, y: y}, you can write {x, y}. Similarly, instead of writing {myMethod: function() {...}}, you can write {myMethod() {...}}. To use computed property names, you can write {[propName]: 'value'}.

What are the benefits of using ES6 Enhanced Object Literals?

ES6 Enhanced Object Literals offer several benefits. They make your code cleaner and easier to read, and they can significantly reduce the amount of code you need to write when working with large objects. They also allow you to dynamically create property names, which can be useful when working with user input.

Are there any drawbacks to using ES6 Enhanced Object Literals?

One potential drawback to using ES6 Enhanced Object Literals is that they are not supported in older browsers. However, this can be mitigated by using a transpiler like Babel to convert your ES6 code into ES5 code, which is supported in all browsers.

Can I use ES6 Enhanced Object Literals with other ES6 features?

Yes, you can use ES6 Enhanced Object Literals with other ES6 features. For example, you can use them with arrow functions, template literals, and destructuring assignment. This allows you to write even more concise and readable code.

How do ES6 Enhanced Object Literals compare to traditional object literals?

ES6 Enhanced Object Literals offer several improvements over traditional object literals. They allow you to define properties and methods in a more concise way, and they allow you to dynamically create property names. However, they are not supported in older browsers, so you may need to use a transpiler if you need to support these browsers.

Where can I learn more about ES6 Enhanced Object Literals?

There are many resources available online where you can learn more about ES6 Enhanced Object Literals. Some good places to start include the Mozilla Developer Network (MDN) and various JavaScript tutorials and blogs. You can also experiment with ES6 Enhanced Object Literals in online code editors like JSFiddle or CodePen.

Craig BucklerCraig Buckler
View Author

Craig is a freelance UK web consultant who built his first page for IE2.0 in 1995. Since that time he's been advocating standards, accessibility, and best-practice HTML5 techniques. He's created enterprise specifications, websites and online applications for companies and organisations including the UK Parliament, the European Parliament, the Department of Energy & Climate Change, Microsoft, and more. He's written more than 1,000 articles for SitePoint and you can find him @craigbuckler.

es6es9learn-modernjsmodernjsmodernjs-hubnodeobject literals
Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week