completefrontend
Open lab My progress
THE JAVASCRIPT FIELD GUIDE

Object.create & null-prototype objects

Create objects with a chosen prototype, inspect and change prototype links carefully, and use null-prototype dictionaries without inherited surprises.

By the end, you can
  • 01
    Make an object with a chosen prototypeUse Object.create(proto, descriptors) and check the link it makes.
  • 02
    Pick a safe dictionaryChoose between {}, Object.create(null), and Map when keys come from users.
  • 03
    Keep prototype links stableExplain why setPrototypeOf is for rare fixes, not for code that runs often.

Choose the prototype link up front

In the last few lessons, you saw that an object can have a prototype. A prototype is a second object that JavaScript checks when the first object doesn't have a property. This lesson shows how to pick that prototype yourself.

Object.create(proto) makes a new, empty object whose prototype is proto. It doesn't call a constructor function. It doesn't copy any properties from proto into the new object. It only adds a link.

The short version

Three tools, three jobs:

  • Object.create(proto) makes a new object linked to proto.
  • Object.create(null) makes a new object with no prototype, so it inherits nothing.
  • Object.setPrototypeOf(obj, proto) changes the link of an object that already exists. Use it rarely, because it makes code slower.
Real-life analogyA shop chooses its warehouse

A shop sells the items on its own shelves. When a shelf is empty, the staff ask the warehouse. If the warehouse doesn't have the item either, they ask the warehouse's own supplier, and so on. A new shop picks its warehouse before it opens. It can switch to another warehouse later, but switching on a busy day causes trouble for everyone.

In real life: A new shop picks its warehouse before it opens
In JavaScript: Object.create(proto) picks the prototype when the object is made
In real life: When a shelf is empty, staff fetch the item from the warehouse
In JavaScript: When a property is missing, JavaScript looks for it on the prototype
In real life: Switching warehouses on a busy day slows everyone down
In JavaScript: Object.setPrototypeOf changes the link later and slows the code down

JavaScript looks up a property in the same order. First it checks the object itself. Then it checks the prototype, then the prototype's prototype, and so on, until the chain of links ends.

Sometimes you want the opposite: an object with no prototype at all. This is called a null-prototype object. It holds only the keys you put in it. It has no inherited extras such as toString, constructor, or hasOwnProperty. That makes it a safe dictionary: an object used as a lookup table from keys to values. This ties in with Map & Set, Choosing a data structure, and JSON.

Object.create: make an object with a chosen prototype

STEP THROUGH

Object.create needs one argument: the object to use as the prototype, or null for none. It returns a new object. The new object keeps a hidden link to that prototype. The specification calls this link [[Prototype]]. The double square brackets mean your code can't read it as a normal property.

A rabbit linked to an animalPop out in the code editor (opens in a new tab)JavaScript
const animal = { eats: true };
const rabbit = Object.create(animal);
console.log(rabbit.eats);
console.log(Object.hasOwn(rabbit, "eats"));

Line by line:

  • Line 1 makes animal, which owns eats.
  • Line 2 makes rabbit and links it to animal. Nothing is copied.
  • Line 3 prints true. rabbit has no eats of its own, so JavaScript follows the link and finds it on animal.
  • Line 4 prints false. Object.hasOwn(obj, key) asks, "Does obj itself hold this key?" It ignores the prototype, and rabbit holds nothing.
Object.create(animal). The rabbit cart is empty. An arrow labeled prototype goes from rabbit to the animal cart, which holds eats: true. Reading rabbit.eats gives true.Object.create(animal)rabbitprototypeeats: trueanimalowns nothingrabbit.eats → true
rabbit starts empty. It only has a link to animal, so rabbit.eats is found on animal and gives true.

Object.create also takes an optional second argument: the properties to add to the new object. Each property is described by a property descriptor. A descriptor is a small object that says what the property holds and what you may do with it. Object.defineProperties uses the same format. A descriptor can have these parts:

  • value: the value the property holds.
  • writable: can you change the value later?
  • enumerable: does the property show up in lists, such as the one Object.keys gives?
  • configurable: can you delete the property or change its parts later?

The last three are yes-or-no flags. Any flag you leave out is false. This surprises many people.

The step-through below uses a bigger example. Its first line, "use strict", turns on strict mode: a set of stricter rules. In strict mode, some mistakes that would fail quietly throw an error instead. Line 9 sets enumerable: true, so name shows up in Object.keys. It leaves writable out, so at first name can't be changed.

