How to Read the ECMAScript Specification

The Ecmascript Language Specification (Ecmascript) The Javascript Specification, or ECMA-262, is a great resource for learning the basics of how Javascript works. However, this is a huge professional text, and at first glance you may feel confused, fearful, passionate and unsure of what to do.

preface

Whether you plan to read a bit of the ECMAScript specification every day or make it an annual or quarterly goal, this article is designed to make it easier to get started with the most authoritative JavaScript language references.

Why read the ECMAScript specification

The Ecmascript specification is the authoritative source for all JavaScript runtime behavior, whether in your browser environment, server environment (Node.js), on a spacesuit [nodejs-NASA], or on your iot device [Johnny-Five]. All JavaScript engine developers rely on this specification to ensure that their hype about new features works as expected with other JavaScript engines.

The Ecmascript specification is not only useful for JavaScript engine developers, it is also useful for ordinary JavaScript coders that you simply don’t realize or use.

Suppose one day you find yourself at work with this strange question:

> 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 {}
Copy the code

And very confused as to why one method works on its prototype, but another method doesn’t work on its prototype. Unfortunately, you can’t Google this question, and Stack Overflow probably won’t solve it either.

At this point you should read the Ecmascript language specification.

Or, you might be wondering how the infamous loose Equality Operator (==) works (the word ‘function’ [WAT] is used loosely). What’s more, you can still find explanation on MDN. What’s more, you can still find explanation on MDN.

At this point you should read the Ecmascript language specification.

On the other hand, I do not recommend that developers who are new to JavaScript read the ECMAScript specification. If you’re new to JavaScript, have fun playing with the Web! Develop some Web applications, some javascript-based cameras, etc. You may want to return to this document when you have enough JavaScript holes, or when JavaScript issues are no longer limiting your ability to develop.

Well, you now know that JavaScript specifications can be useful in helping you understand the complexities of a language or platform. But what exactly does the ECMAScript specification contain?

What does and does not belong to the ECMAScript specification

The textbook answer to this question is “only language features are included in the ECMAScript specification” but that doesn’t help because it’s like saying “JavaScript features are JavaScript” and I don’t like taunts [xkCD-703].

Instead, what I’m going to do is list some things that are common in JavaScript applications and tell you if they are a language feature.

features Whether to belong to
Syntax of syntactic elements (i.e., what a valid for.. in loop looks like) Y
Semantics of syntactic elements (i.e., what typeof null, or { a: b } returns) Y
import a from ‘a’; ? [1]
Object, Array, Function, Number, Math, RegExp, Proxy, Map, Promise, ArrayBuffer, Uint8Array, … Y
console, setTimeout(), setInterval(), clearTimeout(), clearInterval() N [2]
Buffer, process, global* N [3]
module, exports, require(), __dirname, __filename N [4]
window, alert(), confirm(), the DOM (document, HTMLElement, addEventListener(), Worker, …) N [5]
  1. The specification specifies the syntax of these declarations and what they mean, but it does not specify how modules are loaded.
  2. These things are available in browsers and Node.js, but are not standard. For Node.js, they have counterpartsNodejs document. For browsers,consolebyThe Console standardWhile the rest is defined byThe HTML standardSpecified.
  3. These are only global variables supported by Node.js and are defined by the Nodejs documentation. * Global actually has the opportunity to become part of ECMAScript and implement ECMA-262-Global in browsers.
  4. These are “globals” that contain only node.js modules and are documented/specified by their documentation.
  5. These are browser-specific things.

Where can I view the ECMAScript specification?

When you Google “ECMAScript Specification “, you’ll see a lot of results that claim to be legitimate specifications. Which one should you read?

More likely, the specification published on tc39.github. IO/ecMA262 / is exactly what you want for ECMA-262.

To make a long story short:

The Ecmascript language specification was developed by a group of people from different backgrounds known as THE ECMA International Technical Committee 39(or as they are better known, TC39[TC39]). TC39 maintains the latest specification of the ECMAScript language in tc39.github. IO [ECMA-262].

