How to Read the ECMAScript Specification

Living Document,

This version:
https://timothygu.me/es-howto/
Issue Tracking:
GitHub
Inline In Spec
Author:
Timothy Gu

Abstract

The ECMAScript Language specification (aka. the JavaScript specification, or ECMA-262) is a great resource for learning the intricacies of how JavaScript works. However, it is a huge text that can be confusing and intimidating at first. This document aims to make it easier to get started with reading the best JavaScript language reference available.

1. Prelude

So you’ve decided that reading a bit of ECMAScript spec each day is good for your health. Maybe it was a New Year’s resolution, or maybe it was just your doctor’s prescription. Whatever it is, welcome aboard!

Note: In this document, I will only use the term "ECMAScript" to refer to the specification itself, and "JavaScript" anywhere else. However, both terms refer to the same thing. (There are some historical distinctions between ECMAScript and JavaScript, but discussing that is outside the scope of this document, and you can Google that distinction very easily.)

1.1. Why should I read the ECMAScript specification

The ECMAScript specification is the authoritative source of the behavior of all JavaScript implementations, whether it be in your browser [WHATISMYBROWSER], on your server through Node.js [NODEJS], or on your IoT device [JOHNNY-FIVE]. All developers of JavaScript engines depend on the spec to make sure their shiny new feature works as intended, in the same way other JavaScript engines do.

But I claim that the utility of the spec extends beyond the mythical creatures known as "developers of JavaScript engines." In fact, I say that it is useful to you, the average JavaScript coder, and you just haven’t realized it.

Suppose you discover the following peculiar juxtaposition at work one day

> Array.prototype.push(42)
1
> Array.prototype
[ 42 ]
> Array.isArray(Array.prototype)
true
> Set.prototype.add(42)
TypeError: Method Set.prototype.add called on incompatible receiver #<Set>
    at Set.add (<anonymous>)
> Set.prototype
Set {}

and are very confused why a method works on its prototype, but another method does not work on its prototype. Google unfortunately always fails when you need it the most, and so does the ever-helpful Stack Overflow.

Reading the spec can help.

Or, you might wonder just how the heck does the notorious loose equality operator (==) actually function (using the word "function" loosely here [WAT]). Ever the studious software engineer, you look it up on MDN, only to find its paragraphs of explanation hurt your eyes more than they help [MDN].

Reading the spec can help.

On the other hand, I do not recommend reading the ECMAScript specification to developers new to JavaScript. If you are new to JavaScript, then play around with the web! build some web apps! or some JavaScript-based nannycams! or anything! and consider returning to this document when you have either experienced enough JavaScript warts or gotten rich enough to not have to worry about JavaScript.

Okay, now you know specifications can be very helpful tools in helping you understand the intricacies in a language or platform. But what exactly falls into in the realm of the ECMAScript specification?

1.2. What belongs to the ECMAScript specification, and what does not

The textbook answer to this question is "only language features go into the ECMAScript specification." But that doesn’t help, since that’s like saying "JavaScript features are JavaScript." And I’m not one for tautologies [XKCD-703].

Instead, what I’m going to do is list some things that are commonly seen in JavaScript apps, and tell you if each of them is a language feature or not.

Syntax of syntactic elements (i.e., what a valid for..in loop looks like)
Semantics of syntactic elements (i.e., what typeof null, or { a: b } returns)
import a from 'a'; [1]
Object, Array, Function, Number, Math, RegExp, Proxy, Map, Promise, ArrayBuffer, Uint8Array, globalThis, ...
console, setTimeout(), setInterval(), clearTimeout(), clearInterval() [2]
Buffer, process, global* [3]
module, exports, require(), __dirname, __filename [4]
window, alert(), confirm(), the DOM (document, HTMLElement, addEventListener(), Worker, ...) [5]
[1] The ECMAScript spec specifies the syntax of such declarations and what they are supposed to mean, but does not specify how the module is loaded.

[2] These things are available in both browsers and Node.js, but are non-standard. For Node.js, they are documented/specified by its documentation. For browsers, console is specified by the Console Standard [CONSOLE], while the rest is specified by the HTML Standard [HTML].

