cf.completefrontendCode editorOpen lab
THE JAVASCRIPT FIELD GUIDE

Spec types: Completion, Property Descriptor & more

Read ECMAScript's bookkeeping types through small JavaScript examples: follow completions, inspect property flags, and recognize lists, closures, and bytes.

By the end, you can
  • 01
    Trace control flowRead normal and abrupt Completion Records to predict returns, throws, breaks, continues, and finally blocks.
  • 02
    Inspect property rulesTell data and accessor descriptors apart, and test writable, enumerable, and configurable attributes.
  • 03
    Read spec notationRecognize Lists, Records, Abstract Closures, and Data Blocks without mistaking them for JavaScript classes.

The spec has its own vocabulary

You can put JavaScript values in variables and log them. The ECMAScript specification also names intermediate values used to describe how an operation works. It calls these specification types. They are a way to explain behavior, not a new set of constructors for your application.

An ordinary JavaScript valuePop out in the code editor (opens in a new tab)JavaScript
const cart = { total: 3 };
console.log(cart.total);

Line 1 creates a cart with a total of 3. Line 2 reads that property and prints 3. A specification type is different: there is no JavaScript statement that hands you the actual Completion Record created while evaluating line 2.

Definition

A specification type is a meta-value used in ECMAScript algorithms. It can describe an intermediate result, but it cannot itself be stored as an ordinary JavaScript variable or object property.

In How to read ECMAScript, you learned to follow algorithm steps and double-square bracket names. Now we give those steps concrete names. This lesson covers Completion Records, Property Descriptors, Lists, Records, Abstract Closures, and Data Blocks. An Environment Record models name lookup in nested scopes; the later Environment Records lesson takes that topic further.

Completion Records: what happened next?

A Completion Record is the spec's account of a step's outcome. Its [[Type]] says whether control continues normally, returns, throws, breaks, or continues a loop. Its [[Value]] carries the resulting value. Its [[Target]] is a label for a directed break or continue, or the spec marker EMPTY when no label is needed.

One normal resultPop out in the code editor (opens in a new tab)JavaScript
const price = 3;
console.log(price);

Line 1 stores the JavaScript value 3. Line 2 prints 3. The spec can describe that expression as completing normally with a value. The Completion Record is not another object printed to the console.

Quoted step from NormalCompletion (spec notation, not JavaScript)Text
1. Return Completion Record { [[Type]]: NORMAL, [[Value]]: value, [[Target]]: EMPTY }.

The quoted NormalCompletion step names all three fields. Here [[Type]] is NORMAL, [[Value]] is the produced value, and [[Target]] is EMPTY. EMPTY is a spec marker, not JavaScript undefined.

Replay return and finally
Step 0 of 5Ready
Your turn: follow the blue line

Replay instrumented JavaScript control flow. These frames illustrate Completion Records; they do not expose engine records.

Running in
  1. script
Next: line 9
Click the blue line to take the next stepPop out in the code editor (opens in a new tab)JavaScript
function chooseTea(available) {  try {    if (!available) throw new Error("sold out");    return "tea";  } finally {    console.log("checked");  }}
CallStoreChangeResultRun = next line. Ran = already executed.
Recent returnsNothing yet. Start with the blue line.
A guided replay recorded from real JavaScript calls, not an engine debugger. Step follows executed statements; Back reviews a snapshot. Reset starts a fresh run.

The replay calls a real JavaScript function. Line 3 checks whether tea is available. Line 4 prepares a return of tea. Line 6 logs checked before the return reaches line 9, which prints tea. The output is therefore checked followed by tea. The colored frames replay instrumented example code; they do not inspect the engine.

Five possible Completion Record types
TypeControl flowExample
NORMALContinue with a valueAn expression finishes without transferring control
RETURNLeave the current functionA return statement carries its value to the caller
THROWLook for a handlerA throw statement carries the thrown value
BREAK / CONTINUELeave a loop / begin its next iterationA label can be carried in [[Target]]
Real-life analogyThe order token

You order food and receive a token. The token can tell you that your order is ready or that the item is sold out. The message and the order name answer different questions.

In real life: Token says an order is ready
In JavaScript: NORMAL means continue
In real life: Token says an item sold out
In JavaScript: THROW means leave for error handling
In real life: Token includes the order name
In JavaScript: [[Value]] carries the result

