completefrontend
Open lab My progress
THE JAVASCRIPT FIELD GUIDE

Enumerability & ownership

Learn which JavaScript properties are own, inherited, enumerable, hidden, string, or symbol keys, and which loop or method can see each one.

What you’ll be able to predict
  • 01
    Answer the two visibility questionsSay if a property is stored in the object or in its prototype, and if it shows up in lists of keys.
  • 02
    Pick the right tool for listing keysCompare Object.keys, for...in, Reflect.ownKeys, and the tools for string keys and symbol keys.
  • 03
    Predict the order of keysUse the three groups: number keys, then other strings, then symbols.

Two visibility questions

An object can hold more properties than a quick loop shows you. A key is the name of a property, like name in user.name. Some tools list an object's keys, and each tool shows a different set.

To predict what a tool shows, ask two questions about each property:

  1. Ownership: is the property stored in this object itself? Or is it stored in another object that this one looks at next (an inherited property)?
  2. Enumerability: does the property show up in lists of keys? Or is it left out of them (a hidden property)?
Real-life analogyA phone book for a street

Think of a phone book. Some records belong to your own house. Some belong to your parents' house down the street. Each record is either printed in the phone book or kept out of it. Different clerks read different shelves: one reads only your house, one reads both houses, one reads every record.

In real life: A record kept at your own house
In JavaScript: An own property
In real life: A record kept at your parents' house
In JavaScript: An inherited property
In real life: Printed in the phone book
In JavaScript: enumerable: true
In real life: Kept out of the phone book
In JavaScript: enumerable: false

You have already met each piece on its own: for...in, Object.keys, symbols, the prototype chain, and the enumerable flag. This lesson puts them together. By the end, you can say which tool sees which key.

Own vs inherited

STEP THROUGH

An own property is stored in the object itself. An inherited property is stored in the object's prototype. The prototype is another object that JavaScript looks in next when the first object doesn't have the key. The prototype can have its own prototype, and so on; that line of objects is the prototype chain.

Reading user.name works the same for both kinds. But many tools that list keys care about the difference.

Two tools in the code below check for a key:

  • Object.hasOwn(user, "name") gives true only if user itself stores name.
  • "name" in user gives true if JavaScript can find name in user or in any prototype.

Object.create(prototype) on line 2 makes a new, empty object whose prototype is prototype. Guess which checks print true, then step through.

Stored here, or found in the prototype?
Step 0 of 6Ready
Your turn: follow the blue line

Guess which checks give true. Then step through: stored here, or found in the prototype?

Running in
  1. script
Next: line 1
const user = Object.create(prototype);user.name = "Ada"; console.log(Object.hasOwn(user, "name"));console.log(Object.hasOwn(user, "inheritedRole"));console.log("inheritedRole" in user);
CallStoreChangeResultBlue line: runs next. 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.
Two carts. The user cart holds name: Ada, an own property. The prototype cart holds inheritedRole, an inherited property. An arrow from user to prototype shows where JavaScript looks next.name: "Ada"userown propertynext, looks hereinheritedRoleprototypeinherited property
name is stored in user, so it is an own property. inheritedRole is stored in prototype; user only finds it by looking there next.
Use the right question

Object.hasOwn(obj, key) asks: "Is it stored right here?" The in operator asks: "Can JavaScript find it here or in a prototype?" Both are useful. They answer different questions.

Enumerable vs non-enumerable

JavaScript keeps a few settings for every property. Together they are called the property's descriptor. One setting is enumerable. It is true or false.

  • An enumerable property shows up in lists of keys.
  • A non-enumerable property is left out of those lists. This lesson also calls it a hidden property.

How you make a property decides the setting:

  • Writing user.name = "Ada" or { name: "Ada" } makes an enumerable property.
  • Object.defineProperty(obj, key, settings) makes a property with the settings you give. If you don't say enumerable: true, the property is hidden.

Hidden does not mean secret. Code that knows the name can still read the property. Only tools that list keys skip it, such as Object.keys, for...in, and JSON.stringify.

A hidden property can still be readPop out in the code editor (opens in a new tab)JavaScript
const user = { name: "Asha" };Object.defineProperty(user, "password", { value: "secret" }); console.log(user.password);console.log("password" in user);console.log(user.propertyIsEnumerable("password"));