[3] These are all Node.js-only globals, documented/specified by its documentation. * Note that unlike global, globalThis is a part of ECMAScript and implemented in browsers as well.

[4] These are Node.js-only module-wide "globals", documented/specified by its documentation.

[5] These are all browser-only things.

1.3. Before going any further, where is the ECMAScript specification?

When you Google "ECMAScript specification" you see so many results, all claiming to be the legitimate specification. Which one should you read??

Tl;dr, more likely than not, the specification published at tc39.es/ecma262/ is the one you want [ECMA-262].

Long version:

The ECMAScript language specification is developed by a group of people from diverse backgrounds, known as the Ecma International Technical Committee 39 (or as they are more familiarly known, TC39 [TC39]). TC39 maintains the latest specification for the ECMAScript language at tc39.es [ECMA-262].

What complicates the matter is that, every year, TC39 picks a point in time to take a snapshot of the spec to become the ECMAScript Language Standard of that year, along with an edition number. For example, the ECMAScript® 2019 Language Specification (ECMA-262, 10th edition) [ECMA-262-2019] (popularly known as ES10 or ES2019) is simply the spec as seen at tc39.es [ECMA-262] in June 2019, put into formaldehyde, properly shrinkwrapped and PDFified for the permanent archives.

Because of that, unless you want your web application to run on only browsers from June 2019 that are put into formaldehyde, properly shrinkwrapped, and PDFified for the permanent archives, you want to always look at the latest spec at tc39.es [ECMA-262]. But if you want to (or have to) support older browsers or Node.js versions, then referencing the older specifications may help.

Note: The ISO/IEC also republishes the ECMAScript Language Standard as ISO/IEC 22275 [ISO-22275-2018]. Don’t worry about it though, since the standard is basically a hyperlink to [ECMA-262].

The ECMAScript specification talks about a HUGE amount of things. Even though its authors try their best to separate it into logical chunks, it’s still a huge text.

Personally, I like to divide the spec into five parts:

but that’s not how the spec organizes it. Instead, it puts the first bullet point in §5 Notational Conventions through §9 Ordinary and Exotic Objects Behaviours, the next three in an interleaved form in §10 ECMAScript Language: Source Code through §15 ECMAScript Language: Scripts and Modules, like

  • §13.6 The if Statement Grammar productions

    • §13.6.1-6 Static semantics

    • §13.6.7 Runtime sematics

  • §13.7 Iteration Statements Grammar productions

    • §13.7.1 Shared static and runtime semantics

    • §13.7.2 The do-while Statement

      • §13.7.2.1-5 Static semantics

      • §13.7.2.6 Runtime semantics

    • §13.7.3 The while Statement

      • ...

while the APIs are spread out through clauses §18 The Global Object through §26 Reflection.

At this point, I’d like to point out that absolutely no one reads the spec from top to bottom. Rather, only look at the section corresponding to what you are trying to look for, and in that section only look at what you need. Try to determine which one of the five big sections your specific question relates to; and if you are having trouble determining which one it is, ask yourself the question "at which time is this (whatever you are trying to confirm) evaluated?" which may help. Don’t worry, navigating the spec will only become easier with practice.

2. Runtime semantics

Runtime semantics of The Language and APIs are the biggest parts of the spec, and usually the ones people have the most questions about.

On the whole, reading these sections in the spec is pretty straightforward. However, the spec employs a lot of shorthands that are pretty icky for people just starting out (at least for me). I’m going to try explaining some of these conventions, and then apply them to a usual workflow of figuring out how several things work.

2.1. Algorithm steps

Most runtime semantics in ECMAScript are specified by a series of algorithm steps, not unlike pseudocode but in a much more precise form.

A sample set of algorithm steps are:

  1. Let a be 1.

  2. Let b be a+a.

  3. If b is 2, then

    1. Hooray! Arithmetics isn’t broken.

  4. Else

    1. Boo!

Further reading: §5.2 Algorithm Conventions

2.2. Abstract operations

You will sometimes see a function-like thing being called in the spec. The first step of the Boolean() function is:

When Boolean is called with argument value, the following steps are taken:

  1. Let b be ! ToBoolean(value).

  2. ...

