Monday, 2 July, 2018 UTC


Summary

This article is a continuation of last week’s post on ES2017 Async-Await, detailing the updates of ES2017.
Check out the ES2016 plus compatibility table for more information on the current browser support. Most likely, you will need a transpiler.
Use the ES2017 Babel Preset for code transpilation if you want to support older browsers.
New Object Extensions
In this section, we will introduce three Object methods:
  • Object.entries,
  • Object.values,
  • Object.getOwnPropertyDescriptors.

Object.entries and Object.values

We already know Object.keys:
let account = {
    first: 'Zsolt',
    last: 'Nagy',
    email: '[email protected]'
};

Object.keys( account );
> ["first", "last", "email"]
In ES7, Object.values and Object.entries are also available:
Object.values( account )
> ["Zsolt", "Nagy", "[email protected]"]

Object.entries( account ) 
>  [ ["first", "Zsolt"], 
     ["last", "Nagy"], 
     ["email", "[email protected]"] ]
Object.entries can also be used for creating maps.
let accountMap = new Map( Object.entries( account ) );
> Map {
    "first" => "Zsolt", 
    "last"  => "Nagy", 
    "email" => "[email protected]"
  }
Symbol keys are ignored from the keys, values, and entries arrays.
The entries method was already defined for arrays in ES6, and it returned an ArrayIterator.
let iterator = Object.values( account ).entries();
> ArrayIterator {}

console.log( iterator.next() );
> { value: [0, "Zsolt"], done: false }

for ( let [val, key] of iterator ) {
    console.log( val, key );
}
> 1 "Nagy"
> 2 "[email protected]"

Object.getOwnPropertyDescriptors

Object.getOwnPropertyDescriptors returns all property descriptors of its first argument:
let player = {
    cards: [ 'Ah', 'Qc' ], 
    chips: 1000 
};

let descriptors = 
    Object.getOwnPropertyDescriptors( player );

console.log( descriptors );
> Object {cards: Object, chips: Object}

console.log( descriptors.cards );
> Object {
    value: Array[2], 
    writable: true, 
    enumerable: true, 
    configurable: true
  }
Object.getOwnPropertyDescriptors returns all property descriptors in an object with the same keys as the keys of the original object. The following four property descriptors are returned (source: developer.mozilla.com):
  • value: the value of the property
  • writable: true if and only if the value associated with the property may be changed (data descriptors only)
  • get: A function which serves as a getter for the property, or undefined if there is no getter (accessor descriptors only)
  • set: A function which serves as a setter for the property, or undefined if there is no setter (accessor descriptors only)
  • configurable: true if and only if the type of this property descriptor may be changed and if the property may be deleted from the corresponding object
  • enumerable: true if and only if this property shows up during enumeration of the properties on the corresponding object
Let’s construct an example for getters and setters
let player = {
    cards: [ 'Ah', 'Qc' ], 
    chips: 1000,
    flop: [ '7d', '7c', '2c' ],
    get hand() {
        return [ ...this.cards, ...this.flop ];
    },
    set hand( newHand ) {
        if ( newHand.length && newHand.length === 5 ) {
            [ this.cards[0], 
              this.cards[1], 
              ...this.flop 
            ] = newHand;
        }
    }
};

let descriptors =
    Object.getOwnPropertyDescriptors( player );

console.log( descriptors );
> Object {
    cards: Object, 
    chips: Object, 
    flop: Object, 
    hand: Object
}

console.log( Object.keys( descriptors.hand ) );
> ["get", "set", "enumerable", "configurable"]

descriptors.hand.get
> function get hand() {
    return [ ...this.cards, ...this.flop ];
  }
Object.getOwnPropertyDescriptors handles String keys as well as Symbol keys[nodejssymbolshow]:
[nodejssymbolshow]: in order to show Symbol keys in node and in some browsers, instead of console.log, use console.dir with the flag showHidden: true. Check out this node issue for more information, or re-read the (relevant section) of Chapter 9.
let s = Symbol('test');
let test = {
    [s]: 'test'   
};

console.log( Object.getOwnPropertyDescriptors( test ) );
> { Symbol(test): Object {
        configurable : true
        enumerable : true
        value : "test"
        writable : true
    }
  }
As a consequence, Object.getOwnPropertyDescriptors can be used to make shallow copies of objects using Object.create.
Object.create takes two arguments:
  • the prototype of the object we wish to clone,
  • the property descriptors of the object.
In order to illustrate the difference between a shallow copy and a deep copy, let’s create a shallow copy of the player object defined above.
let player = {
    cards: [ 'Ah', 'Qc' ], 
    chips: 1000,
    flop: [ '7d', '7c', '2c' ],
    get hand() {
        return [ ...this.cards, ...this.flop ];
    },
    set hand( newHand ) {
        if ( newHand.length && newHand.length === 5 ) {
            [ this.cards[0], 
              this.cards[1], 
              ...this.flop 
            ] = newHand;
        }
    }
};

let proto = Object.getPrototypeOf( player );
let descriptors = 
    Object.getOwnPropertyDescriptors( player );

let newPlayer = Object.create( proto, descriptors );

newPlayer.chips = 1500;

console.log( player.chips, newPlayer.chips );
> 1000 1500
We have created two seemingly independent entities. However, when trying to change a card of the new player, the change will be made in the context of the old player as well.
newPlayer.cards[1] = 'Ad';

console.log( newPlayer.cards[1], player.cards[1] );
> 'Ad' 'Ad'
This is because shallow copying only copied the reference of the cards array to the new player object. The original and the copied reference point at the exact same array.
New String Extensions
This section is about two String prototype extensions:
  • padStart,
  • padEnd.
These two methods are not yet implemented in all browsers. You need to open the Firefox developer tools or Chrome 57 to experiment with them.
Padding is used to add additional characters to the start or the end of a string so that it reaches a given size.
Padding is useful in character mode for alignment.
In the following example, let’s format the amounts such that the integer part contains 6 characters, and the fractional part contains 2 characters. Let’s pad the characters in front of the interger part with spaces, and the decimal part with zeros.
Let’s console log the result.
let amounts = [
    '1234.0',
    '1',
    '2.56'
];

console.log( `|dddddd.ff|` );
for ( let amount of amounts ) {
    let [ front, back = '' ] = amount.split('.');
    front = front.padStart( 6 );
    back = back.padEnd( 2, '0' );
    console.log( `|${front}.${back}|` );
}

> |dddddd.ff|
> |  1234.00|
> |     1.00|
> |     2.56|
If the second argument of padStart or padEnd is not given, ' ' characters will be used by default.
If you are interested in similar articles, sign up for weekly email updates below:
Learn ES6 in Practice
Sign up below to access an ES6 course with many exercises and reference solutions.