Prototype Methods, Objects Without __proto__

Let’s first explore the modern methods for setting up a prototype.

Amongst them are:

  1. Object.create(proto[, descriptors]). This method is used for creating an empty object with given proto as [[Prototype]], as well as, optional property descriptors.
  2. Object.getPrototypeOf(obj). This one helps to return the [Prototype]] of the obj.
  3. Object.setPrototypeOf(obj, proto). The method is aimed at setting the [[Prototype]] of the obj to proto.

So, you can use the mentioned methods instead of the __proto__.

Take a look at the following example:

let animal = {
  speaks: true
};
let dog = Object.create(animal);// create a new object with animal as a prototype

console.log(dog.speaks); // true
console.log(Object.getPrototypeOf(dog) === animal); // true

Object.setPrototypeOf(dog, {}); // change the prototype of  dog to {}

There is an alternative argument for Object.create: property descriptors. You have the option of providing additional properties to the object there.

For example:

let animal = {
  speaks: true
};

let dog = Object.create(animal, {
  runs: {
    value: true
  }
});

console.log(dog.runs); // true

For performing an object cloning, it’s more convenient to use Object.create than copying properties in for..in.

The example looks like this:

// fully identical shallow clone of obj
let clone = Object.create(Object.getPrototypeOf(obj), Object.getOwnPropertyDescriptors(obj));

The call mentioned above creates the exact copy of obj, which includes all the properties: non-enumerable and enumerable, data properties, setters/getters, and with the suitable [[Prototype]].

Short History

There are many ways of managing the [[Prototype]].

One of the reasons is that the “prototype” property has worked since ancient times. In 2012, Object.create was added to the standard. It started to allow creating objects with __proto__ accessor.

Later, in 2015, Object.setPrototypeOf and Object.getPrototypeOf appeared in the standard for performing the same functionality as __proto__.

Nowadays, all these methods are at the developers’ disposal.

Technically, it is possible to get/set [[Prototype]] at any time. But, commonly, it is set once while creating the object and is not modified anymore. Changing a prototype with Object.setPrototypeOf or obj.__proto__= can be considered a slow operation. The reason is that it breaks internal optimizations for object property access operations.

"Very Plain" Objects

You can use objects as associative arrays for storing key/value pairs. But in the event of trying to store user-provided keys inside it, a unique error may occur: all the keys will work well except "__proto__".

Look at this example:

let obj = {};

let key = prompt("What is the key value?", "__proto__");
obj[key] = "some value";

console.log(obj[key]); // [object Object], not "some value"!

Here when the user types __proto__, the assignment is ignored. Thus, the __proto__ property is unique. It can be either null or an object. A string can’t transform into a prototype.

So, the main problem is that "__proto__" is not saved accurately. It’s a bug. Some unexpected things can happen while assigning toString that is a function by default.

To avoid such kinds of problems, first, you can switch to using Map .

The Object may also be helpful. The __proto__ is not an object property, but an accessor property of Object.prototype.

Check out this case:

In obj.__proto__ is set or read, the appropriate setter/getter is called from its prototype, as well as, sets/gets [[Prototype]].

If you intend to use an object as an associative array, you can implement it with a trick.

Here is how to do it:

let obj = Object.create(null);

let key = prompt("What's the key value?", "__proto__");
obj[key] = "some value";

console.log(obj[key]); // "some value"

Object.create(null) generates an empty object without a prototype:

It can be assumed that there isn’t any inherited getter/setter for __proto__. It is handled as an ordinary data property.

Such objects can be named “very plain” because they are more straightforward than the ordinary plain object {...}.

The disadvantage is that such objects lack built-in object methods (toString).

For instance:

let obj = Object.create(null);

console.log(obj); // Error (no toString)

Usually, it’s fine for associative arrays.




Do you find this helpful?

Related articles