That "ToBoolean" function is called an abstract operation: it’s abstract because it is not actually exposed as a function to JavaScript code. It is simply a notation spec writers invented to allow them to not write the same things over and over again.

Note: For now, don’t worry about the ! that comes before ToBoolean. We’ll talk about it later, in § 2.4 Completion Records; ? and !.

Further reading: §5.2.1 Abstract Operations

2.3. What is [[This]]

From time to time, you may see the [[Notation]] being used like "Let proto be obj.[[Prototype]]." This notation can technically mean several different things depending on the context in which it appears, but you would go a long way with the understanding that this notation refers to some internal property that is not observable through JavaScript code.

Precisely, it can mean three different things, which I will illustrate with examples from the specification. Feel free to skip them for now, however.

2.3.1. A field of a Record

The ECMAScript spec uses the term Record to refer to a key-value map with a fixed set of keys – a bit like a structure in C-like languages. Each key-value pair of a Record is called a field. Because Records can only appear in specifications and not in actual JavaScript code, it makes sense to use the [[Notation]] to refer to fields of a Record.

Notably, Property Descriptors are also modeled as Records with fields [[Value]], [[Writable]], [[Get]], [[Set]], [[Enumerable]], and [[Configurable]]. The IsDataDescriptor abstract operation uses this notation extensively:

When the abstract operation IsDataDescriptor is called with Property Descriptor Desc, the following steps are taken:

  1. If Desc is undefined, return false.

  2. If both Desc.[[Value]] and Desc.[[Writable]] are absent, return false.

  3. Return true.

Another concrete example of Records can be found in the next section, § 2.4 Completion Records; ? and !.

Further reading: §6.2.1 The List and Record Specification Types

2.3.2. An internal slot of a JavaScript Object

JavaScript Objects may have so-called internal slots that the specification uses to keep data in them. Like Record fields, these internal slots also cannot be observed using JavaScript, but some of them may be exposed through implementation-specific tools like Google Chrome’s DevTools. Thus, it makes sense also to use the [[Notation]] to describe internal slots.

The specifics of internal slots will be covered in § 2.5 JavaScript Objects. For now, don’t worry too much about what they are used for, but do note the following example.

Most JavaScript Objects have an internal slot [[Prototype]] that refers to the Object they inherit from. The value of this internal slot is usually the value that Object.getPrototypeOf() returns. In the OrdinaryGetPrototypeOf abstract operation, the value of this internal slot is accessed:

When the abstract operation OrdinaryGetPrototypeOf is called with Object O, the following steps are taken:

  1. Return O.[[Prototype]].

Note: Internal slots of Objects and Record fields are identical in appearance, but they can be disambiguated by looking at the precedent of this notation (the part that came before the dot), whether it is an Object or a Record. This is usually fairly obvious from the surrounding context.

2.3.3. An internal method of a JavaScript Object

JavaScript Objects may also have so-called internal methods. Like internal slots, these internal methods are not directly observable through JavaScript. Thus, it makes sense also to use the [[Notation]] to describe internal methods.

The specifics of internal methods will be covered in § 2.5 JavaScript Objects. For now, don’t worry too much about what they are used for, but do note the following example.

All JavaScript functions have an internal method [[Call]] that runs that function. The Call abstract operation has the following step:

  1. Return ? F.[[Call]](V, argumentsList).

where F is a JavaScript function object. In this case, the [[Call]] internal method of F is itself called with arguments V and argumentsList.

Note: This third sense of the [[Notation]] can be distinguished from the rest by looking like a function call.

2.4. Completion Records; ? and !

Every runtime semantic in the ECMAScript spec either explicitly or implicitly returns a Completion Record that reports its outcome. This Completion Record is a Record that has three possible fields:

Note: Two brackets are used to denote fields of Records. See § 2.3.1 A field of a Record for a primer on Records and the notations associated with them.

A Completion Record whose [[Type]] is normal is called a normal completion. Every Completion Record other than a normal completion is also known as an abrupt completion.

Most of the time, you are only going to deal with abrupt completions whose [[Type]] is throw. The other three abrupt completion types are only useful in seeing how a specific syntactic element is evaluated. In fact, you will never see any of those other types in the definition of a built-in function, since break/continue/return don’t work across function boundaries.