Line 2 adds password with Object.defineProperty, so it is hidden. Then:

  • Line 4 reads user.password by name. It prints "secret".
  • Line 5 asks if JavaScript can find password. The in check doesn't care about lists, so it prints true.
  • Line 6 asks propertyIsEnumerable. It gives true only for an own property that shows up in lists. password is hidden, so it prints false.
The user cart holds name and password. user.password gives secret. The in check gives true. propertyIsEnumerable gives false, because password is hidden.namepassworduserpassword is hiddenuser.password"secret""password" in usertruepropertyIsEnumerablefalse
password is really there: you can read it, and in finds it. It is only marked as hidden, so propertyIsEnumerable says false.

You can also read the descriptor itself. Object.getOwnPropertyDescriptor(obj, key) gives back an object with all the settings of one property.

Read the enumerable settingPop out in the code editor (opens in a new tab)JavaScript
const user = { name: "Asha" };Object.defineProperty(user, "password", { value: "secret" });const password = Object.getOwnPropertyDescriptor(user, "password"); console.log(password.enumerable);console.log(Object.hasOwn(user, "password"));

Line 3 gets the descriptor of password and keeps it in a variable. Line 5 prints its enumerable setting: false. Line 6 prints true, because password is still stored in user itself. Hidden and own can both be true at once.

The two questions don't depend on each other. In the table, each row is one kind of key. Each column says whether it is listed or hidden.

Ownership and listing are two separate questions
Kind of keyWhen it shows up in listsWhen it is hidden
Own propertyobj.name = "Ada" makes a key that Object.keys lists.defineProperty(obj, "secret", { value: 1 }) makes a key that Object.keys skips.
Inherited propertyfor...in lists it.Reading it by name works, but no list shows it.
String keyThe common lists show it.getOwnPropertyNames still finds it.
Symbol keySpread and Object.assign copy it.getOwnPropertySymbols and Reflect.ownKeys still find it.

Object.keys vs for…in vs Reflect.ownKeys

INTERACTIVE

A common mistake is to think every key tool is a bigger or smaller Object.keys. It isn't. Each tool has its own rule. The rule answers three questions: own or inherited, listed or hidden, string or symbol.

  • Object.keys, Object.values, and Object.entries read own keys that are listed and are strings.
  • for...in reads listed string keys of the object, then listed string keys of its prototypes.
  • JSON.stringify turns an object into JSON text. It uses own listed string keys and skips symbols.
  • Object.assign and spread ({ ...obj }) copy own listed keys, both strings and symbols.
  • Reflect.ownKeys reads every own key: strings and symbols, listed or hidden.

The playground runs every tool on the same object, in your browser. Each card shows what one tool found.

Which tool sees what?
One object with every kind of keyPop out in the code editor (opens in a new tab)JavaScript
const shown = Symbol("shown");const hiddenSymbol = Symbol("hidden");const proto = { inheritedEnum: "from prototype" };Object.defineProperty(proto, "inheritedHidden", { value: "secret" });const item = Object.create(proto);item[2] = "integer-like";item.title = "Guide";Object.defineProperty(item, "secret", { value: 42 });item[shown] = "symbol value";Object.defineProperty(item, hiddenSymbol, { value: "symbol secret" });
ToolWhat it found

The results appear once the page has loaded in your browser.

Try it yourself

Running the tools in your browser…

The object has a listed string key, a hidden string key, a number key, two symbol keys, and two inherited keys.
Fun fact

Object.values and Object.entries don't have their own rule. They take the same keys as Object.keys. Then they give back the values, or key-and-value pairs, for those keys.

Copying and turning into JSON are close, but not the same. Spread and Object.assign keep listed symbol keys. JSON.stringify drops every symbol, because JSON text can only have string keys. So pick the tool by what you are making: a copy of the object, or JSON text.

Object.assign copies listed keys, not hidden onesPop out in the code editor (opens in a new tab)JavaScript
const id = Symbol("id");const source = { name: "Asha" };Object.defineProperty(source, "secret", { value: 7 });source[id] = 42; const copy = Object.assign({}, source);console.log(Object.keys(copy));console.log(Object.getOwnPropertySymbols(copy).length);console.log("secret" in copy);

Lines 1 to 4 make a source object with three keys:

  • name: a listed string key.
  • secret: a hidden string key.
  • id: a listed symbol key.

Line 6 copies source into a new empty object. Line 7 prints [ 'name' ]. Line 8 prints 1, so the symbol was copied. Line 9 prints false, so the hidden secret was not copied.

getOwnPropertyNames & getOwnPropertySymbols