To complicate matters, each year TC39 takes a snapshot of the specification at a point in time to become the ECMAScript language standard of the year, with a version number attached. For example, the ECMAScript 2018 Language specification (ECMA-262, 9th edition)[ECMA-262-2018](commonly referred to as ES9) is simply the specification seen in tc39.github. IO [ECMA-262] in June 2018, placed in an archive, And properly wrapped and PDFified for permanent archive.

Because of this, unless you want your Web application to only run on a browser with formaldehyde, properly wrapped and PDFified for permanent archiving starting June 2018, you should always check tc39.github. IO [ECMA-262] for the latest specification. However, if you want (or must) support older browser or Node.js versions, it may be helpful to refer to older versions of the specification.

Note: ISO/IEC also publishes the ECMAScript language standard as ISO/IEC 16262[ISO-16262-2011]. But don’t worry, because the standard text is exactly the same as the standard text published by ECMA International, the only difference is that you have to pay 198 Swiss francs.

In Navigating the spec

The Ecmascript specification talks a lot. Despite the best efforts of its authors to break it down into small logical chunks, it is still a supersized text.

Personally, I like to divide specs into five sections:

  • What is a number? When the said specificationthrow a TypeError exceptionWhat does that mean?
  • Grammar Productions of the Languagefor-inCycle?)
  • Static semantics of the languagevarHow are variable names determined in statements?
  • Runtime semantics of the languagefor-inHow is the loop executed?
  • APIs (String.prototype.substring()What to do?

But that’s not how the specification is organized. Instead, it puts the first point in §5 Notational Conventions — through §9 Ordinary and Exotic Objects — The next three are placed in a crossover form in §10 ECMAScript Language: Source Code via §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 The Statement

    • § 13.7.2.1-5 Static semantics

    • § 13.7.2.6 Runtime semantics

§ 13.7.3 The while Statement

.

APIs extend Global objects through §18 The Global Object through §26 Reflection.

At this point, I would like to point out that absolutely no one reads the specification from top to bottom. Instead, just look at the section that corresponds to what you’re looking for, and only look at what you need in that section. Try to determine which of the five sections your specific question covers; If you’re not sure which part is relevant, ask yourself this question: “At what point (whatever you want to confirm) was the problem evaluated?” That might help. Don’t worry, the spec will only get easier in practice.

Runtime semantics Runtime semantics

The Runtime Semantics of The Language and The Runtime semantics of APIs are The most important part of The specification and are usually of The greatest concern.

In general, reading these chapters in the specification is very simple. However, the specification uses a lot of talent that is just beginning (to me at least) to be quite annoying. I’ll try to explain some of these conventions and then apply them to a general workflow to figure out how a few things work.

Algorithm Steps Algorithm steps

Most runtime semantics in Ecmascript are specified by a set 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.
  1. Let b be a+a.
  1. If b is 2, then
  1. Hooray! Arithmetics isn ‘t broken.
  1. Else
  1. Boo!

§5.2 Algorithm Conventions

Abstract operations Abstract operations

You’ll sometimes see something like a function called in a spec. The first step of the Boolean() function is:

When a Boolean is called with a parameter value, the following steps are performed:

  1. Let b be ToBoolean(value).

  2. .

This ToBoolean function is called abstract Operation: It is abstract because it is not actually exposed to JavaScript code as a function. It was just invented by a notation spec writers to get them not to write the same thing over and over again.

Further reading: §5.2.1 Abstract Operations

What is [[This]]

Sometimes, you might see [[Notation]] used as “Let proto be obj.[[Prototype]]”. This symbol can technically mean several different things, depending on the context in which it appears, but you can understand that this symbol refers to some internal property that cannot be observed through JavaScript code.

To be precise, it can mean three different things, which I’ll illustrate with examples from the specification. You can skip them for now, though.

A field of a Record

The Ecmascript specification uses the term Record to refer to a key-value map with a fixed set of keys — a bit like a structure in C. Each key-value pair of a Record is called a field. Because Records can only appear in the specification, not in the actual JavaScript code, it makes sense to use the [[Notation]] to refer to the Record’s field.

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.
  1. If both Desc.[[Value]] and Desc.[[Writable]] are absent, return false.
  1. Return true.

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

§6.2.1 The List and Record Specification Types

An internal slot of a JavaScript Object

Javascript objects may have so-called internal slots, which the specification uses to hold data. Like the Record field, these internal slots cannot be observed with JavaScript, but some of them may be exposed through implementation-specific tools such as DevTools for Google Chrome. Therefore, it makes sense to use the [[Notation]] Notation to describe internal slots.

The details of internal slots are covered in §2.5 JavaScript Objects. Now, don’t worry too much about their uses, but 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: The Internal slots for Object and Record Fields are identical in appearance, but you can disambiguate them by looking at the precedents for this symbol (the part before the dot), whether it is Object or Record. This is usually quite obvious from the surrounding environment.

An internal method of a JavaScript Object

Javascript objects may also have so-called internal methods. Like internal slots, these internal methods cannot be observed directly through JavaScript. Therefore, it makes sense to use the [[Notation]] Notation to describe internal methods.

Details of internal methods are covered in §2.5 JavaScript Objects. Now, don’t worry too much about their uses, but 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: The third meaning of [[[Notation]] can be distinguished by looking like a function call.

Completion Records; ? and !

Each runtime semantics in the Ecmascript specification explicitly or implicitly returns a Completion Record reporting its results. The Completion Record is a Record with three possible fields:

  • A [[Type]] (normal, return, throw, break, or continue)

  • If [[Type]] is normal, return, or throw, then it can also have [[Value]] (” What’s returned/thrown”)

  • If [[Type]] is break or continue, it may choose to have a tag called [[Target]], and the script executes breaks from/continues for runtime semantics reasons

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.

In most cases, you just need to deal with the abrupt Completions whose [[Type]] is the throw. The other three types of Abrupt Completion are useful only to see how specific syntax elements are evaluated. In fact, you’ll never see any other types in the definition of built-in functions, because break/continue/return does not work across function boundaries.

§6.2.3 The Completion Record Specification Type

Because of the definition of Completion Records, details in JavaScript, such as bubbling errors up to try-catch blocks, do not exist in the specification. In fact, errors (or, more accurately, abrupt completions) are handled explicitly.

Without any abbreviations, the canonical text of a normal call to an abstract operation might return a calculated result or throw an error that looks like this:

Here are some steps to invoke an abstract operation that can throw an operation without any shorthands:

  1. Let resultCompletionRecord be AbstractOp().

Note: resultCompletionRecord is a Completion Record.

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

Note: Here, if it is an Abrupt Completion, the resultCompletionRecord is returned directly. In other words, the error thrown in AbstractOp is forwarded and the remaining steps are terminated.

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

Note: After ensuring that we have a Normal completion, we can now deconstruct the Completion Record to get the actual result of the calculation we need.

  1. Result is the result we need. We can do a lot more with it now.

This may 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
Copy the code

But to reduce these tedious steps, the editor of the ECMAScript specification has added some abbreviations. Since ES2016, the same specification text can be written in two equivalent ways:

The following steps invoke an abstract operation that might throw ReturnIfAbrupt:

  1. Let result be AbstractOp().

Note: Here, as in Step 1 in the previous example, the result is a Completion Record.

  1. ReturnIfAbrupt(result).

Note: ReturnIfAbrupt processes any abrupt completions that may occur by forwarding, and automatically deconstructs result to its [[Value]]

  1. Result is the result we need. We can do a lot more with it now.

Or, more precisely, with a special question mark (?). Symbol:

Calls may have question marks (?) Several steps of the abstract operation:

  1. Let result be ? AbstractOp().

Note: In this notation, we do not deal with Completion Records at all. ? Shorthand takes care of everything for us, and Result is immediately available

  1. Result is the result we need. We can do a lot more with it now.

Sometimes, if we know that a particular call to AbstractOp will never return an abrupt completion, it can tell the reader more about a spec’s intent. In these cases, a punctuation mark (!) is a punctuation mark. Used for:

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

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.

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

Expanded reading: §5.2.3.4 Returnifabnormal short thands

JavaScript Objects

In ECMAScript, each Object has a set of internal methods that are called by the rest of the specification to accomplish certain tasks. Some internal methods that all objects have are:

  • [[Get]], which gets a property on an Object (e.g. obj.prop)

  • [[Set]], which sets a property on an Object (e.g. obj.prop = 42;)

  • [[GetPrototypeOf]], which gets the Object’s prototype (i.e., Object.getPrototypeof (obj))

  • [[GetOwnProperty]], which gets the Property Descriptor of an own property of an Object (i.e., Object.getOwnPropertyDescriptor(obj, “prop”))

  • [[Delete]], which deletes a property on an Object (e.g. delete obj.prop)

A full list can be found in §6.1.7.2 Object Internal Methods and Internal Slots.

Based on this definition, function objects (or just “functions”) are simple objects that attach [[Call]] inner methods and possibly [[Construct]] inner methods; For this reason, they are also called Callable objects.

The specification then classifies all objects into two categories: ordinary objects and Exotic objects. Most of the objects you encounter are ordinary objects, which means that all their Internal Methods are listed in §9.1 Ordinary Object Internal Methods and Internal Slots.

However, the ECMAScript specification also defines a number of Exotic objects that can override the default implementation of these internal methods. There are minimum limits to what foreign objects are allowed to do, but in general, a plethora of internal methods can do a lot of special effects without violating the specification.

Array objects are one of these Exotic objects. Tools available with Ordinary Objects do not capture special semantics like the length attribute of an Array object.

One is that setting the Length property of an Array object removes the property from the object, but the Length property appears to be just a generic data property. In contrast, new Map().size is just a getter function specified on map.prototype and does not have attributes like [].length.

> 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 }
Copy the code
> 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 }
Copy the code

