left-icon

ECMAScript 6 Succinctly®
by Matthew Duffield

Previous
Chapter

of
A
A
A

CHAPTER 3

Enhanced Object Properties

Enhanced Object Properties


Property shorthand

Creating object literals has never been easier. Let’s first look at an ES5 syntax for creating an object literal:

Code Listing: 19

let first = "john";

let last = "doe";

let obj = {first: first, last: last};

Now, let’s look at the same thing using ES6 syntax:

Code Listing: 20

let first = "john";

let last = "doe";

let obj = {first, last};

This makes for very clean and succinct code when dealing with object literals. Let’s look at another example of what you can do:

Code Listing: 21

function createDog(name, ability) {

  return { type: "animal", name, ability };

}

let a = createDog("wolf", "bark");

console.log(JSON.stringify(a));     

Here is the output of the newly created object:

Code Listing: 22

{"type":"animal","name":"wolf","ability":"bark"}

This shorthand won’t change your code drastically, but it can make it a little bit cleaner and more succinct.

Computed property names

It is now possible to define computed property names in object property definitions. You can do this using variables as shown below:

Code Listing: 23

var prop = "foo";

var o = {

  [prop]: "hey",

  ["b" + "ar"]: "there",

};

console.log(o.foo);

console.log(o.bar);

Here is the output:

Code Listing: 24

hey

there

You could create your property names in the following way as well:

Code Listing: 25

let i = 0;

let a = {

  ["foo" + ++i]: i,

  ["foo" + ++i]: i,

  ["foo" + ++i]: i

};

console.log(a.foo1);

console.log(a.foo2);

console.log(a.foo3);

Here is the output:

Code Listing: 26

1

2

3

It is also possible to use functions for more advanced property names, as shown in the following example:

Code Listing: 27

function foo() {

  return "firstname";

}

let obj = {

    foo: "bar",

    [ "prop_" + foo() ]: 42

}

console.log(JSON.stringify(obj));

Let’s look at the output:

Code Listing: 28

{"foo":"bar","prop_firstname":42}

Method properties

Method properties are another shortcut to what we could already do in ES5. Let’s look at what they looked like in ES5:

Code Listing: 29

let myMath = {

  add: function(a, b) { return a + b; },

  subtract: function(a, b) { return a - b; },

  multiply: function(a, b) { return a * b; },

  divide: function(a, b) { return a / b; }

}

Now let’s see what the same code would look like in ES6:

Code Listing: 30

let myMath = {

  add(a, b) { return a + b; },

  subtract(a, b) { return a - b; },

  multiply(a, b) { return a * b; },

  divide(a, b) { return a / b; }

}

As you can see when comparing the two, the new ES6 version is simply nicer and more succinct.

object.assign

The object.assign() method is used to copy property values from one or more source objects to a given target object. It will return the target object.

We can use it to create a simple clone of an object, as shown below:

Code Listing: 31

var obj = { firstname: "matt", lastname: "duffield" };

var copy = Object.assign({}, obj);

console.log(copy);

When we run this, we get a new copy of the object:

Code Listing: 32

Object {firstname: "matt", lastname: "duffield"}

Here we see that we provide an empty object literal as our target object and then provide another object literal as a given source. We get back the target with the values from the source as well.

Let’s look at another example. This time, let’s provide multiple sources as well as a target with values. We will see that the target will receive the values from the sources and return with the values merged. We will also see that the original target variable is also changed as well, o1 being the target with o2 and o3 the two sources:

Code Listing: 33

var o1 = { a: 1 };

var o2 = { b: 2 };

var o3 = { c: 3 };

var obj = Object.assign(o1, o2, o3);

console.log(obj);

console.log(o1);

When we run this, we see that both the new object obj and o1 have the same values.

Code Listing: 34

Object {a: 1, b: 2, c: 3}

Object {a: 1, b: 2, c: 3}

Let’s do one more; we’ll copy an object literal with accessors:

Code Listing: 35

var obj = {

  foo: 1,

  get bar() {

    return 2;

  }

};

var copy = Object.assign({}, obj);

console.log(copy);

Running the preceding code will produce the following result:

Code Listing: 36

{foo: 1, bar: 2}

What is happening here is that when Object.assign() performs the merge, it actually executes the getter bar() method and places the result into a property called bar instead.

If you really want to copy accessors, consider the following code:

Code Listing: 37

function myAssign(target, ...sources) {

  sources.forEach(source => {

    Object.defineProperties(target, Object.keys(source).reduce((descriptors, key) => {

      descriptors[key] = Object.getOwnPropertyDescriptor(source, key);

      return descriptors;

    }, {}));

  });

  return target;

}

