Categories
Modern JavaScript

Best of Modern JavaScript — Symbols API

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 symbols.

Crossing Realms with Symbols

Symbols don’t travel well across realms.

Since there’re different global objects in different frames, we can’t use them across frames.

Special symbols like Symbol.iterator should work across different realms.

To get a symbol across different realms, we can use the Symbol.for method to get the symbol with a string.

For example, we can write:

const sym = Symbol.for('foo');

Then we can call Symbol.keyFor to get the string we passed into Symbol to create the symbol:

Symbol.keyFor(sym)

Special symbols like Symbol.iterator will return undefined if we call it with Symbol.keyFor :

Symbol.keyFor(Symbol.iterator)

Are Symbols Primitives or Objects?

Symbols are primitive values.

Symbols are like strings in that they can be used as property keys.

But they’re like objects in that each symbol has their own identity.

They’re immutable and they can be used as property keys.

However, it doesn’t have many properties of objects like prototypes and wrappers.

Symbol also can’t be examined by operators and methods like instance or Object.keys .

Why are Symbols Useful?

Symbols are useful for avoid clashes of identifiers.

This is easy since we can’t create the same symbol twice.

Are JavaScript Symbols Like Ruby’s Symbols?

JavaScript and Ruby symbols aren’t alike.

Ruby symbols are literals for creating values.

For instance, if we have:

`:foo` `==` `:foo`

then the expression returns true .

Symbol(‘foo’) !== Symbol(‘foo’) returns true .

So no 2 symbols are the same even though we passed in the same argument.

The Spelling of Well-Known Symbols

Well-known symbols are spelled this way because they’re used as normal property keys.

Symbol API

Symbol is a function that takes a string as its description.

So we can use it by writing:

const sym = Symbol('foo');

to get a new symbol.

Methods of Symbols

The only useful method in a symbol is the toString method.

Well-known Symbols

There’re several well-knowns that may be useful to us.

The Symbol.hasInstance method lets an object customize the behavior of the instanceof operator.

Symbol.toPrimitive is a method that lets us customize how it’s converted to a primitive value.

This is the 1st steep whenever something is being coerced into a primitive value.

Symbol.toStringTag is a method called by Object.prototype.toString to return the string description of an object.

Therefore, we can override it to provide our own string description.

Symbol.unscopables is a method that hides some properties with the with statement.

Symbol.iterator is a method that lets us define our own iterable to create an iterable object.

Once we added this method, we can iterate it with the for-of operator.

String Methods

String methods are forwarded to methods with the given symbols.

They’re forwarded as follows:

  • String.prototype.match(str, ...) is forwarded to str[Symbol.match]().
  • String.prototype.replace(str, ...) is forwarded to str[Symbol.replace]().
  • String.prototype.search(str, ...) is forwarded to str[Symbol.search](···).
  • String.prototype.split(str, ...) is forwarded to str[Symbol.split]().

Other Special Symbols

Symbol.species is a method to configure built-in methods to create objects similar to this .

Symbol.isConcatSpreadable is a boolean to configure whether Array.prototype.concat adds the indexed element as the result.

Conclusion

There’re many special symbols we can use to override the behavior of objects.

Categories
Modern JavaScript

Best of Modern JavaScript — Strings and Symbols

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 core features of JavaScript.

New String Methods

New string methods include code point and template literal methods.

There’s the String.raw method that returns the raw version of a string.

For instance, we can write:

String.raw`n`

to return the raw version of the string.

The slashes aren’t interpreted.

A function used for template literals is called a template tag.

The String.fromCodepoint method returns a string from the codepoints given.

For instance, we can use it by writing:

String.fromCodePoint(9731, 9733, 9842)

Then we get:

"☃★♲"

The String.prototype.normalize method lets us combine change code point combinations that have the same value to be the same.

For instance, we can write:

'u00F1'.normalize('NFC')

Then we get:

"ñ"

'NFC' is the format for normalizing a string and it’s the default one.

It means canonical decomposition followed by canonical composition.

Symbols

Symbols is a new primitive type for ES6.

We can create it with the Symbol factory function.

For instance, we can write:

`const` foo `=` `Symbol('`foo`');`

Every time we call the factory, a new symbol is created.

So no 2 symbols are the same.

These are useful as unique property keys.

For instance, the Symbol.iterator symbol is a special symbol for creating iterable objects.

We can write:

const iterableObject = {
  [Symbol.iterator]() {
    //···
  }
}
for (const x of iterableObject) {
  console.log(x);
}

to create the object and loop through it with for-of.

We can also use them as constants to represent concepts.

For example, we can write:

const COLOR_RED = Symbol('red');
const COLOR_GREEN = Symbol('green');
const COLOR_BLUE = Symbol('blue');

function getValue(color) {
  switch (color) {
    case COLOR_RED:
      return 1;
    case COLOR_BLUE:
      return 2;
    case COLOR_GREEN:
      return 2;
    default:
      throw new Error('Unknown color: ');
  }
}