This behavior is implemented by overriding the [[DefineOwnProperty]] inner method. See §9.4.2 Array Exotic Objects


Javascript objects may also have internal slots defined to contain values of a particular type. I tend to see an internal slots as even for the Object. The getOwnPropertySymbols () are hidden attribute named symbols. Internal slots are allowed for both ordinary objects and Exotic objects.

In An internal slot of a JavaScript Object, I refer to An internal slot named [[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]], which I briefly described above. Is there any difference between them?

Most: While most objects have [[Prototype]] internal slot, all objects implement [[GetPrototypeOf]] internal methods. It is worth noting that Proxy objects do not have their own [[Prototype]], and its [[GetPrototypeOf]] internal methods follow the registered handler or its target Prototype, Stored in the internal slot of the Proxy object [[ProxyTarget]].

Therefore, when working with objects, it is almost always a good idea to refer to the appropriate internal method rather than looking directly at the value of the internal slot.


Another way to think about the relationship between Objects, internal methods and internal slots is through the classic Object-oriented lens. “Object” is like an interface that specifies several internal Methods that must be implemented. Ordinary Objects provides a default implementation, and Exotic Objects can be partially or fully covered. On the other hand, internal slots resembles the implementation details of Object’s instance variable.

All of these relationships can be summarized in the following UML diagram:

Example: the String. The prototype. The substring ()

Now that we have a good understanding of how specifications are organized and written, let’s get started!

Suppose I now have the following questions:

What does the following snippet return if you don’t run the code?

String.prototype.substring.call(undefined.2.4)
Copy the code

This is quite a tricky problem. There seem to be two plausible outcomes:

  1. String. The prototype. The substring () firstundefinedCast to"undefined"String, and then get characters at the second and third positions of the string (that is, the interval [2,4])"de".
  2. On the other hand,String.prototype.substring()It is also reasonable to throw an error to reject undefined input.

Unfortunately, WHEN the value of this is not a string, MDN doesn’t really provide any indication of how the function works either.

Before reading the Algorithm Steps, let’s think about what we know. I assume we have a basic understanding of how str.substring() normally works: that is, returning part of a given string. What we’re really not sure about now is how it works when this is undefined. Therefore, we will specifically look for algorithm steps that resolve this value.

Fortunately, the String. The prototype. The substring () the first step in the algorithm designed to handle this value:

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

? Summary Allows us to conclude that in some cases the RequireObjectCoercible abstraction operation may actually throw an exception because otherwise! Will be used instead. In fact, if it throws an error, it will correspond to our second assumption above! Hopefully, we can see what RequireObjectCoercible does by clicking the hyperlink.