var copy = myAssign({}, obj);

console.log(copy);

Don’t be alarmed if you see some new features in the preceding code that you don’t completely understand; we will address them later in the book. When we run the preceding code, we will get the following result:

Code Listing: 38

{foo: 1, bar: [Getter]}

This time we get what we are expecting, a copy of the properties and accessors.

Math

The global object Math has several new methods in ES6. The following will provide their names and a short description for each:

Math.sign(x)

Returns the sign of x as -1 or +1. Unless x is either NaN or zero; then x is returned.

Math.trunc(x)

Removes the decimal part of x and returns the integral part.

Math.cbrt(x)

Returns the cube root of x.

Math.expm1(x)

Returns Math.exp(x) – 1. The inverse of Math.log1p().

Math.log1p(x)

Returns Math.log(1 + x). The inverse of Math.expm1().

Math.log2(x)

Computes the logarithm to base 2.

Math.log10(x)

Computes the logarithm to base 10.

Math.fround(x)

Rounds x to the nearest single-precision floating point value, which uses 32 bits.

Math.imul(x, y)

Multiplies the two 32 bit integers x and y and returns the lower 32 bits of the result. This is the only 32 bit basic math operation that can’t be simulated by using a JavaScript operator and coercing the result back to 32 bits.

Math.clz32(x)

Counts the leading zero bits in the 32 bit integer x.

Math.sinh(x)

Computes the hyperbolic sine of x.

Math.cosh(x)

Computes the hyperbolic cosine of x.

Math.tanh(x)

Computes the hyperbolic tangent of x.

Math.asinh(x)

Computes the inverse hyperbolic sine of x.

Math.acosh(x)

Computes the inverse hyperbolic cosine of x.

Math.atanh(x)

Computes the inverse hyperbolic tangent of x.

Math.hypot(…values)

Computes the square root of the sum of squares of its argument.

These new methods provide added convenience when performing computations and complex operations.

Number

The Number object has several new methods and properties. The following will provide their names and a short description:

Number.EPSILON

Used for comparing floating point numbers with a tolerance for rounding errors. The property represents the difference between one and the smallest value greater than one that can be represented as a Number, which is approximately 2.2e-16.

Number.MAX_SAFE_INTEGER

Constant to represent the maximum safe integer in JavaScript (253 – 1).

Number.MIN_SAFE_INTEGER

Constant to represent the minimum safe integer in JavaScript (-(253 – 1)).

Number.isNaN()

This method determines whether the passed value is NaN. This is a more robust version of the original global isNaN().

Number.isFinite()

This method determines whether the passed value is a finite number.

Number.isInteger()

This method determines whether the passed value is an integer.

Number.isSafeInteger()

This method determines whether the provided value is a number that is a safe integer.

Number.parseFloat()

This method parses a string argument and returns a floating point number. This method behaves identically to the global function parseFloat(), but its purpose is modularization of globals.

Number.parseInt()

This method parses a string argument and returns an integer of the specified radix or base.

String

The string object has several new methods. The following will provide the names and a short description along with some examples:

String.fromCodePoint()

This method returns a string created by using the specified sequence of code points. This is a static method.

Code Listing: 39

String.fromCodePoint(42);      // *

String.fromCodePoint(65, 90);  // AZ

String.codePointAt()

This method returns a non-negative integer that is the UTF-16 encoded code point value.

Code Listing: 40

'ABC'.codePointAt(1);          // 66

'\uD800\uDC00'.codePointAt(0); // 65536

String.startsWith()

This method determines whether a string begins with the characters of another string, returning true or false as appropriate.

Code Listing: 41

var str = 'If you dream it, you can do it.';

console.log(str.startsWith('If you'));         // true

console.log(str.startsWith('you can do'));     // false

console.log(str.startsWith('you can do', 17)); // true

String.endsWith()

This method determines whether a string ends with the characters of another string, returning true or false as appropriate. The last example allows you to specify the length of the string, thus clamping the actual string to the new value.

Code Listing: 42

var str = 'If you can dream it, you can do it.';

console.log(str.endsWith('do it.')); // true

console.log(str.endsWith('you can'));     // false

console.log(str.endsWith('you can', 28)); // true

String.includes()

This method determines whether one string may be found within another string, returning true or false as appropriate.

Code Listing: 43

var str = 'If you can dream it, you can do it.';

console.log(str.includes('If you can'));       // true

console.log(str.includes('it.'));    // true

console.log(str.includes('nonexistent')); // false

console.log(str.includes('If you can', 1));    // false