Where the analogy stops: A completion also models returns and labelled jumps; a real order token does not control a program.

Abrupt completions redirect control

An abrupt completion is any Completion Record whose type is not NORMAL. A throw moves toward a catch handler; a return leaves a function; break and continue direct loop evaluation. Abrupt does not mean something went wrong: a normal return statement is abrupt in this precise spec sense.

Throw moves to catchPop out in the code editor (opens in a new tab)JavaScript
function priceOrError(price) {  if (price < 0) throw new Error("negative price");  return price;}try { console.log(priceOrError(-1)); }catch (error) { console.log(error.message); }

Line 1 defines a price check. Line 2 throws an Error when the price is negative, so line 3's return is skipped. Line 5 calls the function with -1; line 6 catches the thrown value and prints negative price.

The spec's Completion Record for that throw has type THROW and carries the Error as its value. The JavaScript Error is observable; the Completion Record wrapper is not. That distinction helps when an algorithm says "ReturnIfAbrupt" or uses the ? shorthand introduced in the previous lesson.

Continue and break in a small loopPop out in the code editor (opens in a new tab)JavaScript
let served = 0;for (const order of ["tea", "skip", "done"]) {  if (order === "skip") continue;  if (order === "done") break;  served++;}console.log(served);

Line 1 starts the count at zero. Line 2 visits tea, skip, then done. Line 3 skips the second item; line 4 exits on the third. Line 5 counts only tea, so line 7 prints 1. When a jump has a label, [[Target]] carries that label; an ordinary unlabelled jump has no label to name.

Why finally can replace a return

A finally block runs even while a return or throw is pending. If finally finishes normally, that pending completion survives. If finally itself returns or throws, the new abrupt completion takes precedence. This is why a return from finally can hide an earlier result or even an error.

A return inside finallyPop out in the code editor (opens in a new tab)JavaScript
function getStatus() {  try { return "ready"; }  finally { return "closed"; }}console.log(getStatus());

Line 1 defines getStatus. Line 2 prepares a return of ready. Line 3 returns closed from finally instead, so line 5 prints closed. The first return is not printed.

Compare the earlier tea replay: its finally block only logged checked, so tea still reached the caller. In real code, use finally for cleanup such as releasing a resource. Avoid returning from finally unless replacing a pending outcome is truly intended.

The same rule matters when try throws. A normal finally allows the throw to continue toward a handler; a throwing finally replaces it with the new thrown value. You can predict both cases by asking which completion finishes the finally block.

Property Descriptors describe own properties

A Property Descriptor is a spec Record describing an object's own property attributes. JavaScript lets you define those attributes with Object.defineProperty and inspect a regular object describing them with Object.getOwnPropertyDescriptor. The object returned by this API is not itself the internal spec Record.

Define and inspect statusPop out in the code editor (opens in a new tab)JavaScript
const order = {};Object.defineProperty(order, "status", { value: "ready" });console.log(Object.getOwnPropertyDescriptor(order, "status").writable);console.log(Object.keys(order).length);

Line 1 creates an empty order. Line 2 defines a status value but leaves flags out. Line 3 prints false for writable; line 4 prints 0 because enumerable also defaults to false on a new defined property. An ordinary property assigned with order.status = "ready" has different default flags.

Quoted FromPropertyDescriptor steps (spec notation)Text
1. If propertyDesc has a [[Value]] field, then
   a. Perform CreateDataPropertyOrThrow(obj, "value", propertyDesc.[[Value]]).

These short steps from FromPropertyDescriptor show the bridge: a spec field called [[Value]] becomes the ordinary object property value. The reverse direction, ToPropertyDescriptor, reads fields from a supplied JavaScript object.

Replay a property definition
Step 0 of 5Ready
Your turn: follow the blue line

Replay instrumented property creation and inspection. This is not an engine-internal trace.

Running in
  1. script
Next: line 1
Click the blue line to take the next stepPop out in the code editor (opens in a new tab)JavaScript
Object.defineProperty(order, "status", { value: "ready", enumerable: false });const description = Object.getOwnPropertyDescriptor(order, "status");console.log(description.value, description.writable);console.log(Object.keys(order).length);
CallStoreChangeResultRun = next line. Ran = already executed.
Recent returnsNothing yet. Start with the blue line.
A guided replay recorded from real JavaScript calls, not an engine debugger. Step follows executed statements; Back reviews a snapshot. Reset starts a fresh run.