Make an object and check its link
Step 0 of 7Ready
Your turn: follow the blue line

Guess the method's result, the two link checks, and whether line 16 can change name. Then step through the run.

Running in
  1. script
Next: line 2
"use strict";  greet() {    return "hi from base";  },}; const child = Object.create(base, {  name: { value: "Kid", enumerable: true },}); console.log(child.greet());console.log(Object.getPrototypeOf(child) === base);console.log(base.isPrototypeOf(child));try {  child.name = "Ada";} catch (error) {  console.log(error.name);}console.log(child.name);
CallStoreChangeResultBlue line: runs next. Ran: already executed.
Recent returnsNothing yet. Start with the blue line.
Change the descriptor for line 9

Changing the descriptor starts a new run. Guess what line 16 does before you step.

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.

What each part of the run shows:

  • Line 8 makes child, already linked to base. Line 9 gives it its own name property.
  • Line 12 calls child.greet(). child doesn't own greet, so JavaScript follows the link and runs the method from base. It prints hi from base. So the method was not copied.
  • Line 13 checks the link from the child's side. Object.getPrototypeOf(child) returns the prototype of child. That is base, so it prints true.
  • Line 14 checks the link from the other side. base.isPrototypeOf(child) asks, "Is base anywhere in the chain of prototypes behind child?" It prints true.
  • Line 16 tries to change name. writable was left out, so it is false. In strict mode, the change throws a TypeError, and line 18 prints the error's name. Line 20 prints Kid: the value never changed.

Pick the writable descriptor and step again. Now line 16 changes name, and line 20 prints Ada.

Read a property's descriptor flagsPop out in the code editor (opens in a new tab)JavaScript
const base = { label: "shared" };
const child = Object.create(base, {
  name: { value: "Asha", enumerable: true },
});
const descriptor = Object.getOwnPropertyDescriptor(child, "name");
console.log(descriptor.value);
console.log(descriptor.writable);
console.log(descriptor.enumerable);
console.log(descriptor.configurable);

Object.getOwnPropertyDescriptor(obj, key) gives back the descriptor of one property that obj owns. Use it to see the flags instead of guessing:

  • Line 5 reads the descriptor of name.
  • Line 6 prints the value, Asha.
  • Line 7 prints false. writable was left out, so it is false.
  • Line 8 prints true. Line 3 set enumerable: true.
  • Line 9 prints false. configurable was left out too.

If you want to change or delete a property later, set all three flags yourself.

Writing adds a property to the childPop out in the code editor (opens in a new tab)JavaScript
const animal = { eats: true };
const rabbit = Object.create(animal);
rabbit.eats = false;
console.log(rabbit.eats);
console.log(animal.eats);
console.log(Object.hasOwn(rabbit, "eats"));

Line 3 writes to rabbit.eats. Writing a property doesn't change the prototype. Instead, JavaScript adds a new eats property to rabbit itself. This own eats now hides the one on animal. Hiding an inherited property like this is called shadowing. The output:

  • Line 4 prints false: the value on rabbit.
  • Line 5 prints true: animal didn't change.
  • Line 6 prints true: rabbit now owns eats.
rabbit.eats = false. Now the rabbit cart holds its own eats: false card. It still links to the animal cart, which still holds eats: true.rabbit.eats = falseeats: falserabbitprototypeeats: trueanimalrabbit.eats → falseanimal.eats → true
Writing rabbit.eats puts a new card in rabbit. The card in animal doesn't change, so animal.eats is still true.
A descriptor with no flags hides and locks the propertyPop out in the code editor (opens in a new tab)JavaScript
const profile = Object.create(null, {
  name: { value: "Asha" },
});
console.log(Object.keys(profile));
profile.name = "Mina";
console.log(profile.name);

The descriptor on line 2 sets only a value, so all three flags are false. This code has no "use strict" line, so it runs with the older, looser rules, often called sloppy mode:

  • Line 4 prints [], an empty list. Object.keys lists only enumerable properties, and name is not enumerable.
  • Line 5 tries to change name. It is not writable, so nothing happens, and no error is shown.
  • Line 6 prints Asha. The value didn't change.

In strict mode, line 5 would throw a TypeError instead, as the step-through above showed.

Another way to pick a prototype: __proto__ inside an object literal

An object literal is an object written with curly braces, like { own: 1 }. Inside one, { __proto__: proto, own: 1 } is special syntax. It makes proto the prototype of the new object while the object is made.