We have to use them variable created from Symbol to reference a symbol since a new one will be created every time we call Symbol .

Symbols can’t be coerced to strings.

For instance, the following won’t work:

const foo = Symbol('foo');
const str = foo + '';

or

const foo = Symbol('foo');
const str = `${foo}`;

We’ll get the error ‘Uncaught TypeError: Cannot convert a Symbol value to a string’ in both examples.

We can use toString to convert it.

So we can write:

const foo = Symbol('foo');
const str = foo.toString();

Then str is 'Symbol(foo)’ .

The following operations are aware of symbols:

  • Reflect.ownKeys()
  • Property access via []
  • Object.assign()

And the following ignore symbols as keys:

  • Object.keys()
  • Object.getOwnPropertyNames()
  • for-in loop

Symbol ‘s string parameter is optional.

Everything symbol is unique, so:

Symbol() === Symbol()

returns false .

If we use the typeof operator with it:

typeof Symbol()

We get 'symbol' .

We can use symbols as property keys by writing:

const FOO = Symbol();
const obj = {
  [FOO]: 123
};

We can also define methods with it:

const FOO = Symbol();
const obj = {
  [FOO](){}
};

Conclusion

ES6 correctly identifies all Unicode characters.

Also, it introduced the symbol data type used as unique identifiers.

Categories
Modern JavaScript

Best of Modern JavaScript —Template Literals

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 template literals.

Template Literals

A template literal is a string literal where we can interpolate JavaScript expressions right into the string.

For example, we can write:

const firstName = 'james';
console.log(`Hello ${firstName}`);

Then we get:

'Hello james'

It’s delimited by backticks instead of single and double-quotes.

Escaping in Template Literals

The backslash is used for escaping inside template literals.

For example, we can write:

`\``

and get:

'`'

However, we can’t write:

`${`

since we’ll get a SyntaxError.

Other than that, they work like regular string literals.

Line terminators in template literals are always LF.

LF is the line feed character, which is n or U+000A which is used by Unix and macOS.

CR, which is r or U+900D is used by old Mac OS.

CRLF, which is rn is used by Windows.

They’re all normalized to LF in template literals.

Tagged Template Literals

Tagged template literals is putting a tag in front of a template literal.

For example, we can write:

tagFunction`Hello ${firstName} ${lastName}`

where tagFunction is a function that we call on the string.

It’s the same as writing:

`tagFunction(['Hello ',` `' '],` `firstName,` `lastName)`

The tag takes the template string and substitutions for the placeholder.

The substitutions are done at runtime.

But template strings are known statically at compile time.

Tag functions also gets the raw version which isn’t interpreted.

It also gets the final version where the backslashes are special.

Raw Strings

ES6 has the String.raw template tag for raw strings.

It returns the string with all the backslashes intact.

For example, we can write:

const str = String.raw`Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus fringilla mauris vel erat facilisis lacinia. n

Vivamus tincidunt, massa sit amet fermentum rhoncus, nibh mi convallis elit, eget bibendum eros ipsum elementum purus. Donec tristique ligula mi, non dapibus quam finibus et. Orci varius natoque penatibus et magnis dis parturient montes`

with the n intact.

This is useful whenever we need to keep the backslashes in our strings.

We can use the sh template tag to run shell commands.

For example, we can write:

`const` `proc` `=` ``sh`ps ax | grep ${pid}`;``

Packages like the sh Template Tag package provides us with the sh template tag to run commands with the tag.

Facebook GraphQL

Facebook Relay lets us make queries to a GraphQL API.

For example, we can use it by writing:

import React from "react"
import { createFragmentContainer, graphql, QueryRenderer } from "react-relay"
import environment from "./lib/createRelayEnvironment"
import ArtistHeader from "./ArtistHeader"

export default function ArtistRenderer({artistID}) {
  return (
    <QueryRenderer
      environment={environment}
      query={graphql`
        query QueryRenderersArtistQuery($artistID: String!) {
          # The root field for the query
          artist(id: $artistID) {
            # A reference to your fragment container
            ...ArtistHeader_artist
          }
        }
      `}
      variables={{artistID}}
      render={({error, props}) => {
        if (error) {
          return <div>{error.message}</div>;
        } else if (props) {
          return <Artist artist={props.artist} />;
        }
        return <div>Loading</div>;
      }}
    />
  );
}

using the example from https://relay.dev/.

We just pass in our own template string into the graphql tag to make the request.

Conclusion

Template strings are useful.

They let us interpolate expressions and call them with tags.

Categories
Modern JavaScript

Best Features of ES2019 — Catch, Sort, and toString

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 best features of ES2019.

Catch Binding

catch binding is now optional with ES2019.

For instance, we can write:

let jsonData;
try {
  jsonData = JSON.parse(str);
} catch {
  jsonData = {};
}

Then we don’t need to get the thrown object in the parentheses after the catch .