The replay uses the lesson's real describeOrder function. The source defines status as ready and non-enumerable. It prints ready false on line 4, then 0 on line 5. The returned descriptor also reports configurable false. Back restores earlier snapshots without changing the created object in your app.

Playground: show or hide the status key
The current property definitionPop out in the code editor (opens in a new tab)JavaScript
const order = {};Object.defineProperty(order, "status", { value: "ready", enumerable: true });console.log(Object.keys(order));
Current result
Object.keys(order)status
order.statusready
enumerabletrue
Try it yourself

The property still has value ready. Its enumerable flag is true, so Object.keys returns status.

The checkbox changes only enumerable in the shown definition; Reset restores true. The output uses the real descriptor APIs.

Change only the enumerable checkbox. With it on, the output lists status. With it off, the list is empty but order.status stays ready. Reset restores true. This answers a common debugging question: a property can exist without appearing in Object.keys.

Data properties and accessor properties

A data descriptor has [[Value]] or [[Writable]]. An accessor descriptor has [[Getter]] or [[Setter]]. Both can carry enumerable and configurable fields. The spec forbids mixing data and accessor fields in a single descriptor.

A getter reads the current pricePop out in the code editor (opens in a new tab)JavaScript
const order = { price: 3 };Object.defineProperty(order, "total", { get() { return this.price; } });console.log(order.total);order.price = 4;console.log(order.total);

Line 1 creates an order priced at 3. Line 2 installs a getter named total. Line 3 reads it and prints 3. Line 4 changes price to 4; line 5 calls the getter again and prints 4. The getter runs each time, rather than storing a separate total value.

Descriptor fields answer different questions
FieldKindEffect
value and writableData descriptorHolds a value and controls ordinary assignment
get and setAccessor descriptorRuns functions when code reads or writes
enumerableEither kindControls Object.keys and related enumeration
configurableEither kindControls deletion and reconfiguration

Writable controls assignment to a data property. The following example uses Reflect.set so a failed write produces a clear Boolean, regardless of whether an editor runs the snippet in strict mode.

A non-writable data propertyPop out in the code editor (opens in a new tab)JavaScript
const cart = {};
Object.defineProperty(cart, "total", { value: 3 });
console.log(Object.getOwnPropertyDescriptor(cart, "total").writable);
console.log(Reflect.set(cart, "total", 4), cart.total);

Line 1 creates a cart. Line 2 defines total as 3. Line 3 prints false because writable was omitted. Line 4 prints false 3: the write fails and the stored value stays three.

A configurable property can be deletedPop out in the code editor (opens in a new tab)JavaScript
const todo = {};
Object.defineProperty(todo, "done", { value: false, configurable: true });
console.log(delete todo.done);
console.log("done" in todo);

Line 1 makes an empty todo. Line 2 defines done with configurable true. Line 3 prints true because deletion succeeds. Line 4 prints false because the property is gone. Without configurable true, deleting this newly defined property would fail.

A non-enumerable password still existsPop out in the code editor (opens in a new tab)JavaScript
const user = { name: "Asha" };
Object.defineProperty(user, "password", { value: "secret" });
console.log(Object.keys(user).join(","));
console.log(Object.getOwnPropertyDescriptor(user, "password").enumerable);

Line 1 sets the user's name. Line 2 adds a password property but does not make it enumerable. Line 3 prints name, and line 4 prints false. Hiding a key from enumeration is not security: JavaScript can still read the value directly or inspect its descriptor.

Lists keep order; Records name fields

A List is an ordered sequence used inside specification algorithms, such as the argument values in a function call. Its notation uses angle quotes, for example « "tea", 3 ». This does not promise that the runtime created a JavaScript Array you can inspect.

Two ordered argumentsPop out in the code editor (opens in a new tab)JavaScript
function receipt(item, price) {  console.log(item, price);}receipt("tea", 3);

Line 1 declares receipt with item then price. Line 2 prints the two received values. Line 4 passes tea then 3, so the output is tea 3. The spec describes this ordered set of argument values as a List.

Short excerpt of List algorithm notationText
"« 1, 2 » defines a List value that has two elements"
"A new empty List can be expressed as « »."

These short quotations are from the List language in the List and Record definitions. The same definition says argument lists use this ordered type. The function call above lets you see why preserving order matters.