Writing obj["__proto__"] = proto later is different. On a normal object, that write runs an old built-in setter: a hidden function that runs when you assign a property. That setter changes the prototype after the object already exists.

getPrototypeOf & setPrototypeOf

INTERACTIVE

Three built-in functions read or change the link of an object that already exists:

  • Object.getPrototypeOf(obj) returns the prototype of obj. This is the standard way to look at the link.
  • Object.setPrototypeOf(obj, proto) makes proto the new prototype of obj, then returns obj. If the change isn't allowed, it throws an error.
  • Reflect.setPrototypeOf(obj, proto) makes the same change. Instead of throwing, it returns true if the change worked and false if it didn't. Reflect is a built-in object that holds these lower-level versions of object operations.

Some links are not allowed:

  • You can set the prototype to null. The chain then ends at that object.
  • You can't make a loop. Object.setPrototypeOf(a, a) would make a its own prototype, so it throws a TypeError. In Node and Chrome the message is Cyclic __proto__ value. "Cyclic" means "going round in a circle".
  • Some objects don't allow a new prototype. A frozen object, made with Object.freeze, is locked against changes. Reflect.setPrototypeOf returns false for it.
Change a prototype, then try two changes that fail
Read and change a prototypePop out in the code editor (opens in a new tab)JavaScript
const toolbox = { label: "toolbox", find() { return "from toolbox"; } };const archive = { label: "archive", find() { return "from archive"; } };const item = { name: "receipt" }; console.log(item.find?.());Object.setPrototypeOf(item, toolbox);console.log(item.find());console.log(Object.getPrototypeOf(item).label);console.log(Reflect.setPrototypeOf(Object.freeze({}), archive));try {  Object.setPrototypeOf(item, item);} catch (error) {  console.log(error.name + ": " + error.message);}
Consolenot run

Press Run to run the code.

Try it yourself

Read the code first. It changes the prototype of item after item already exists, so you can see how the tools work. The lesson then explains why you should pick the link when you make the object.

This shows how the tools work. It is not advice to use them in code that runs often.
Object.setPrototypeOf(item, toolbox). The item cart holds name: receipt. A new arrow labeled prototype goes from item to the toolbox cart, which holds the find function. item.find() now gives from toolbox.Object.setPrototypeOf(item, toolbox)name: "receipt"itemprototypefind()toolboxitem.find() → "from toolbox"
Line 6 links the existing item to toolbox. Now item.find() finds the method on toolbox. It works, but changing a link later makes code slower.

Why use it rarely? The JavaScript engine is the program inside the browser (or Node) that runs your code. It learns where each object keeps its properties, so it can find them fast. Changing an object's prototype later throws away what it learned. Code that uses that object can then run slower.

Real-life analogySwitching warehouses on a busy day

Picture a shop that switches to a new warehouse in the middle of the lunch rush. It can be done in an emergency. But nobody would plan to do it every day. Treat setPrototypeOf the same way: know that it exists, but set the link when you make the object.

In real life: Staff have learned where everything is in the old warehouse
In JavaScript: The engine has learned how to find this object's properties fast
In real life: A new warehouse means everyone has to learn again
In JavaScript: A new [[Prototype]] link makes the engine forget what it learned
In real life: Picking the warehouse before opening causes no trouble
In JavaScript: Classes, constructors, and Object.create set the link when the object is made

Object.create(null) dictionaries

INTERACTIVE

A normal object, such as {}, has Object.prototype as its prototype. That is usually helpful. It gives every object methods such as toString and hasOwnProperty.

But sometimes you use an object as a dictionary, and the keys come from data you don't control, such as words a user types. Then the inherited names get in the way. Even before you add anything:

  • "toString" in {} is already true. The in operator checks the object and its prototypes.
  • Reading ({}).toString gives a function, not undefined.

Object.create(null) makes an object with no prototype. So it has none of these inherited names: no constructor, no toString, and no hasOwnProperty. A key such as "constructor" or "__proto__" is just a normal key there.

Left: const plain = {}. The plain cart is empty but links to an inherited toString function, so it has toString. Right: Object.create(null). The dict cart is empty and has no link, so nothing is inherited.const plain = {}plaintoString()inheritedhas toStringObject.create(null)dictnothing inherited
An empty {} still finds toString through its prototype. An object made with Object.create(null) has no prototype, so it holds only the keys you add.