However, other kinds of errors that aren’t related to parsing will be silently ignored, since we just set it the jsonData object to an empty object.

Another example would be to get a deeply nested property.

For instance, we can just write:

function logId(item) {
  let id;
  try {
    id = item.data.id;
  } catch {
    console.log(id);
  }
}

to let us safely get the deeply nested id property of item to id if it exists.

If it doesn’t exist, then the code in the catch block is run.

Stable Array.prototype.sort()

The Array.prototype.sort method is now guaranteed to be stable.

So if something is considered to be the same when sorting, then they’ll stay in the same order in engines that supports ES2019.

If we have:

const arr = [{
    key: 'foo',
    value: 1
  },
  {
    key: 'bar',
    value: 2
  },
  {
    key: 'foo',
    value: 3
  },
];
arr.sort((x, y) => x.key.localeCompare(y.key, 'en'));

Then arr would be in the same order regardless of which environment it’s in.

JSON.stringify

JSON.stringify can now take a lone surrogate.

So if we have:

JSON.stringify('u{D800}')

Then we get:

""ud800""

JSON superset

JSON strings now can have the '"u2028"' character.

Function.prototype.toString Changes

Function.prototype.toString now has some changes since ES2019.

The functions we defined returns a string with the original source code.

If we call toString with a built-in function, then we see [native code] .

For instance, if we have:

isNaN.toString()

Then we get:

"function isNaN() { [native code] }"

Functions created dynamically with the Function and GeneratorFunction engines must create the appropriate source code and attach it to functions.

All other cases would throw a type error.

Conclusion

Catch binding, function toString , sort methods all have changes with ES2019.

Categories
Modern JavaScript

Best Features of ES2019 — Arrays, Strings, and Symbols

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 best features of ES2019.

Array.prototype.flat

The flat method on the array instance is a useful method in ES2019.

It lets us flatten arrays one or more levels deep.

For instance, we can write:

const arr = [1, 2, [3, 4],
  [
    [5, 6]
  ],
  [7, 8]
].flat(0)

Then there’s no change to the array.

If we have:

const arr = [1, 2, [3, 4],
  [
    [5, 6]
  ],
  [7, 8]
].flat(1)

Then we get:

[
  1,
  2,
  3,
  4,
  [
    5,
    6
  ],
  7,
  8
]

And if we have:

const arr = [1, 2, [3, 4],
  [
    [5, 6]
  ],
  [7, 8]
].flat(2)

We get:

[
  1,
  2,
  3,
  4,
  5,
  6,
  7,
  8
]

1 is the default value.

Array.prototype.flatMap()

flatMap combines the map and flat methods together.

For instance, we can write:

const arr = [1, 2, 3].flatMap(x => x)

Then we get:

[
  1,
  2,
  3
]

If we have:

const arr = [1, 2, 3].flatMap(x => [x])

then we get the same thing.

And if we have:

const arr = [1, 2, 3].flatMap(x => [[x]])

then we get:

[
  [
    1
  ],
  [
    2
  ],
  [
    3
  ]
]

The callback lets us map the entries to what we want and call flat on each entry.

Object.fromEntries()

The Object.fromEntries() method lets us create an object from an array of key-value arrays.

For instance, we can write:

const obj = Object.fromEntries([
  ['a', 1],
  ['b', 2]
]);

Then obj is {a: 1, b: 2} .

It does the opposite action from Object.entries() :

const obj = {
  a: 1,
  b: 2,
};

const arr = Object.entries(obj);

Then we get:

[
  [
    "a",
    1
  ],
  [
    "b",
    2
  ]
]

String.prototype.trimStart

The trimStart string instance method lets us trim the whitespace at the start of a string.

For instance, we can write:

const str = '  foo  '.trimStart()

Then we get:

'foo  '

String.prototype.trimEnd

The trimEnd method lets us trim the end of a string.

For instance, we can write:

'  foo'

trimLeft and trimRight are equivalent to trimStart and trimEnd respectively.

But those are legacy methods since we want consistent with padStart and padEnd .

What are Whitespaces?

The following characters are whitespaces:

  • <TAB> (character tabulation, U+0009)
  • <VT> (line tabulation, U+000B)
  • <FF> (form feed, U+000C)
  • <SP> (space, U+0020)
  • <NBSP> (no-break space, U+00A0)
  • <ZWNBSP> (zero-width bo break space, U+FEFF)
  • Any other Unicode character with the property White_Space in category Space_Separator (Zs).

Line terminators are also whitespaces. They include:

  • <LF> (line feed, U+000A)
  • <CR> (carriage return, U+000D)
  • <LS> (line separator, U+2028)
  • <PS> (paragraph separator, U+2029)

Symbol.prototype.description

The Symbol.prototype.description method returns the description of the symbol.

For instance, if we have:

const sym = Symbol('description');

console.log(sym.description)

Then 'description’ is logged.

Conclusion

ES2019 introduced various arrays, strings, and symbol methods.