A key is either a string or a symbol. A symbol is a unique value made with Symbol("label"). Many tools only look at string keys. Two tools find hidden keys too, one for each kind:

  • Object.getOwnPropertyNames(obj) gives every own string key, listed or hidden.
  • Object.getOwnPropertySymbols(obj) gives every own symbol key, listed or hidden.

Use both when you need both kinds. Or use Reflect.ownKeys(obj), which gives every own key in one list.

A symbol key can be listed or hidden, just like a string key. Two things decide which tools see it:

  • Being a symbol decides which tools can see it at all.
  • The enumerable setting decides whether spread and Object.assign copy it.
Real-life analogyReflect.ownKeys opens every drawer

Your house keeps its records in two drawers: one for name cards, one for badge cards. One clerk reads only the printed phone book. Another clerk opens the name drawer. Another opens the badge drawer. The last clerk empties both drawers onto the desk and reads every card.

In real life: The printed phone book
In JavaScript: Object.keys
In real life: The drawer of name cards
In JavaScript: Object.getOwnPropertyNames
In real life: The drawer of badge cards
In JavaScript: Object.getOwnPropertySymbols
In real life: Both drawers, emptied onto the desk
In JavaScript: Reflect.ownKeys
Find string keys, symbol keys, or bothPop out in the code editor (opens in a new tab)JavaScript
const visible = Symbol("visible");const hidden = Symbol("hidden");const user = { name: "Asha" };user[visible] = "tag";Object.defineProperty(user, "password", { value: "secret" });Object.defineProperty(user, hidden, { value: "note" }); console.log(Object.getOwnPropertyNames(user));console.log(Object.getOwnPropertySymbols(user).length);console.log(Reflect.ownKeys(user).length);

Lines 1 and 2 make two symbols. Lines 3 to 6 give user four keys:

  • Line 3: name, a listed string key.
  • Line 4: the symbol visible, a listed symbol key.
  • Line 5: password, a hidden string key.
  • Line 6: the symbol hidden, a hidden symbol key.

Line 8 prints both string keys. Line 9 prints 2, the number of symbol keys. Line 10 prints 4: Reflect.ownKeys found all of them.

The user cart holds two string keys, name and password, and two symbol keys, visible and hidden. getOwnPropertyNames gives name and password. getOwnPropertySymbols gives 2. Reflect.ownKeys gives 4.[visible][hidden]namepasswordusergetOwnPropertyNamesname, passwordgetOwnPropertySymbols2Reflect.ownKeys4
The bottom cards are string keys; the top cards, in square brackets, are symbol keys. Each tool finds hidden keys too, but only of its own kind. Reflect.ownKeys finds all four.
Listed by Object.keys?
  • Own enumerable string key
  • Own non-enumerable string key
  • Inherited enumerable string key
  • Own enumerable symbol key
  • Own enumerable key "0" on an array
  • Array length
Try it yourself
0 of 6 correct

Put each kind of key in a group. Object.keys lists a key only if all three are true: it is own, it is enumerable, and it is a string.

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

Property order rules

STEP THROUGH

Tools that list own keys, like Object.keys and Reflect.ownKeys, follow a fixed order. It is not always the order you added the keys. The keys come in three groups:

  1. Number keys first, smallest number first.
  2. Other string keys next, in the order they were added.
  3. Symbol keys last, in the order they were added.

A number key here is a string that is a whole number of 0 or more, written the normal way, such as "2" or "10". "01" starts with an extra zero, and "-1" is below zero. So both stay in the group of other strings.

Step through the order of keys
Step 0 of 6Ready
Your turn: follow the blue line

Step through the three groups: number keys, other strings, symbols.

Running in
  1. script
Next: line 1
const b = Symbol("b");const record = { "10": "ten", "2": "two" };record.name = "Ada";record.city = "Pune";record[b] = "second symbol";record[a] = "first symbol"; console.log(Reflect.ownKeys(record));
CallStoreChangeResultBlue line: runs next. Ran: already executed.
Recent returnsNothing yet. Start with the blue line.
Choose the keys

A new choice starts the steps again. Guess the order that Reflect.ownKeys prints first.

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.
Reflect.ownKeys(record) gives the keys in three groups. First the number keys 2 and 10. Then the other strings name and city. Then the symbols b and a.Reflect.ownKeys(record)"2""10"1. number keyssmallest first"name""city"2. other stringsin order addedSymbol(b)Symbol(a)3. symbolsin order added
"2" comes before "10" because number keys go smallest first. name was added before city, and symbol b before symbol a, so they keep that order.