Further reading: §6.2.3 The Completion Record Specification Type


Because of the definition of Completion Records, niceties in JavaScript like bubbling errors until a try-catch block don’t exist in the spec. In fact, errors (or more precisely abrupt completions) are handled explicitly.

Without any shorthands, the spec text for an ordinary call to an abstract operation that may either return a computation result or throw an error would look like:

A few steps that call an abstract operation that may throw without any shorthands:

  1. Let resultCompletionRecord be AbstractOp().

    Note: resultCompletionRecord is a Completion Record.

  2. If resultCompletionRecord is an abrupt completion, return resultCompletionRecord.

    Note: Here, resultCompletionRecord is directly returned if it is an abrupt completion. In other words, errors thrown in AbstractOp are forwarded, and the remaining steps aborted.

  3. Let result be resultCompletionRecord.[[Value]].

    Note: After ensuring we got a normal completion, we can now unwrap the Completion Record to get the actual result of the computation we need.

  4. result is the result we need. We can now do more things with it.

This may possibly vaguely remind you of manual error handling in C:

int result = abstractOp();              // Step 1
if (result < 0)                         // Step 2
  return result;                        // Step 2 (continued)
                                        // Step 3 is unneeded
// func() succeeded; carrying on...     // Step 4

But to reduce these heavily boilerplated steps, editors of the ECMAScript spec added a few shorthands. Since ES2016, the same spec text can instead be written in the following two equivalent ways:

A few steps that call an abstract operation that may throw with ReturnIfAbrupt:

  1. Let result be AbstractOp().

    Note: Here, just like the step 1 in the previous example, result is a Completion Record.

  2. ReturnIfAbrupt(result).

    Note: ReturnIfAbrupt deals with any possible abrupt completions by forwarding it, and unwraps the result to its [[Value]] automatically.

  3. result is the result we need. We can now do more things with it.

or, even more concisely, with a special question mark (?) notation:

A few steps that call an abstract operation that may throw with a question mark (?):

  1. Let result be ? AbstractOp().

    Note: In this notation we don’t deal with Completion Records at all. The ? shorthand handles everything for us, and result is ready to use immediately after.

  2. result is the result we need. We can now do more things with it.


Sometimes, it can convey more information to the reader about the spec’s intent if we know that a particular call to AbstractOp will never return an abrupt completion. In those cases, an exclamation mark (!) is used:

A few steps that call an abstract operation that cannot ever throw with an exclamation mark (!):

  1. Let result be ! AbstractOp().

    Note: While ? forwards any errors we may have gotten, ! asserts that we never get any abrupt completions from this call, and it would be a bug in the specification if we did. Like the case with ?, we don’t deal with Completion Records at all. result is ready to use immediately after.

  2. result is the result we need. We can now do more things with it.

CAUTION

The ! can admittedly become pretty confusing if it looks like a valid JavaScript expression:

  1. Let b be ! ToBoolean(value).

— Excerpted from Boolean().

Here, the ! just means that we are certain that this call to ToBoolean will never return an exception, not that the result is inverted!

Further reading: §5.2.3.4 ReturnIfAbrupt Shorthands.

2.5. JavaScript Objects

In ECMAScript, every Object has a set of internal methods that the rest of the specification call on to do certain tasks. A few of these internal methods that all Objects have are:

(an exhaustive list is available in §6.1.7.2 Object Internal Methods and Internal Slots).

Based on this definition, function objects (or just "functions") are simply Objects that additionally have the [[Call]] internal method, and possibly the [[Construct]] internal method too; for this reason they are also known as callable objects.

The spec then divides all Objects into two camps: ordinary objects and exotic objects. Most of the objects you encounter are ordinary objects, which means that all of their internal methods are the default ones specified in §9.1 Ordinary Object Internal Methods and Internal Slots.

However, ECMAScript spec also defines a few kinds of exotic objects, which may override the default implementations of those internal methods. There are certain minimal constraints put on what exotic objects are allowed to do, but in general the overriden internal methods can do a lot of acrobatics without going against the spec.

Array objects are one kind of these exotic objects. Some special semantics around the length property of Array objects cannot be achieved using the instruments available to ordinary objects.