You can still turn it into JSON. JSON.stringify reads only the object's own enumerable properties, so it doesn't need Object.prototype.

A null-prototype dictionary has no helper methodsPop out in the code editor (opens in a new tab)JavaScript
const dict = Object.create(null);
dict.apple = 2;
dict.constructor = 1;
console.log(typeof dict.toString);
console.log(typeof dict.hasOwnProperty);
console.log(Object.hasOwn(dict, "constructor"));
console.log(Object.entries(dict));

Line by line:

  • Line 1 makes the dictionary, with no prototype.
  • Lines 2 and 3 add two keys, apple and constructor.
  • Lines 4 and 5 print undefined. typeof gives the type of a value as text. Neither method exists here, because nothing is inherited.
  • Line 6 prints true. Object.hasOwn is a function on Object itself, not a method of the dictionary, so it still works.
  • Line 7 prints the two key-value pairs. Object.entries is also a function on Object.

So, to look inside a null-prototype dictionary, use functions on Object, such as Object.hasOwn, Object.keys, and Object.entries.

Try tricky keys in three kinds of dictionary
The same keys in three dictionariesPop out in the code editor (opens in a new tab)JavaScript
const keys = ["toString", "__proto__", "constructor", "hasOwnProperty", "apple"]; const plain = {};const dict = Object.create(null);const map = new Map(); for (const key of keys) {  const value = key === "__proto__" ? { polluted: true } : "value:" + key;  plain[key] = value;  dict[key] = value;  map.set(key, value);} console.log(typeof ({}).toString);console.log(plain.polluted);console.log(Object.hasOwn(plain, "__proto__"));console.log(dict["__proto__"]);console.log(typeof Object.create(null).hasOwnProperty);console.log(JSON.stringify(dict));
Reading __proto__real results
Plain objectprototype changed; polluted=true
Own __proto__?false
Null prototype[object Object]
hasOwnPropertyundefined
Mapvalue:__proto__
Size5

Before anything is added, ({}).toString is a function. JSON.stringify still works on the null-prototype dictionary: {"toString":"value:toString","__proto__":{"polluted":true},"constructor":"value:constructor","hasOwnProperty":"value:hasOwnProperty","apple":"value:apple"}

Try it yourself
Read key:

On a plain {}, writing __proto__ runs the inherited setter, which changes this object's prototype. On a null-prototype object and on a Map, __proto__ is just a key.

The keys are fixed; you can't type your own. Each result comes from running the code above in your browser.

The table compares three ways to store keys and values. Each row is one choice: when it fits best, and what to watch out for.

Which kind of dictionary to use
What you useWhen does it fit best?What should you watch out for?
A plain object, {}Use it for records whose field names you know, like user.name or options.theme.It inherits names like toString, and writing a __proto__ key changes its prototype.
Object.create(null)Use it for text keys that come from data or from users.It has no methods, so use Object.hasOwn(dict, key) and Object.entries(dict).
MapUse it when you add and delete keys often, or when keys aren't text.JSON.stringify doesn't turn it into JSON. Convert it first, for example with Array.from(map).

In the Choosing a data structure lesson, Object.groupBy returned a null-prototype object. That was on purpose. The group names come from data, and data might contain the word "constructor". A later lesson, Prototype pollution, covers the security side. For now, keep one habit: don't let keys from data turn into inherited behavior.

Prototype performance: keep links stable

This lesson doesn't give speed numbers. A small speed test, called a benchmark, often measures how the engine warms up, not how your real page behaves. But the advice is clear:

  • MDN and the teams who build engines say that changing a prototype after an object is made is slow. That covers both Object.setPrototypeOf and assigning to __proto__.
  • Long prototype chains also cost time. Each missing property can mean one more link to check.
What to do
  • Pick the prototype when you make the object: with a class, a constructor function, __proto__ inside an object literal, or Object.create.
  • Keep chains short and simple for objects your code uses very often.
  • Don't use setPrototypeOf as a normal way to change how an object behaves.
  • Measure real pages before you try to make them faster. Two later lessons, Hidden classes & object shapes and Prototypes inside the engine, explain what the engine does.
Which dictionary should you choose?
  • A fixed settings object with keys your code picks, like theme and pageSize
  • A word-count table whose keys come from article text
  • Data stored for each page element, with the element object as the key
  • A list of user choices you will add, delete, and loop over often
  • A JSON response from your API with known fields
  • A result like the one Object.groupBy(items, callback) returns