A Record has named fields written with double square brackets in the spec. A Property Descriptor is one named Record shape. You can make a JavaScript object that looks similar, but that object is only a teaching comparison, not a spec Record value.

A JavaScript object for comparisonPop out in the code editor (opens in a new tab)JavaScript
const result = { status: "ready", count: 3 };
console.log(result.status, result.count);

Line 1 makes an ordinary object with named status and count properties. Line 2 prints ready 3. A spec Record similarly names fields, but its fields can contain other spec values and cannot be assigned to a JavaScript variable.

An ordinary object can be changedPop out in the code editor (opens in a new tab)JavaScript
const label = { status: "ready" };
label.status = "sent";
console.log(label.status);

Line 1 creates a real label. Line 2 updates its status. Line 3 prints sent. Do not interpret that update as changing the invisible spec Record that may have described an earlier operation.

Four more specification types
TypeSpec roleDo not confuse with
ListOrdered spec sequenceArguments passed to a function, not necessarily a JS Array
RecordNamed spec fieldsA Property Descriptor is one kind of Record, not a JS plain object
Abstract ClosureAlgorithm plus captured valuesA spec helper, not a JavaScript function exposed to code
Data BlockFixed-size mutable bytesExplains ArrayBuffer storage, not the buffer object itself

Abstract Closures capture values for an algorithm

An Abstract Closure is a spec helper: algorithm steps plus a collection of captured values. An algorithm creates it inline and later calls it like a function in spec notation. When the spec says it captures an alias, it takes the value associated with that alias at creation time.

A JavaScript closure for comparisonPop out in the code editor (opens in a new tab)JavaScript
function makeTax(adjustment) {  return (price) => price + adjustment;}const addTax = makeTax(2);console.log(addTax(3));

Line 1 defines a function that accepts an adjustment. Line 2 returns a function that adds it to a price. Line 4 calls the factory with 2; line 5 calls the result with 3 and prints 5. This JavaScript closure is an analogy for retaining information, not an Abstract Closure you can directly obtain.

Short excerpt of the spec's Abstract Closure exampleText
1. Let addend be 41.
2. Let closure be a new Abstract Closure with parameters (x) that captures addend.
3. Let value be closure(1).

The Abstract Closure definition uses an addend and then calls its helper. The displayed step 2 omits its nested arithmetic step. Captured aliases refer to their associated values when the helper is created; do not assume this rule applies to all JavaScript closure bindings.

Save a value before a later changePop out in the code editor (opens in a new tab)JavaScript
let price = 3;
const savedPrice = price;
const showSaved = () => savedPrice;
price = 4;
console.log(showSaved(), price);

Line 1 gives price the value 3. Line 2 copies that number into savedPrice. Line 3 builds a JavaScript function reading savedPrice. Line 4 changes price to 4. Line 5 prints 3 4. This is an explicit teaching comparison for capture-at-creation, not an implementation of the spec's Abstract Closure.

Data Blocks explain byte storage

A Data Block is a distinct, mutable sequence of fixed-length bytes in spec algorithms. Each byte is a number from 0 to 255. Creation initializes the bytes to zero. An ArrayBuffer is a JavaScript object associated with such storage; a typed array is a view that lets code read and write bytes.

Read and write three bytesPop out in the code editor (opens in a new tab)JavaScript
const buffer = new ArrayBuffer(3);const bytes = new Uint8Array(buffer);console.log(bytes.join(","));bytes[1] = 255;console.log(bytes.join(","));

Line 1 creates a buffer for three bytes; line 2 creates a byte view. Line 3 prints 0,0,0. Line 4 writes 255 to the second position, so line 5 prints 0,255,0. A view is what your code sees, not the specification's Data Block meta-value itself.

Quoted CreateByteDataBlock steps (spec notation)Text
1. Let dataBlock be a new Data Block value consisting of size bytes.
2. Set all of the bytes of dataBlock to 0.
3. Return dataBlock.

The short quotation is from CreateByteDataBlock. The zeroing step explains the first output. It does not claim the JavaScript constructor invokes that named helper in any particular engine implementation; the language promises the observable initial bytes.

Read a multi-byte value byte by bytePop out in the code editor (opens in a new tab)JavaScript
const buffer = new ArrayBuffer(2);
const view = new DataView(buffer);
view.setUint16(0, 258);
console.log(view.getUint8(0), view.getUint8(1));

