Maximum call stack size exceeded - Object Initializer with reference to self

183 views Asked by At

While playing around with a simple Javascript Object Initializer example, I could not find an explanation while the following code:

const obj = {
  self: this
};

console.log(obj);

would lead an Error: Maximum call stack size exceeded?

Meanwhile, a slightly different but may look similar initializer block as follows:

const obj = {
  self: obj
};

console.log(obj);

would lead an Error: Cannot access uninitialized variable. while the following example using method initializers works perfectly fine:

const obj = {
  name: 'Bob',
  a() {
    return obj.name;
  },
  b() {
    return this.name;
  },
};

console.log(obj.a());
console.log(obj.b());

Can you elaborate on the object initialization process and why the language allows the under initialization object and its this references usage inside method initializers and not in property values?

1

There are 1 answers

0
Newbie On

Case 1 this === global

Here this is the global javascript context. Typically is huge and contains many circular references. When you console.log() an object with circular references the outcome depends on the implementation (it does not throw an error on Chrome 93.0.4577.63).

console.log({ self: this });

See what is globalThis.

See how to print a circular reference.

Case 2 obj is undefined

This is invalid syntax. First the expression { self: obj } will be evaluated, then the assignment will be performed. But when the expression is evaluated obj does not exist hence leads to Error: Cannot access uninitialized variable..

const obj = { self: obj };

This will do what you expect:

const obj = {};
obj.self = obj;

Case 3

The last example is not related to the other examples at all.

  • You never create a circular reference nor attempt to log one.
  • You access obj from a() at a deferred time, so const obj = has already been performed.
const obj = {
    name: 'Bob',
    a() {
        // This is executed only when calling `a()`
        return obj.name;
    },
    b() {
        // `this` here is not the global context bu `obj`
        return this.name;
    },
};

// Both functions returns a string, so no circular dependency here 
console.log(obj.a());
console.log(obj.b());

This function will lead to the errors you see above:

const obj = {
    a() {
        obj.self = notExisting;
        return obj;
    },
    b() {
        this.self = this;
        return this;
    },
};