Built-ins hide their housekeeping

INTERACTIVE

Built-in objects, like arrays, use the same enumerable setting.

  • Array positions are listed. So Object.keys(["a", "b"]) gives ["0", "1"].
  • An array's length is an own property too, but it is hidden. So Object.keys and for...in don't show it.

Class methods work the same way. A method is stored in the class's prototype, not in each object, and it is hidden. So a loop over an object made by the class shows only its data, not its method names.

On line 8, new Counter() makes an object from the class. The results run in your browser.

Arrays and classes hide some keys
List the keys of an array and a class objectPop out in the code editor (opens in a new tab)JavaScript
class Counter {  increment() {    return 1;  }} const array = ["a", "b"];const counter = new Counter(); console.log(Object.keys(array));console.log("length" in array);console.log(Object.keys(counter));console.log(Object.getOwnPropertyNames(Counter.prototype));
CheckResult

The results appear once the page has loaded.

Try it yourself

Arrays and classes are normal objects. Some of their properties are set to hidden. Your browser ran this code.

Array positions are listed. An array's length and a class's methods are hidden.

Where you’ll use this

These rules matter whenever you copy an object, turn it into JSON, or loop over its keys:

  • Use Object.keys for plain data.
  • Use Object.hasOwn inside for...in when inherited keys would be a bug.
  • Use descriptors when a copy must keep hidden properties, symbols, getters, setters, or settings.
Copy every own property

Object.defineProperties({}, Object.getOwnPropertyDescriptors(source)) copies every own property with all its settings. Use it instead of spread when hidden properties or symbols matter.

Spread copies only the listed keysPop out in the code editor (opens in a new tab)JavaScript
const user = { name: "Asha" };Object.defineProperty(user, "sessionToken", { value: "abc123" });const publicProfile = { ...user }; console.log(Object.keys(publicProfile));console.log(user.sessionToken);

Line 2 adds a hidden sessionToken. The spread on line 3 copies only own listed keys. So line 5 shows that publicProfile has name but no token. Line 6 reads the token from the original by name, and prints "abc123".

This is handy for making a copy to show on screen. It does not keep anything safe. Never put a real secret in an object that runs in the browser.

A common use: leave a property out of JSON without deleting it. Make the property hidden. Code that knows the name can still read it, but JSON.stringify leaves it out.

Keep a screen-only field out of JSONPop out in the code editor (opens in a new tab)JavaScript
const profile = { name: "Asha", theme: "dark" };Object.defineProperty(profile, "lastViewedPage", { value: "/home" }); console.log(JSON.stringify(profile));console.log(profile.lastViewedPage);

Line 1 makes the profile. Line 2 adds lastViewedPage, which only the page itself needs, as a hidden property. Line 4 prints JSON with only name and theme. Line 5 still reads "/home". This changes what goes into the JSON. It does not protect a password or token.

Common misconceptions

“Object.keys shows all properties.”

It shows only own keys that are enumerable and are strings. It skips symbol keys, hidden keys, and inherited keys.

“for...in is the same as Object.keys.”

for...in also shows enumerable string keys from prototypes. Check each key with Object.hasOwn when you want only the object's own keys.

“Non-enumerable means private.”

A hidden property is only left out of some lists. Any code that knows its name can still read it. It keeps nothing safe.

“propertyIsEnumerable checks whether JavaScript can find the key.”

It asks a smaller question. It gives true only for an own property that is enumerable. For an inherited property, in gives true, but this method gives false.

“Symbols are invisible.”

Many tools that read string keys skip symbols. But Object.getOwnPropertySymbols and Reflect.ownKeys find them.

“Object key order is random.”

Tools that list own keys follow a fixed order: number keys, then other strings, then symbols. You stepped through it above.

Each row of this table is one tool. Read across to see which keys it includes.

Which keys each tool includes
ToolOnly own keys?Only enumerable keys?Which kind of keys?
Object.keysYes, it skips inherited keys.Yes, it skips hidden keys.Strings only.
for...inNo, it adds inherited keys.Yes, it skips hidden keys.Strings only.
Object.getOwnPropertyNamesYes, it skips inherited keys.No, it finds hidden keys too.Strings only.
Object.getOwnPropertySymbolsYes, it skips inherited keys.No, it finds hidden keys too.Symbols only.
Reflect.ownKeysYes, it skips inherited keys.No, it finds hidden keys too.Strings and symbols.