One of them is the fact that setting the length property of an Array object can remove properties from the object, but the length property seems to be just an ordinary data property. In contrast, new Map().size is only a getter function specified on Map.prototype, and does not have the magical properties [].length does.

> const arr = [0, 1, 2, 3];
> console.log(arr);
[ 0, 1, 2, 3 ]
> arr.length = 1;
> console.log(arr);
[ 0 ]
> console.log(Object.getOwnPropertyDescriptor([], "length"));
{ value: 1,
  writable: true,
  enumerable: false,
  configurable: false }
> console.log(Object.getOwnPropertyDescriptor(new Map(), "size"));
undefined
> console.log(Object.getOwnPropertyDescriptor(Map.prototype, "size"));
{ get: [Function: get size],
  set: undefined,
  enumerable: false,
  configurable: true }

This behavior is achieved by overriding the [[DefineOwnProperty]] internal method. See §9.4.2 Array Exotic Objects for details.

The ECMAScript spec also allows other specs to define their own exotic objects. It is through this mechanism the limitations browsers put on cross-origin API access are specified (see WindowProxy) [HTML]. It is also possible for JavaScript programmers to create their own exotic objects through the Proxy API.


JavaScript Objects may also have internal slots defined to contain certain types of values. I tend to think of internal slots as Symbol-named properties that are hidden even to Object.getOwnPropertySymbols(). Both ordinary objects and exotic objects are allowed to have internal slots.

In § 2.3.2 An internal slot of a JavaScript Object, I mentioned an internal slot called [[Prototype]] that most Objects have. (In fact, all ordinary objects and even some exotic objects like Array objects have it.) But we also know that there is an internal method called [[GetPrototypeOf]] that I briefly described above. What is the difference?

The keyword here is most: while most objects have the [[Prototype]] internal slot, all objects implement the [[GetPrototypeOf]] internal method. Notably, Proxy objects do not have their own [[Prototype]], and its [[GetPrototypeOf]] internal method instead defer to either the registered handler or the prototype of its target, stored in the [[ProxyTarget]] internal slot of the Proxy object.

For this reason, when dealing with Objects, it is almost always a good idea to refer to the appropriate internal method rather than directly looking at the value of an internal slot.


Another way of thinking about the relations between Objects, internal methods, and internal slots are through a classical object-oriented lens. "Object" is like an interface specifying several internal methods that must be implemented. Ordinary objects provide default implementations, which exotic objects may override either partially or fully. On the other hand, internal slots are like instance variables of an Object – the implementation details of that Object.

All of these relations are summarized by the following UML diagram (click to enlarge):

Boxes denoting concepts and connections between them denoting hierarchy

2.6. Example: String.prototype.substring()

Now that we have a pretty good understanding of how the spec is organized and written, let’s practice!

Suppose I now have the following question:

Without running the code, what does the following code fragment return?

String.prototype.substring.call(undefined, 2, 4)

This is a pretty tricky question. It seems that there are two plausible outcomes:

  1. String.prototype.substring() could first cast undefined to the string "undefined", and then take characters at positions two and three (i.e., the interval [2, 4)) of that string to result in "de"

  2. On the other hand, String.prototype.substring() may just as reasonably throw an error, thus rejecting undefined as an input.

Unfortunately, MDN also doesn’t really offer any insights on how the function behaves when the this value isn’t a string.

Spec to the rescue! After typing in substring in the search box in the top-left corner on the spec [ECMA-262], we arrive at §21.1.3.22 String.prototype.substring ( start, end ), which is the normative specification of how the function works.

Before reading the algorithm steps, let’s think about what we know first. I assume we have a basic understanding of how str.substring() ordinarily works, which is to return a part of the given string. What we are not really sure of right now, is how it acts with the this value being undefined. So, we would specifically look for algorithm steps that address the this value.

Lucky for us, the first step of the algorithm for String.prototype.substring() deals specifically with the this value:

  1. Let O be ? RequireObjectCoercible(this value).

The ? shorthand allows us to conclude that there may be some cases where the RequireObjectCoercible abstract operation may actually throw exceptions, since otherwise ! would have been used instead. In fact, if it throws an error it would correspond with our second hypothesis above! With hope, we look into what RequireObjectCoercible does by clicking on the hyperlink.