console.log(str.includes('IF YOU'));       // false

String.normalize()

This method returns the Unicode Normalization Form of a given string. If the value isn’t a string, it will be converted to one first.

Code Listing: 44

// U+1E9B: Latin small letter long s with dot above

// U+0323: Combining dot below

var str = '\u1E9B\u0323';

// Canonically-composed form (NFC)

// U+1E9B: Latin small letter long s with dot above

// U+0323: Combining dot below

str.normalize('NFC'); // '\u1E9B\u0323'

str.normalize();      // same as above

// Canonically-decomposed form (NFD)

// U+017F: Latin small letter long s

// U+0323: Combining dot below

// U+0307: Combining dot above

str.normalize('NFD'); // '\u017F\u0323\u0307'

// Compatibly-composed (NFKC)

// U+1E69: Latin small letter s with dot below and dot above

str.normalize('NFKC'); // '\u1E69'

// Compatibly-decomposed (NFKD)

// U+0073: Latin small letter s

// U+0323: Combining dot below

// U+0307: Combining dot above

str.normalize('NFKD'); // '\u0073\u0323\u0307'

String.repeat()

This method constructs and returns a new string that contains the specified number of copies of the string on which it was called, concatenated together.

Code Listing: 45

'foo'.repeat(-1);   // RangeError

'foo'.repeat(0);    // ''

'foo'.repeat(1);    // 'foo'

'foo'.repeat(2);    // 'foofoo'

'foo'.repeat(2.5);  // 'foofoo' (count will be converted to integer)

'foo'.repeat(1/0);  // RangeError

[@@iterator]()

This method returns a new Iterator object that iterates over the code points of a String value, returning each code point as a String value.

Code Listing: 46

var string = 'A\uD835\uDC68';

var strIter = string[Symbol.iterator]();

console.log(strIter.next().value); // "A"

console.log(strIter.next().value); // "\uD835\uDC68"

Array

The Array object has several new methods. The following will provide the name and a short description and example of each:

Array.of()

This method creates a new Array instance with a variable number of arguments, regardless of the number or type of the arguments.

Code Listing: 47

Array.of(1);             // [1]

Array.of(1, 2, 3);       // [1, 2, 3]

Array.of("a", 7, 12.5);  // ["a", 7, 12.5]

Array.of(undefined);     // [undefined]

Array.copyWithin(target, start[, end = this.length])

This method copies the sequence of array elements within the array to the position starting at the target. The copy is taken from the index positions of the second and third arguments, start and end. The end argument is optional and defaults to the length of the array.

Code Listing: 48

[1, 2, 3, 4, 5].copyWithin(0, 3);      // [4, 5, 3, 4, 5]

[1, 2, 3, 4, 5].copyWithin(0, 3, 4);   // [4, 2, 3, 4, 5]

[1, 2, 3, 4, 5].copyWithin(0, -2, -1); // [4, 2, 3, 4, 5]

Array.entries()

This method returns a new Array Iterator object that contains the key/value pairs for each index in the array.

Code Listing: 49

var arr = ['a', 'b', 'c'];

var eArr = arr.entries();

console.log(eArr.next().value); // [0, 'a']

console.log(eArr.next().value); // [1, 'b']

console.log(eArr.next().value); // [2, 'c']

Array.fill(value[, start = 0[, end = this.length]])

This method fills all the elements of an array from a start index to an end index with a static value.

Code Listing: 50

[1, 2, 3].fill(4);               // [4, 4, 4]

[1, 2, 3].fill(4, 1);            // [1, 4, 4]

[1, 2, 3].fill(4, 1, 2);         // [1, 4, 3]

[1, 2, 3].fill(4, 1, 1);         // [1, 2, 3]

[1, 2, 3].fill(4, -3, -2);       // [4, 2, 3]

[1, 2, 3].fill(4, NaN, NaN);     // [1, 2, 3]

Array(3).fill(4);                // [4, 4, 4]

[].fill.call({ length: 3 }, 4);  // {0: 4, 1: 4, 2: 4, length: 3}

Array.find(callback[, thisArg])

This method returns a value in the array if an element in the array satisfies the provided testing function. Otherwise, undefined is returned.

Code Listing: 51

function isPrime(value, index, array) {

  for (var i = 2; i < value; i++) {

    if (value % i === 0) {

      return false;

    }

  }

  return value > 1;

}

console.log([4, 6, 8, 12].find(isPrime)); // undefined, not found

console.log([4, 5, 8, 12].find(isPrime)); // 5

Array.findIndex(callback[, thisArg])