Try it yourself
0 of 6 correct

Put each situation under the best first choice. Then read the explanation for each one.

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

Where you’ll use this

Most everyday code uses object literals, classes, and Map. You reach for Object.create when the link itself is what you want. For example:

  • an object that hands some of its work to another object,
  • a stand-in object for tests that gets its default behavior from a real one,
  • small parts inside libraries and frameworks,
  • dictionaries with no inherited keys.
A class, a constructor, and Object.create all set a prototypePop out in the code editor (opens in a new tab)JavaScript
class User {
  greet() { return "hello " + this.name; }
}
function UserRecord(name) {
  this.name = name;
}
UserRecord.prototype.greet = function () {
  return "hello " + this.name;
};
function makeUser(name) {
  return Object.create(UserRecord.prototype, {
    name: { value: name, writable: true, enumerable: true, configurable: true },
  });
}
const classUser = new User();
classUser.name = "Asha";
const constructorUser = new UserRecord("Asha");
const linkedUser = makeUser("Asha");
console.log(classUser.greet());
console.log(constructorUser.greet());
console.log(linkedUser.greet());
console.log(Object.getPrototypeOf(linkedUser) === UserRecord.prototype);

Three objects, one result:

  • Lines 1 to 3: the class User puts greet on User.prototype. Line 15 makes classUser with new User(), so it is linked to User.prototype.
  • Lines 4 to 9: the constructor function UserRecord works the same way. Line 7 puts greet on UserRecord.prototype by hand. Line 17 makes constructorUser with new.
  • Lines 10 to 14: makeUser calls Object.create with UserRecord.prototype as the prototype. Line 18 makes linkedUser this way, without new.
  • Lines 19 to 21 each print hello Asha. Line 22 prints true: linkedUser has the same prototype that new UserRecord() gives.

Use a class or a constructor when you make many objects of the same kind. Use Object.create when you want to link one object straight to another, or set the property flags yourself.

Copy an object with its prototype and flagsPop out in the code editor (opens in a new tab)JavaScript
const animal = { eats: true };
const rabbit = Object.create(animal, {
  name: { value: "Bun", writable: true, enumerable: true },
});
const clone = Object.create(
  Object.getPrototypeOf(rabbit),
  Object.getOwnPropertyDescriptors(rabbit),
);
const spread = { ...rabbit };
console.log(clone.name);
console.log(Object.getPrototypeOf(clone) === animal);
console.log(Object.getOwnPropertyDescriptor(clone, "name").writable);
console.log(Object.getPrototypeOf(spread) === animal);

The code makes two copies of rabbit:

  • Lines 5 to 8 make clone. Object.getPrototypeOf(rabbit) gives the same prototype, animal. Object.getOwnPropertyDescriptors(rabbit) gives the descriptors of all the properties rabbit owns, flags included.
  • Line 9 makes spread with { ...rabbit }. The spread syntax ... copies the own enumerable values into a new plain object.

The output:

  • Lines 10 to 12 print Bun, true, and true. clone has the same name, the same prototype, and the same writable flag.
  • Line 13 prints false. spread got the usual Object.prototype, not animal.

Both are shallow copies. If a property held another object, both copies would point to that same inner object.

A safe word counter with a null-prototype dictionaryPop out in the code editor (opens in a new tab)JavaScript
function countWords(words) {  const counts = Object.create(null);  for (const word of words) {    counts[word] = (counts[word] || 0) + 1;  }  return counts;}console.log(countWords(["constructor", "constructor"]).constructor);

The word "constructor" is a well-known trap. Suppose line 2 made a plain {}. Then counts.constructor would already hold an inherited function before any word is counted. So (counts[word] || 0) + 1 on line 4 wouldn't start from 0. It would turn the function into text and stick 1 on the end, so the count becomes strange text instead of a number.

Line 2 uses Object.create(null) instead. The dictionary starts truly empty, with only the keys you add, so line 7 prints 2.

The next lesson, Delegation vs classical inheritance, builds on this. It shows a style called OLOO, short for "objects linked to other objects". OLOO uses Object.create to link objects straight to each other, instead of copying behavior down a family tree of classes.

Common misconceptions

“Object.create copies the prototype’s methods.”

No. It makes a link. When the new object doesn't have a property, JavaScript follows the link to find it.

“A null-prototype object can’t be turned into JSON.”

