3

I am evaluating a way of use the Singleton Pattern called Monostate in JavaScript.

I have some code like the following:

class Boss {
  get name() { return Boss._name }
  set name(value) {
    Boss._name = value;
  }

  get age() { return Boss._age }
  set age(value) {
    Boss._age = value
  }

  toString() {
    return `
      Boss' name is ${this.name}
      and he is ${this.age} years old.
    `
  }
}

Boss._age = undefined;
Boss._name = undefined;

But I am not deeply understanding the difference between the Class scope vs the Instance scope

I mean if I make the following:

let boss = new Boss();
boss.name = 'Bob';
boss.age = 55;

let boss2 = new Boss();
boss2.name = 'Tom';
boss2.age = 33;

console.log(boss.toString());
console.log(boss2.toString());

I will always get the data of the second instance with name Tom, but why?

Boss' name is Tom and he is 33 years old.
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
Ruben Saucedo
  • 239
  • 1
  • 13
  • you failed to find answer because (iirc) there is no `Class scope` and `Instance scope` in javascript. search for prototype / inheritance for better understand. (I believe there is a duplicate, but I cannot find it now) – apple apple Jul 10 '19 at 15:45
  • and what is `I will always get the first instance with name Bob` mean? – apple apple Jul 10 '19 at 15:45
  • 4
    `I will always get the first instance with name Bob, but why?` That's strange, I would have expected you always get Tom.. – Keith Jul 10 '19 at 15:45
  • Yeah, for example if we make a function inside boss that prints `name` and `age` as a result you will always obtain the data of the first instance `boss` -> `Bob` and `55` Let me add that function to the code and please try it – Ruben Saucedo Jul 10 '19 at 15:48
  • @Keith you are right, I am getting Tom data. A bad note, but I am lossing the Bob information – Ruben Saucedo Jul 10 '19 at 15:51
  • 2
    Yes you will, because your assigning to a `global` value `Boss`, in some respects assigning properties to `Boss` would be no different to say assigning to a global var called `bossStatic`.. If you don't use `this`, it's not assigning to an instance of anything. – Keith Jul 10 '19 at 15:54
  • 3
    That's the point of Monostate. Instances don't get their own data; data is stored on the class (`Boss`) instead. There is only one class, so there is only one set of data that is shared between all instances. – Paul Jul 10 '19 at 15:54
  • Makes sense, thanks Keith and Paulpro, do you suggest any documentation about it? – Ruben Saucedo Jul 10 '19 at 15:58
  • This is exactly the reason why you should avoid the global-state singleton pattern. – Bergi Jul 10 '19 at 18:27

3 Answers3

2

The reason this behaviour happens is due the fact that instances of Boss refer to the same Boss object to set and read their values. Instances of Boss set the _name and _age value on Boss. When reading from these instance attributes, the last value set is returned.

You can see this by the value of Boss._name that changes every time a an instance of Boss sets their name. After boss.name = 'Bob' the value of Boss._name will be 'Bob'. After setting boss2.name = 'Tom' the value of Boss._name will be 'Tom'.

I've added a snippet that hopefully displays the troubling behaviour better. In the scenario below boss1 and boss2 have their own storage while boss3 and boss4 share their storage (you use the Boss object as storage).

class Boss {
  constructor(storage) {
    this.storage = storage || {};
  }
  
  get name() {
    return this.storage.name;
  }
  
  set name(value) {
    this.storage.name = value;
  }
}

var boss1 = new Boss(),
    boss2 = new Boss(),
    sharedStorage = {},
    boss3 = new Boss(sharedStorage),
    boss4 = new Boss(sharedStorage);

boss1.name = "a";
boss2.name = "b";
boss3.name = "c";
boss4.name = "d";

console.log(boss1.name, boss2.name, boss3.name, boss4.name);
3limin4t0r
  • 19,353
  • 2
  • 31
  • 52
0

Boss doesn't refer to instance of class it will refer to the constructor function Boss which is only one in the whole code. In the methods you are adding property not to the instance but the constructor.

You need to use this to access the instance inside the method of an object.

class Boss {
  get name() { return this._name }
  set name(value) {
    this._name = value;
  }

  get age() { return this._age }
  set age(value) {
    this._age = value
  }
}


let boss = new Boss();
boss.name = 'Bob';
boss.age = 55;

let boss2 = new Boss();
boss2.name = 'Tom';
boss2.age = 33;


console.log(boss2.name);

console.log(boss.name);
Maheer Ali
  • 35,834
  • 5
  • 42
  • 73
0

Use this instead of the class name Boss to refer to the object instance from within the class definition:

class Boss {
  get name() {
    return this._name
  }
  set name(value) {
    this._name = value;
  }

  get age() {
    return this._age
  }
  set age(value) {
    this._age = value
  }

  toString() {
    return `
      Boss' name is ${this.name}
      and he is ${this.age} years old.
    `
  }
}

const b1 = new Boss();
b1.name = 'Tom';
b1.age = 55;

const b2 = new Boss();
b2.name = 'Bill';
b2.age = 33;

console.log(b1.toString());
console.log(b2.toString());

The Boss you are referring to in your code is actually the constructor when you really want the instance.

Tom O.
  • 5,730
  • 2
  • 21
  • 35