This method returns an index in the array if an element in the array satisfies the provided testing function. Otherwise, -1 is returned.

Code Listing: 52

function isPrime(value, index, array) {

  for (var i = 2; i < value; i++) {

    if (value % i === 0) {

      return false;

    }

  }

  return value > 1;

}

console.log([4, 6, 8, 12].findIndex(isPrime)); // -1, not found

console.log([4, 6, 7, 12].findIndex(isPrime)); // 2

Array.from(arrayLike[, mapFn[, thisArg]])

This method creates a new Array instance from an array-like or iterable object. This method is not new but has some cool benefits.

Code Listing: 53

let nodes = document.querySelectorAll(".business");

nodes.forEach((item, index, arr) => {

  console.log(item.name);

});

Note: This example will only work in a browser.

Array.includes(searchElement[, fromIndex])

This method determines whether an array includes a certain element, returning true or false as appropriate.

Code Listing: 54

[1, 2, 3].includes(2);     // true

[1, 2, 3].includes(4);     // false

[1, 2, 3].includes(3, 3);  // false

[1, 2, 3].includes(3, -1); // true

[1, 2, NaN].includes(NaN); // true

Array.keys()

This method returns a new Array Iterator that contains the keys for each index in the array.

Code Listing: 55

var arr = ["a", "b", "c"];

var iterator = arr.keys();

console.log(iterator.next()); // { value: 0, done: false }

console.log(iterator.next()); // { value: 1, done: false }

console.log(iterator.next()); // { value: 2, done: false }

console.log(iterator.next()); // { value: undefined, done: true }

Array.values()

This method returns a new Array Iterator object that contains the values for each index in the array.

Code Listing: 56

var arr = ['w', 'y', 'k', 'o', 'p'];

var eArr = arr.values();

// your browser must support for..of loop

// and let-scoped variables in for loops

for (let letter of eArr) {

  console.log(letter);

}

Array[@@iterator]()

The initial value of the @@iterator property is the same function object as the initial value of the values() property.

Code Listing: 57

var arr = ['w', 'y', 'k', 'o', 'p'];

var eArr = arr[Symbol.iterator]();

console.log(eArr.next().value); // w

console.log(eArr.next().value); // y

console.log(eArr.next().value); // k

console.log(eArr.next().value); // o

console.log(eArr.next().value); // p

Typed arrays

Typed arrays are useful in handling binary data. They describe an array-like view of an underlying binary data buffer. Be aware that there is no global property named TypedArray, and neither is there a directly visible constructor. Instead, there are a number of different global properties whose values are typed array constructors for specific element types, as listed in Table 1. When dealing with typed arrays, you will deal with two main classes: ArrayBuffer and DataView. We will go into more detail on each of them later. You may be asking, “Why would I ever want to use typed arrays?” They are JavaScript’s answer for dealing with raw binary data. Typical examples of this could be streaming audio or video data.

The following is a list of the typed array constructors available:

Table 1: Typed Array Objects

Type

Size in bytes

Description

Web IDL type

C type

Int8Array

1

8-bit two’s complement signed integer

Byte

int8_t

Uint8Array

1

8-bit unsigned integer

octet

uint8_t

Uint8ClampedArray

1

8-bit unsigned integer (clamped)

octet

uint8_t

Int16Array

2

16-bit two’s complement signed integer

short

int16_t

Uint16Array

2

16-bit unsigned integer

unsigned short

uint16_t

Int32Array

4

32-bit two’s complement signed integer

long

int32_t

Uint32Array

4

32-bit unsigned integer

unsigned long

uint32_t

Float32Array

4

32-bit IEEE floating point number

unrestricted float

float

Float64Array

8

64-bit IEEE floating point number

unrestricted double

double

Let’s look at an example below:

Code Listing: 58

var buffer = new ArrayBuffer(8);

console.log(buffer.byteLength);

We get the following output from the preceding code sample:

Code Listing: 59

8

Let’s take this a step further and create a DataView in order to manipulate the data within our buffer:

Code Listing: 60

var buffer = new ArrayBuffer(8);

var view = new DataView(buffer);

view.setInt8(0, 3);

console.log(view.getInt8(0));

Here, we are creating the buffer like before. Next, we create a DataView that takes in the buffer as an argument. We now have the ability to manipulate the buffer by setting specific typed values. We used the setInt8 function to put the value “3” into the 0-byte offset position of the buffer. If we run this code, we get the following output as expected:

Code Listing: 61

3

Let’s try another example:

Code Listing: 62

var buffer = new ArrayBuffer(8);

var view = new DataView(buffer);