The RequireObjectCoercible abstract operation is a little odd. Unlike most abstract operations, it is defined through a table rather than steps:

Argument Type Result
Undefined Throw a TypeError exception.
... ...

No matter though – in the row corresponding to Undefined (the type of the this value we passed to substring()) the spec says that RequireObjectCoercible should throw an exception. And because the ? notation is used in the definition of the function, we know that the thrown exception must bubble up to the caller of the function. Bingo!

And there we have our answer: the given code fragment throws a TypeError exception.

The spec only specifies the type of the Error thrown, not what message it contains. This means that implementations can have different error messages, maybe even localized ones.

On Google’s V8 6.4 (included in Google Chrome 64), for example, the message is

TypeError: String.prototype.substring called on null or undefined

while Mozilla Firefox 57.0 gives a somewhat less helpful

TypeError: can’t convert undefined to object

At the same time, ChakraCore version 1.7.5.0 (the JavaScript engine in Microsoft Edge) takes V8’s route and throws

TypeError: String.prototype.substring: 'this' is null or undefined

2.7. Example: Can Boolean() and String() ever throw exceptions?

When writing mission-critical code, one must put exception handling at the forefront in programming. As such, the question of "can some built-in function ever throw an exception?" may be oft-pondered.

In this example, we shall try to answer this question for two language built-in functions, Boolean() and String(). We will only look at direct calls to those functions, not the cases of new Boolean() and new String() which form boxed objects – easily one of the most undesirable features in JavaScript and a very much discouraged practice in pretty much all JS style guides out there [YDKJS].

After navigating to the section for Boolean() in the spec, we see that the algorithm seems to be fairly short:

When Boolean is called with argument value, the following steps are taken:

  1. Let b be ! ToBoolean(value).

  2. If NewTarget is undefined, return b.

  3. Let O be ? OrdinaryCreateFromConstructor(NewTarget, "%BooleanPrototype%", « [[BooleanData]] »).

  4. Set O.[[BooleanData]] to b.

  5. Return O.

But on the other hand, it’s not totally straightforward, with some complex acrobatics around OrdinaryCreateFromConstructor involved. More importantly, there is a ? shorthand in step 3 that may indicate this function can throw errors in certain cases. Let’s take a closer look.

Step 1 casts value (the function argument) to a Boolean value. Interestingly there isn’t a ? or ! shorthand for this step, but usually not having a Completion Record shorthand means the same thing as !. So step 1 cannot throw an exception.

Step 2 checks if something called NewTarget is undefined. NewTarget is the spec equivalent for the new.target meta property that was first added in ES2015, allowing the spec to distinguish between a new Boolean() call (where it is Boolean) and a Boolean() call (where it is undefined). Because we are only looking at direct calls to Boolean() at this moment, we know that NewTarget is always going to be undefined, and the algorithm will always return b straightaway without any additional processing.

Because calling Boolean() without new can only access the first two steps in the algorithm for Boolean(), neither of which can throw exceptions, we conclude that Boolean() never throws exceptions no matter what the input is.


Let’s turn our attention to String():

When String is called with argument value, the following steps are taken:

  1. If no arguments were passed to this function invocation, let s be "".

  2. Else,

    1. If NewTarget is undefined and Type(value) is Symbol, return SymbolDescriptiveString(value).

    2. Let s be ? ToString(value).

  3. If NewTarget is undefined, return s.

  4. Return ? StringCreate(s, ? GetPrototypeFromConstructor(NewTarget, "%StringPrototype%")).

From our experience with doing the same kind of analysis with the Boolean() function, we know that NewTarget will always be undefined for our case, and therefore the last step can be skipped from our consideration. We also know that Type and SymbolDescriptiveString are safe as well, since abrupt completions are not handled for either of them. Yet, there is still a tell-tale ? preceding the call to the ToString abstract operation. Let’s take a closer look.

Like RequireObjectCoercible we looked at earlier, ToString(argument) is also defined with a table:

Argument Type Result
Undefined Return "undefined".
Null Return "null".
Boolean If argument is true, return "true".

If argument is false, return "false".