It can. JSON.stringify reads only the object's own enumerable properties, and a null-prototype object can have those.

“setPrototypeOf is just a harmless assignment.”

It is a standard function, but it is slow. Changing the link makes the engine forget what it learned about finding that object's properties.

“dict.hasOwnProperty always works.”

Not on a null-prototype dictionary. It has no prototype, so it has no hasOwnProperty method. Use Object.hasOwn(dict, key) instead.

“__proto__ is always a normal property name.”

On a normal object, writing to __proto__ runs an old built-in setter that changes the prototype. On a null-prototype object or a Map, __proto__ is just a key.

Practice: create, inspect, and count safely

5 EXERCISES
Exercise 1 · Warm-upPredict prototype checks

Read the code and predict the three lines it prints.

Starter codePop out in the code editor (opens in a new tab)JavaScript
const base = { kind: "base" };
const child = Object.create(base);
console.log(Object.getPrototypeOf(child) === base);
console.log("kind" in child);
console.log(Object.hasOwn(child, "kind"));

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

    Exercise 2 · PracticeFix the word counter

    The starter counts words such as apple correctly, but not constructor. Make the dictionary safe, then type the count it should print.

    Starter codePop out in the code editor (opens in a new tab)JavaScript
    function countWords(words) {
      const counts = {};
      for (const word of words) {
        counts[word] = (counts[word] || 0) + 1;
      }
      return counts;
    }
    console.log(countWords(["constructor", "constructor"]).constructor);

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

      Exercise 3 · PracticeMake an object with a chosen prototype

      Make an object linked to shared. Then answer: does the new object own the method?

      Starter codePop out in the code editor (opens in a new tab)JavaScript
      const shared = { describe() { return "shared method"; } };
      const item = Object.create(shared);
      item.name = "item";
      console.log(item.describe());
      console.log(Object.hasOwn(item, "describe"));

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

        Exercise 4 · PracticeWhen should you set a prototype?

        Finish the advice with one word: set prototypes at ___ time.

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

          Exercise 5 · ChallengeLoop over a null-prototype dictionary safely

          Which function lists the key-value pairs of the dictionary, without calling a method on the dictionary?

          Starter codePop out in the code editor (opens in a new tab)JavaScript
          const dict = Object.create(null);
          dict.apple = 2;
          dict.constructor = 1;
          for (const [key, value] of Object.entries(dict)) {
            console.log(key + ":" + value);
          }

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

            Quiz: check your understanding

            7 QUESTIONS

            Answer each question, then read why each choice is right or wrong.

            Lesson quiz · 7 questionsScore: first tries count
            1. Question 1 of 7What does Object.create(proto) do?

              Choose an answer to see the explanation.

            2. Question 2 of 7What does this inherited property check print?

              Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
              const base = { answer: 42 };
              const child = Object.create(base);
              console.log(Object.hasOwn(child, "answer"));

              Choose an answer to see the explanation.

            3. Question 3 of 7Why should code that runs often avoid Object.setPrototypeOf(obj, proto)?

              Choose an answer to see the explanation.

            4. Question 4 of 7What does this null-prototype dictionary print?

              Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
              const dict = Object.create(null);
              dict.constructor = 2;
              console.log(dict.constructor);

              Choose an answer to see the explanation.

            5. Question 5 of 7Which check works safely for a null-prototype dictionary?

              Choose an answer to see the explanation.

            6. Question 6 of 7What happens on a plain object when you assign obj["__proto__"] = { polluted: true }?

              Choose an answer to see the explanation.

            7. Question 7 of 7What does this print?

              Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
              const a = {};
              try {
                Object.setPrototypeOf(a, a);
              } catch (error) {
                console.log(error.name);
              }

              Choose an answer to see the explanation.

            Key takeaways

            • Object.create(proto) makes a new object linked to proto. It doesn't copy anything.
            • Its second argument takes property descriptors. Any flag you leave out (writable, enumerable, configurable) is false.
            • Object.getPrototypeOf reads the link. Object.setPrototypeOf changes it, but that is slow, so use it rarely.
            • Object.create(null) makes a dictionary with no prototype: no inherited toString, constructor, or hasOwnProperty.
            • When keys come from data, use Object.hasOwn, Object.entries, or a Map.

            Remember the one-liner.
            Object.create picks an object's prototype when the object is made. Object.create(null) picks no prototype at all.

            Up next: Delegation vs classical inheritance.

            CompleteFrontend Clear concepts. Working examples.