Line 1 makes a two-byte buffer. Line 2 creates a DataView. Line 3 writes the number 258 as an unsigned 16-bit value using the default big-endian order. Line 4 prints 1 2 when reading the two bytes separately. Data Blocks explain the storage; the view specifies how a larger number becomes bytes.

The spec also defines Shared Data Blocks for storage accessible to more than one agent. Sharing brings additional memory-model rules; a plain ArrayBuffer example does not demonstrate cross-agent behavior. Keep that distinction whenever an algorithm mentions shared bytes.

Using spec vocabulary at work

Start with an observable symptom. A theme setting is readable in your application but missing from a list of keys. Before changing the interface, inspect its property descriptor: was enumerable set to false? Knowing the spec field tells you which JavaScript API can answer the question.

List a site's enumerable settingPop out in the code editor (opens in a new tab)JavaScript
const settings = {};
Object.defineProperty(settings, "theme", { value: "light", enumerable: true });
console.log(Object.keys(settings).join(","));

Line 1 starts an empty settings object. Line 2 defines a theme property with enumerable true. Line 3 prints theme. With enumerable false the key would vanish from Object.keys, although settings.theme would still have the value light. That is a practical reason to read a Property Descriptor.

For a handler that exits early, Completion Records help you follow a pending return through finally. For a function call, Lists help you track argument order. For binary data, Data Blocks help you distinguish the buffer from its views. Each concept answers a different debugging question; none requires you to construct a spec object.

Sort the specification clues
  • [[Type]]: THROW
  • [[Target]] of a labelled break
  • [[Writable]]: false
  • [[Getter]]
  • The List of call arguments
  • The bytes underlying an ArrayBuffer
Try it yourself
0 of 6 correct

Place each clue with the type of behavior it describes, then read why the match fits.

Choose a category for every card. You can change an answer at any time; Reset clears them all.

Common misconceptions

It is tempting to turn every term in a spec algorithm into a JavaScript object. Resist that shortcut. The spec is describing required behavior, not mandating a particular engine representation of each intermediate meta-value.

A missing own property has no descriptorPop out in the code editor (opens in a new tab)JavaScript
const todo = {};
console.log(Object.getOwnPropertyDescriptor(todo, "done"));

Line 1 creates an empty todo. Line 2 asks for its missing done property and prints undefined. That is an actual JavaScript result returned by the API; it is not the spec's marker EMPTY.

  • "Every completion is an error." NORMAL is routine; RETURN is abrupt but not an error.
  • "A descriptor is a plain object internally." The spec Record is different from the API's returned object.
  • "Not enumerable means private." A property remains readable and inspectable.
  • "EMPTY is undefined." EMPTY marks an absent spec value; undefined is a language value.
  • "Spec Lists are JavaScript Arrays." A List is an abstract ordered sequence.
  • "JS closures always snapshot." Abstract Closure capture rules do not rewrite JavaScript closure semantics.
Terms that sound alike but belong to different levels
TermWhat it isNot the same as
Completion RecordA spec account of value and control flowThe object returned by a JavaScript function
Descriptor recordInternal specification fieldsThe ordinary JS object returned by getOwnPropertyDescriptor
EMPTYA specification marker for no value or targetJavaScript undefined
Abstract ClosureCaptures values at creation in spec algorithmsA guarantee every JS closure snapshots variables

When reading a new spec algorithm, mark each term as either a language value you can observe or a meta-value explaining the transition. Then test a small related program. That keeps the model useful without pretending to inspect the engine directly.

Practice exercises

Predict each result before using the code editor. Try the input check, then open hints one at a time. The exercises move from reading a return to using descriptors in a website settings object.

Exercise 1 · Warm-upPredict a return from finally

A status function prepares a return, then runs a finally block. What single word appears in the console? Explain to yourself which return reaches the caller.

Starter codePop out in the code editor (opens in a new tab)JavaScript
function status() { try { return "ready"; } finally { return "closed"; } }
console.log(status());