Number Return NumberToString(argument).
String Return argument.
Symbol Throw a TypeError exception.
Object

Apply the following steps:

  1. Let primValue be ? ToPrimitive(argument, hint String).

  2. Return ? ToString(primValue).

At the point where ToString is called in String(), value can be any value other than a Symbol (which is filtered out in the step immediately before). Yet, there still remain two ? in the row for Object. We can follow the link to ToPrimitive and beyond, and see that there are in fact a lot of opportunities for an error to be thrown if value is an Object:

Several examples where String() throws
// Spec stack trace:
//   OrdinaryGet step 8.
//   Ordinary Object’s [[Get]]() step 1.
//   GetV step 3.
//   GetMethod step 2.
//   ToPrimitive step 2.d.

String({
  get [Symbol.toPrimitive]() {
    throw new Error("Breaking JavaScript");
  }
});
// Spec stack trace:
//   GetMethod step 4.
//   ToPrimitive step 2.d.

String({
  get [Symbol.toPrimitive]() {
    return "Breaking JavaScript";
  }
});
// Spec stack trace:
//   ToPrimitive step 2.e.i.

String({
  [Symbol.toPrimitive]() {
    throw new Error("Breaking JavaScript");
  }
});
// Spec stack trace:
//   ToPrimitive step 2.e.iii.

String({
  [Symbol.toPrimitive]() {
    return { "breaking": "JavaScript" };
  }
});
// Spec stack trace:
//   OrdinaryToPrimitive step 5.b.i.
//   ToPrimitive step 2.g.

String({
  toString() {
    throw new Error("Breaking JavaScript");
  }
});
// Spec stack trace:
//   OrdinaryToPrimitive step 5.b.i.
//   ToPrimitive step 2.g.

String({
  valueOf() {
    throw new Error("Breaking JavaScript");
  }
});
// Spec stack trace:
//   OrdinaryToPrimitive step 6.
//   ToPrimitive step 2.g.

String(Object.create(null));

So for String(), our conclusion is that it never throws exceptions for primitive values, but may throw errors for Objects.

2.8. Example: typeof operator

So far, we’ve only analyzed API functions, let’s try something different.

To be written. <https://github.com/TimothyGu/es-howto/issues/2>

Glossary

Common abstract operations

ArrayCreatelength [ , proto ] ) (spec)

Create an array object of length length, with proto as the value of the [[Prototype]] internal slot. If proto is not specified, %ArrayPrototype% in the current realm is used. Equivalent to new Array(length) if the Array constructor and all of its properties have not been monkeypatched, and proto is not specified or %ArrayPrototype% in the current realm.

CallF, V [ , argumentsList ] ) (spec)
ConstructF [ , argumentsList [ , newTarget ] ] ) (spec)
GetO, P ) (spec)
HasPropertyO, P ) (spec)

Call the corresponding internal method on F or O with the rest of the arguments forwarded. Equivalent to the corresponding method on the Reflect object.

DefinePropertyOrThrowO, P, desc ) (spec)
DeletePropertyOrThrowO, P ) (spec)

Call the corresponding internal method ([[DefineOwnProperty]] and [[Delete]], respectively) on O with the rest of the arguments forwarded, and throw an exception if the operation failed and the internal method returned false.

GetVV, P ) (spec)

Returns Get(V, P), with V converted to an Object with ToObject first if necessary. Equivalent to V[P].

HasOwnPropertyO, P ) (spec)

Returns whether O has an own property named P, by calling O.[[GetOwnProperty]](P). Equivalent to Object.prototype.hasOwnProperty.call(O, P).

InvokeV, P [ , argumentsList ] ) (spec)

Call the method named P on V using argumentsList. Equivalent to V[P](...argumentsList). Unlike in Call, P here is a property key.

IsArrayargument ) (spec)

Returns whether argument is an Array exotic object, or if argument is a Proxy exotic object, whether argument’s innermost [[ProxyTarget]] internal slot is an Array exotic object. Equivalent to Array.isArray(argument).

IsCallableargument ) (spec)

Returns whether argument is a callable object, otherwise known as a function object. Equivalent to typeof argument === 'function' (with the exception of document.all, which is an exotic object with several special behaviors; see §B.3.7 The [[IsHTMLDDA]] Internal Slot).

