7

I know that using an underscore is just a convention to define private variables in JavaScript. But I came across a use case [while using a class] where use of _ seems mandatory to make the code work! My question is how _ is used under the hood by get and set.

The below code throws an error:

RangeError: Maximum call stack size exceeded

class User {
  constructor(name) {
    this.name = name;
  }

  get name() {
    return this.name;
  }

  set name(val) {
    this.name = val;
  }
}

let user = new User("Jhon");
console.log(user.name);

Now, if I use _ the code works!

class User {
  constructor(name) {
    this.name = name;
  }

  get name() {
    return this._name; // Added "_" here
  }

  set name(val) {
    this._name = val; // Added "_" here
  }
}

let user = new User("Jhon");
console.log(user.name);
Boann
  • 48,794
  • 16
  • 117
  • 146
Learner
  • 157
  • 1
  • 7
  • The `_` doesn't have special meaning here either. You just need to store your values in a data property with *any other name*. – Bergi Aug 03 '19 at 09:34

3 Answers3

6

Your first snippet uses the same name for the getter/setter as the property you try to assign to. So, in the constructor, when you do

this.name = name;

you are invoking the name setter, which does:

this.name = val;

again invoking the name setter, which recursively calls itself until the stack overflows.

Using a different variable name for the actual property the data is stored in (compared to the getters/setters) allows for the code to work as intended. It doesn't have to be prefixed with an underscore - pretty much anything other than the same name used by the getters/setters will work.

class User {
  constructor(name) {
    this.name = name;
  }

  get name() {
    return this.actualProperty;
  }

  set name(val) {
    this.actualProperty = val;
  }
}

let user = new User("Jhon");
console.log(user.name);

The _ before a property name is generally meant to indicate that the property is meant to be private, and that only the class itself should access it, but it's no guarantee - users of the class are still free to reference user._name if they wish. If you want actual private data for each instance, you should define the class in a closure with a WeakMap that holds the private data:

const User = (() => {
  const data = new WeakMap();
  return class User {
    constructor(name) {
      this.name = name;
    }

    get name() {
      return data.get(this);
    }

    set name(val) {
      data.set(this, val);
    }
  }
})();

let user = new User("Jhon");
console.log(user.name);
CertainPerformance
  • 356,069
  • 52
  • 309
  • 320
4

Just look at this piece of code logically:

get name() {
    return this.name
}

You read object.name. To return a value, the get name() getter reads this.name, which, in turn, resolves to get name(). And now, welcome to the infinite loop.

Hence, you need a separate variable name (to store the actual content of name) than the getter's name. That would be a private variable, and it has become a convention to prepend an underscore in these cases.

fjc
  • 5,590
  • 17
  • 36
2

The _ affix is commonly used for private properties.

You use private properties in addition to getters and/or setters when you want to be able to control how and when you can update a property, or add side effects to those actions.

You should also have a private property declaration in your class

class User {
  private _name; // <-- here

  constructor(name) {
    this.name = name;
  }

  get name() {
    return this._name;
  }

  set name(val) {
    this._name = val;
  }
}
Morphyish
  • 3,932
  • 8
  • 26