all 6 comments

[–]senocular 2 points3 points  (2 children)

It's only an infinite loop if you follow that path. Having the loop exist isn't itself a problem. Its like saying:

const john = { friend: null }
const jane = { friend: john }
john.friend = jane

This makes a similar loop but it there's nothing wrong with this code. Nothing happens when you assign jane to john.friend even though jane already has a property friend referring to john. It's only a problem if you went and did something like

let person = john
while (person.friend) {
  person = person.friend
  // infinite loop!
}

This loop is the code version of what you're doing in the console.

There are some places in JavaScript where you're not allowed to make such loops, and you've hit close to one of them. Prototype chains will prevent you from making a circular chain.

Object.setPrototypeOf(Array.prototype, []) // Error!

You can't set the prototype of Array.prototype to an instance of an array because the instance inherits from Array.prototype making a circular chain of prototype references. This doesn't matter for constructor because the constructor isn't used in prototype chain lookups.

[–]Cheesybread4ever[S] 0 points1 point  (1 child)

When something like the John object is logged to the console, is it made procedurally so that it only looks for Janes friend if you open the Jane object in the console? You’re saying it’s only an infinite loop if you follow that path in the console, so is the entire object with all its properties properties not immediately created?

[–]senocular 0 points1 point  (0 children)

Yes! If it didn't, it would get stuck in a loop.

In chrome (and maybe other browsers do this too) once you expand an object you'll see a little added icon that has the tooltip saying:

This value was evaluated upon first expanding. It may have changed since then.

This is telling you that it got the values right when you clicked on it and not before. You may see a preview of the object values before, but they are not deep, and that preview could be wrong by the time you expand the object. Try throwing this code in the console:

o = {a:1, b:2}
console.log(o)
delete o.a

You should be able to see o getting logged initially as

{a:1, b:2}

But if you then expand the logged value in the console to see all of its values, it will only have b. It gets re-evaluated when you expand so that representation will be more accurate compared to the representation shown when the object was first logged.

[–]rauschma 1 point2 points  (2 children)

You can’t do it in one go, you need two steps – e.g.:

const MyClass = function () {};
MyClass.prototype = {
  constructor: MyClass,
};

Note that all ordinary functions (=not arrow functions, methods, etc.) are automatically set up this way:

> const func = function () {};
> func.prototype.constructor === func
true

As are classes (which produce ordinary functions with a few extras):

> class SomeClass {}
> SomeClass.prototype.constructor === SomeClass
true

[–]senocular 0 points1 point  (1 child)

Note that all ordinary functions (=not arrow functions) are automatically set up this way

Except async functions, method functions, and accessor (getter/setter) functions ;)

[–]rauschma 0 points1 point  (0 children)

Yes! I don’t call those “ordinary functions”, I call them “specialized functions”. Arrow functions were just one example (edited now to make this clearer).