IsConstructorargument ) (spec)

Returns whether argument is a function object with a [[Construct]] internal method.

ReturnIfAbruptargument ) (spec)

Check if argument is an abrupt completion (like a thrown exception), and if so return that abrupt completion (and allows the exception to bubble up). Otherwise if argument is a normal completion, unwrap that Completion Record and set argument to argument.[[Value]].

See also: § 2.4 Completion Records; ? and !.

StringCreatevalue, prototype ) (spec)

Returns a boxed String Object corresponding to the String value, with the [[Prototype]] internal slot of the resulting object being prototype. Equivalent to new String(value) if prototype is the %StringPrototype% of the current realm.

ToBooleanargument ) (spec)

Returns argument coerced to a Boolean. Equivalent to !!argument.

ToIntegerargument ) (spec)

Returns ToNumber(argument), then truncated (i.e., rounded to 0) to become an integer. Equivalent to Math.trunc(argument).

ToInt8argument ) (spec)
ToUint8argument ) (spec)
ToInt16argument ) (spec)
ToUint16argument ) (spec)
ToInt32argument ) (spec)
ToUint32argument ) (spec)

Returns argument converted to an integer of the specified bits and signedness using truncation.

ToUint8Clampargument ) (spec)

Returns argument converted to an integer of the range [0, 255] using rounding and clamping.

ToNumberargument ) (spec)

Returns argument coerced to a Number. Equivalent to +argument.

ToObjectargument ) (spec)

Returns argument coerced to an Object, using boxed primitive objects if necessary. Equivalent to Object(argument) other than the cases where argument is undefined or null.

ToPrimitiveinput [ , PreferredType ] ) (spec)

Returns input coerced to a primitive (i.e., non-Object) value, optionally using the type hint given through PreferredType. Exact semantics of this abstract operation vary depending on the PreferredType.

ToStringargument ) (spec)

Returns argument coerced to a String. Equivalent to `${argument}`.

Care must be taken to realize that neither String(argument) nor argument + '' is fully equivalent to ToString. String() converts Symbol values to their String descriptions, while ToString throws an exception on Symbol. The addition operator calls some other functions like argument[Symbol.toPrimitive] when attempting to convert the value to a String.
Typeargument ) (spec)

Returns the type of argument.

Index

Terms defined by this specification

Terms defined by reference

References

Informative References

[CONSOLE]
Dominic Farolino; Terin Stock; Robert Kowalski. Console Standard. Living Standard. URL: https://console.spec.whatwg.org/
[DOM]
Anne van Kesteren. DOM Standard. Living Standard. URL: https://dom.spec.whatwg.org/
[ECMA-262]
ECMAScript Language Specification. URL: https://tc39.es/ecma262/
[ECMA-262-2019]
ECMAScript 2019 Language Specification. URL: https://ecma-international.org/ecma-262/10.0/
[HTML]
Anne van Kesteren; et al. HTML Standard. Living Standard. URL: https://html.spec.whatwg.org/multipage/
[ISO-22275-2018]
ISO/IEC 22275:2018 - Information technology — Programming languages, their environments, and system software interfaces — ECMAScript® Specification Suite. URL: https://www.iso.org/standard/73002.html
[JOHNNY-FIVE]
Johnny-Five: The JavaScript Robotics & IoT Platform. URL: http://johnny-five.io/
[MDN]
Mozilla Developer Network. URL: https://developer.mozilla.org/en-US/
[NODEJS]
Node.js. URL: https://nodejs.org/
[TC39]
TC39 - ECMAScript. URL: https://www.ecma-international.org/memento/tc39.htm
[WAT]
Gary Bernhardt. Wat. URL: https://www.destroyallsoftware.com/talks/wat
[WHATISMYBROWSER]
What browser am I using?. URL: https://www.whatsmybrowser.org/
[XKCD-703]
Randall Munroe. xkcd: Honor Societies. URL: https://www.xkcd.com/703/
[YDKJS]
Kyle Simpson. You Don't Know JS (book series). URL: https://github.com/getify/You-Dont-Know-JS

Issues Index

To be written. <https://github.com/TimothyGu/es-howto/issues/2>