view.setInt32(0, 3);

console.log(view.getInt8(0));

console.log(view.getInt8(1));

console.log(view.getInt8(2));

console.log(view.getInt8(3));

console.log(view.getInt32(0));

In this example, we set the value into our ArrayBuffer using setInt32. Since setInt32 takes 4 bytes (32 bits = 4 bytes), instead of setInt8, which takes 1 byte (8 bits = 1 byte), our getInt8 calls don’t find the number until our offset is 4 bytes into the buffer.

If we run this code, we get the following output as expected:

Code Listing: 63

0

0

0

3

3

Rather than creating a generic DataView, there are typed array view classes with more specific names that we can create and access like regular arrays. These are the arrays listed in Table 1.

Consider the following example:

Code Listing: 64

var buffer = new ArrayBuffer(8);

// 32-bit View

var bigView = new Int32Array(buffer);

bigView[0] = 98;

bigView[1] = 128;

for(let value of bigView)

{

  console.log(value);

  // 98

  // 128

}

// 16-bit View

var mediumView = new Int16Array(buffer);

for(let value of mediumView)

{

  console.log(value);

  // 98

  // 0

  // 128

  // 0

}

// 8-bit View

var smallView = new Int8Array(buffer);

for(let value of smallView)

{

  console.log(value);

  // 98

  // 0

  // 0

  // 0

  // -128

  // 0

  // 0

  // 0

}

// 8-bit Unsigned View

var unsignedView = new Uint8Array(buffer);

for(let value of unsignedView)

{

  console.log(value);

  // 98

  // 0

  // 0

  // 0

  // 128

  // 0

  // 0

  // 0

}

As you can see from the preceding code, we are able to treat these typed arrays just like regular arrays. The biggest difference here is that these are truly “typed” arrays.

The following is a small representation of browser supported APIs for Typed Arrays:

  • File API
  • XML HttpRequest
  • Fetch API
  • Canvas
  • WebSockets

Let’s take a look at how we would use each one of these APIs in code.

File API

The File API lets you access local files. The following code demonstrates how to get the bytes of a submitted local file in an ArrayBuffer:

Code Listing: 65

let fileInput = document.getElementById('fileInput');

let file = fileInput.files[0];

let reader = new FileReader();

reader.readAsArrayBuffer(file);

reader.onload = function () {

  let arrayBuffer = reader.result;

  // process the buffer...

};

Note: This example will only work in a browser.

XMLHttpRequest API

In the newer versions of the XMLHttpRequest API, you can have the results delivered as an ArrayBuffer:

Code Listing: 66

let xhr = new XMLHttpRequest();

xhr.open('GET', someUrl);

xhr.responseType = 'arraybuffer';

 

xhr.onload = function () {

  let arrayBuffer = xhr.response;

  // process the buffer...

};

 

xhr.send();

Note: This example will only work in a browser.

Fetch API

Like the XMLHttpRequest API, the Fetch API lets you request resources. However, most will consider it to be an improvement on XMLHttpRequest in terms of API design. It also takes advantage of Promises, which we will look at later on in the book. The following example demonstrates how to download the content pointed to by the URL as an ArrayBuffer:

Code Listing: 67

fetch(url)

  .then(request => request.arrayBuffer())

  .then(arrayBuffer => /* process buffer */);

Note: This example will only work in a browser.

Canvas

The 2-D context of the canvas lets you retrieve the bitmap data as an instance of Uint8ClampedArray:

Code Listing: 68

let canvas = document.getElementById('my_canvas');

let context = canvas.getContext('2d');

let imageData = context.getImageData(0, 0, canvas.width, canvas.height);

let uint8ClampedArray = imageData.data;

Note: This example will only work in a browser.

WebSockets

WebSockets let you send and receive binary data via ArrayBuffers:

Code Listing: 69

let socket = new WebSocket('ws://127.0.0.1:8081');

socket.binaryType = 'arraybuffer';

// Wait until socket is open

socket.addEventListener('open', function (event) {

  // Send binary data

  let typedArray = new Uint8Array(4);

  socket.send(typedArray.buffer);

});

// Receive binary data

socket.addEventListener('message', function (event) {

  let arrayBuffer = event.data;

  // process the buffer...

});

Note: This example will only work in a browser.

Scroll To Top
Disclaimer
DISCLAIMER: Web reader is currently in beta. Please report any issues through our support system. PDF and Kindle format files are also available for download.

Previous

Next



You are one step away from downloading ebooks from the Succinctly® series premier collection!
A confirmation has been sent to your email address. Please check and confirm your email subscription to complete the download.