Answer, then press Check. Spacing and letter case don’t matter.

    Exercise 2 · Warm-upInspect a missing flag

    A cart defines total with only a value. What does its descriptor say for enumerable? Compare the answer with an ordinary property assignment after you finish.

    Starter codePop out in the code editor (opens in a new tab)JavaScript
    const cart = {};
    Object.defineProperty(cart, "total", { value: 3 });
    console.log(Object.getOwnPropertyDescriptor(cart, "total").enumerable);

    Answer, then press Check. Spacing and letter case don’t matter.

      Exercise 3 · PracticeFollow the argument List

      A receipt builder accepts an item and price. Predict its output, including punctuation. Which argument belongs to the first parameter?

      Starter codePop out in the code editor (opens in a new tab)JavaScript
      function receipt(item, price) { return item + ":" + price; }
      console.log(receipt("tea", 3));

      Answer, then press Check. Spacing and letter case don’t matter.

        Exercise 4 · PracticeRead the captured value example

        This small JavaScript function retains an adjustment. What does the call print? Afterwards, explain why running this function does not expose a spec Abstract Closure.

        Starter codePop out in the code editor (opens in a new tab)JavaScript
        const adjustment = 2;
        const add = (price) => price + adjustment;
        console.log(add(3));

        Answer, then press Check. Spacing and letter case don’t matter.

          Exercise 5 · ChallengeCheck zeroed bytes after a write

          A fresh two-byte buffer backs a view. One byte changes. Type both bytes in the order printed and identify which part of the spec describes their initial values.

          Starter codePop out in the code editor (opens in a new tab)JavaScript
          const bytes = new Uint8Array(new ArrayBuffer(2));
          bytes[0] = 255;
          console.log(bytes.join(","));

          Answer, then press Check. Spacing and letter case don’t matter.

            Exercise 6 · ChallengeApply a descriptor to website settings

            A website lists visible settings with Object.keys. Its theme setting was defined explicitly. Which key does the list show? What flag would you change if that key had disappeared?

            Starter codePop out in the code editor (opens in a new tab)JavaScript
            const settings = {};
            Object.defineProperty(settings, "theme", { value: "light", enumerable: true });
            console.log(Object.keys(settings).join(","));

            Answer, then press Check. Spacing and letter case don’t matter.

              Check your understanding

              Decide first whether the question is about control flow, property attributes, or an abstract sequence of values. Then predict any printed result from the code, not from a memorized slogan.

              ECMAScript specification types quiz · 8 questionsScore: first tries count
              1. Question 1 of 8Which Completion Record type is not abrupt?

                Choose an answer to see the explanation.

              2. Question 2 of 8What does this finally example print?

                Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
                function status() { try { return "ready"; } finally { return "closed"; } }
                console.log(status());

                Choose an answer to see the explanation.

              3. Question 3 of 8What does [[Target]] describe?

                Choose an answer to see the explanation.

              4. Question 4 of 8What does this descriptor example print?

                Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
                const cart = {};
                Object.defineProperty(cart, "total", { value: 3 });
                console.log(Object.getOwnPropertyDescriptor(cart, "total").writable);

                Choose an answer to see the explanation.

              5. Question 5 of 8Can one descriptor contain both value and get?

                Choose an answer to see the explanation.

              6. Question 6 of 8What does a spec List represent in a function call?

                Choose an answer to see the explanation.

              7. Question 7 of 8What do freshly created Data Block bytes start as?

                Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
                const bytes = new Uint8Array(new ArrayBuffer(2));
                console.log(bytes.join(","));

                Choose an answer to see the explanation.

              8. Question 8 of 8How does an Abstract Closure capture an alias in the spec?

                Choose an answer to see the explanation.

              For a missed question, return to its small runnable example above and change one input. This is especially useful for finally, the enumerable checkbox, and newly initialized bytes.

              Key takeaways

              The specification gives names to intermediate work so its algorithms are precise. Your JavaScript program observes the effects, not the meta-values themselves.

              • Completion Records name NORMAL, BREAK, CONTINUE, RETURN, and THROW outcomes with value and target fields.
              • Abrupt means control transfers; a return is abrupt without being an error.
              • Property Descriptors explain own data or accessor properties and their writable, enumerable, and configurable attributes.
              • Lists are ordered; Records have named fields. Neither implies a JavaScript Array or object you can store.
              • Abstract Closures capture spec values; Data Blocks describe initialized byte storage.
              • Test observable effects with real JavaScript when reading a spec algorithm.

              Remember the one-liner.
              Specification types describe how JavaScript behaves; your code works with the resulting language values.

              Coming next: The Reference type & this. That lesson explains how property references give a method call its receiver. Afterward, Environment Records explain name lookup, and Abstract Operations connect the pieces.

              CompleteFrontend Clear concepts. Working examples.