The Requireobjectforecble abstraction is a bit strange. Unlike most abstract operations, it is defined by tables rather than steps:

Argument Type Result
The Undefined Throw a TypeError exception
. .

Anyway — in the line corresponding to Undefined (the type of this value we pass to substring()), the specification says RequireObjectCoercible should throw an exception. That’s because it’s used in the definition of the function, right? We know that the thrown exception must bubble up to the caller of the function.

That’s our answer: a given snippet of code throws a TypeError exception.

The specification only specifies the type thrown by the error, not the message it contains. This means that implementations can have different error messages, even localized error messages.

For example, on Google’s V86.4 (included in Google Chrome 64), the message reads:

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

Mozilla Firefox 57.0 offers some less useful features

TypeError: can’t convert undefined to object

Meanwhile, ChakraCore Version 1.7.5.0(a JavaScript engine in Microsoft Edge) took the V8 route and threw it

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

Example: Do Boolean() and String() throw exceptions?

Exception handling must be a priority when writing mission-critical code. So, “Does a built-in function throw an exception?” It needs careful consideration.

In this example, we will attempt to answer questions about two of the language’s built-in functions Boolean() and String(). We’ll focus only on direct calls to these functions, not new Boolean() and new String() — this is one of the most unpopular features in JavaScript and the most discouraged practice in almost all JS programming guides: YDKJS.

After finding the Boolean() section of the specification, we see that the algorithm seems rather short:

When a Boolean is called with a parameter value, the following steps are performed:

  1. Let b be ToBoolean(value).

  2. If NewTarget is undefined, return b.

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

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

  5. Return O.

But on the other hand, it is not entirely simple OrdinaryCreateFromConstructor here involves complex basic skills. More importantly, there is one? The shorthand in Step 3 May indicate that this function may throw an error under certain circumstances. Let’s take a closer look.

Procedure Step 1 Convert value (function parameter) to a Boolean value. Interestingly, none of them? Or!!! This step of shorthand, however, is usually not equivalent to Completion Record Shorthand! Therefore, do not throw an exception in Step 1.

Step 2 checks if the object named NewTarget is undefined. Newtarget is the spec equivalent of the new.target meta-attribute first added in ES2015, which allows the specification to distinguish new Boolean() calls. Because we are now focusing only on direct calls to Boolean(), we know that NewTarget is always undefined and that the algorithm always returns b directly without any additional processing.

Because calling Boolean() without new can only access the first two steps of the Boolean() algorithm, neither of which can raise an exception, we conclude that Boolean() does not raise an exception regardless of the input.

Let’s turn our attention to String () :

When a String is called with a parameter value, the following steps are performed:

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

  2. Else,

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

  4. Let s be ? ToString(value).

  5. If NewTarget is undefined, return s.

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

Based on our experience with the Boolean() function for class analysis, we know that NewTarget is always undefined for our case, so we can skip the last step. We also know that Type and SymbolDescriptiveString are also safe because neither process the abrupt Completions. However, there is also a question about? Is the problem? Before calling the ToString abstract operation. Let’s take a closer look.

Much like RequireObjectCoercible we saw earlier, ToString(argument) is 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)

When ToString is called in String(), value can be anything other than Symbol (filtered out in the next step). However, there are two more? Object row. We can click on the links for ToPrimitive and beyond and see that there are actually many opportunities to throw an error if value is Object:

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

More examples of String() are as follows:

// Spec stack trace:
// OrdinaryGet step 8.
// Ordinary Object's [[Get]]()
// GetV step 3.
// GetMethod step 2.
// ToPrimitive step 2.d.

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

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

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

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

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

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

String(Object.create(null));
Copy the code

Example: Typeof operator

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

To be continued github.com/TimothyGu/e…

reference

  • How to Read the ECMAScript Specification
  • ECMAScript ® 2020 Language Specification
  • JavaScript digs into this from the ECMAScript specification
  • Read ECMAScript specifications