Practice: predict the listing

5 EXERCISES
Exercise 1 · Warm-upPredict what each tool lists

Predict the two printed lines. Type the first line, a slash, then the second line.

Starter codePop out in the code editor (opens in a new tab)JavaScript
const proto = { inherited: 1 };
const box = Object.create(proto);
box.visible = 2;
Object.defineProperty(box, "hidden", { value: 3 });
console.log(Object.keys(box).join(","));
const seen = [];
for (const key in box) seen.push(key);
console.log(seen.join(","));

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

    Exercise 2 · PracticeCopy every own property

    Which pair of tools copies the hidden string property and the symbol property?

    Starter codePop out in the code editor (opens in a new tab)JavaScript
    const id = Symbol("id");
    const source = { name: "Ada" };
    Object.defineProperty(source, "secret", { value: 7 });
    source[id] = 42;
    const copy = Object.defineProperties({}, Object.getOwnPropertyDescriptors(source));
    console.log(Reflect.ownKeys(copy).map(String).join(","));
    console.log(copy.secret);

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

      Exercise 3 · PracticeExplain an inherited key

      Why does the loop print both keys?

      Starter codePop out in the code editor (opens in a new tab)JavaScript
      const proto = { inherited: "yes" };
      const child = Object.create(proto);
      child.own = "yes";
      const seen = [];
      for (const key in child) seen.push(key);
      console.log(seen.join(","));

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

        Exercise 4 · ChallengePredict the key order

        Predict the exact line that Reflect.ownKeys prints.

        Starter codePop out in the code editor (opens in a new tab)JavaScript
        const s = Symbol("s");
        const item = { "10": "ten", "2": "two", apple: 1 };
        item["01"] = "leading";
        item[s] = "symbol";
        console.log(Reflect.ownKeys(item).map(String).join("|"));

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

          Exercise 5 · ChallengeLeave a property out of JSON

          What JSON text prints after password becomes hidden?

          Starter codePop out in the code editor (opens in a new tab)JavaScript
          const user = { name: "Ada", password: "secret" };
          Object.defineProperty(user, "password", { enumerable: false });
          console.log(JSON.stringify(user));
          console.log("password" in user);

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

            Quiz: check your understanding

            7 QUESTIONS

            Every choice explains itself, including the wrong ones.

            Lesson quiz · 7 questionsScore: first tries count
            1. Question 1 of 7Which two questions describe a property’s visibility?

              Choose an answer to see the explanation.

            2. Question 2 of 7What does Object.keys print for the child object?

              Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
              const proto = { inherited: 1 };
              const obj = Object.create(proto);
              obj.own = 2;
              console.log(Object.keys(obj).join(","));

              Choose an answer to see the explanation.

            3. Question 3 of 7Which tool gives every own string key, listed or hidden?

              Choose an answer to see the explanation.

            4. Question 4 of 7What does Object.keys print when the object also has a symbol?

              Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
              const s = Symbol("id");
              const obj = { name: "Ada" };
              obj[s] = 1;
              console.log(Object.keys(obj).join(","));

              Choose an answer to see the explanation.

            5. Question 5 of 7Which tool sees every own key: strings and symbols, listed and hidden?

              Choose an answer to see the explanation.

            6. Question 6 of 7What order does Reflect.ownKeys print for these string keys?

              Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
              const obj = { "10": "ten", "2": "two", a: 1 };
              obj.b = 2;
              console.log(Reflect.ownKeys(obj).join("|"));

              Choose an answer to see the explanation.

            7. Question 7 of 7How can you leave a property out of JSON.stringify without deleting it?

              Choose an answer to see the explanation.

            Key takeaways

            • An own property is stored in the object itself. An inherited property is stored in its prototype.
            • An enumerable property shows up in lists of keys. A hidden (non-enumerable) property is left out, but it still exists.
            • Object.keys lists own keys that are enumerable and are strings.
            • for...in lists enumerable string keys, including inherited ones.
            • Reflect.ownKeys lists every own key: strings and symbols, listed or hidden.
            • Own keys come in a fixed order: number keys, then other strings, then symbols.

            Remember the one-liner.
            Pick a key tool by three questions: own or inherited, listed or hidden, string or symbol.

            Up next: try, catch & finally.

            CompleteFrontend Clear concepts. Working examples.