0

I am new to JS and was learning abstraction and the use of getter and setters in JS. So, as far as I know, getter and setter can be achieved with object.defineProperty but look guys, what is the reason for giving access to private property via getter and setter instead of public property. Since, with getter we can read private property and with setter we can set new value to private property. Isn't it the same if we use public property which also allows to read value of that public property and set new value to it. I just cannot get why to use getter and setter when we can use public property to achieve both getter and setter. Hope you got my point.

  • 1
    Possible duplicate of [Why use getters and setters/accessors?](https://stackoverflow.com/questions/1568091/why-use-getters-and-setters-accessors) – antonku Jul 21 '19 at 08:23
  • I *personally* prefer public fields. But you should be aware what getter and setter can do. – apple apple Jul 21 '19 at 08:24
  • and I don't think @antonku 's link is a good duplicate. since you can change a field to getter/setter with exactly **zero** change of other codes. in javascript. – apple apple Jul 21 '19 at 08:26
  • @appleapple, thank you for your kind comments, I just want to ask how is it possible to hide implementation details thanks to getter and setter since we, anyways, have access to private value and can retrieve it and modify value of it with getter and setter. Isn't it the same if we use public property? – user11807902 Jul 21 '19 at 08:57
  • @user11807902 one point is they don't actually needs to be coupled. You can have getter-only or setter-only, also you can combine multiple info into a field (like month/year/day for date, can be store in single milliseconds filed) – apple apple Jul 21 '19 at 09:00
  • but yes, if all you do is the same as public field, then it's not needed (and I'd prefer not use it). – apple apple Jul 21 '19 at 09:04

2 Answers2

7

A getter/setter is generally useful for when, on property access, the class wants to do something in addition to simply setting/retrieving the data. For example:

class MyClass {
  constructor() {
    this.setHistory = [];
  }
  set item(newVal) {
    this.setHistory.push(newVal);
    this._item = newVal;
  }
  get item() {
    return this._item;
  }
}

const f = new MyClass();
f.item = 'foo';
f.item = 'bar';
console.log(f.setHistory);

Above, storing the "history" of what items have previously been set is possible with a setter function. Without the setter function, with only a public item property, the class cannot save the history.

If all a getter/setter is doing is setting or retrieving a value, you're right that it's mostly useless, eg:

class MyClass {
  set item(newVal) {
    this._item = newVal;
  }
  get item() {
    return this._item;
  }
}

const f = new MyClass();
f.item = 'foo';
f.item = 'bar';
console.log(f.item);

Here, the setters and getters aren't doing anything useful at all, so you may well just remove them. Generally, use a setter/getter when you want to do something other than just store/retrieve values.

Setters and getters can also be useful to hide implementation details unimportant to consumers. (For example, assigning to .innerHTML is pretty similar to invoking a setter.)

They can also be useful when debugging, to (for example) log when an object is being changed.

One caveat: the first snippet above doesn't actually use "private" properties, it simply uses the _-prefix convention to indicate that the property shouldn't be used externally. For a true private property that can't be accessed from the outside, you should define a WeakMap and create the class with an IIFE:

// with this class,
// neither item nor history can be accessed without going through the getters
const MyClass = (() => {
  const privateDatas = new WeakMap();
  return class MyClass {
    constructor() {
      privateDatas.set(this, { setHistories: [] });
    }
    set item(newVal) {
      const privates = privateDatas.get(this);
      privates.setHistories.push(newVal);
      privates.item = newVal;
    }
    get item() {
      return privateDatas.get(this).item;
    }
    getSetHistory() {
      return privateDatas.get(this).setHistories;
      // if you want to make sure the internal array doesn't get mutated outside,
      // return a copy: .setHistories.slice()
    }
  }
})();

const f = new MyClass();
f.item = 'foo';
f.item = 'bar';
console.log(f.getSetHistory());
CertainPerformance
  • 356,069
  • 52
  • 309
  • 320
  • I don't think the private field part really matters to this question. Besides, I think you should use private field on `_item`, not `setHistories`. Lastly, one can still mutate the history returned by `getSetHistory` if I get it right. – apple apple Jul 21 '19 at 08:38
  • @CertainPerformance, ok, if all I need is to get and set value, I can simply use public variable instead of get&set, RIGHT? The next question, your statement "Setters and getters can also be useful to hide implementation details unimportant to consumers". How is it possible to hide something if we can get private data and set value to it. Isn't it the same as public property. Hope you got my point – user11807902 Jul 21 '19 at 08:48
  • @user11807902 The stuff to be hidden is *implementation details*, not the actual data that gets returned. Imagine if the getter had to do some calculations before returning the data - those calculations can be somewhat hidden by exposing a getter rather than exposing a function. – CertainPerformance Jul 21 '19 at 08:55
  • @CertainPerformance, please can you add clear example code I just cannot get it. By the way, if all I need is to retrieve property with getter and set value to that property with setter, I can simply use public variable instead of get&set, RIGHT? – user11807902 Jul 21 '19 at 09:03
  • @user11807902 The `this.setHistory.push(newVal);` in the first snippet is an example of an implementation detail that consumers of `MyClass` don't really care about. They don't care *how* the class does what it does, they just care that the class's external interface(s) work properly with input/output. Think of it like a black box. Yes, if you only want to set and retrieve a value, there's very little use to having getters/setters. – CertainPerformance Jul 21 '19 at 09:05
0

The field don't really needs to exist for each getter/setter. One example is the Date which I believe only store milliseconds but you can get all the information (it doesn't use the new getter/setter syntax but the concept is the same)


Here is a simple wrapper to Date using the new syntax, hope it makes it more clear.

class NewSyntaxDate{
  constructor(){
    this.date = new Date()
  }
  get Hours(){return this.date.getHours()}
  set Hours(h){this.date.setHours(h)}
  get Minutes(){return this.date.getMinutes()}
  set Minutes(m){this.date.setMinutes(m)}
}

let date = new NewSyntaxDate()
console.log(date.Hour)
console.log(date.Minutes)

console.log(date.date)
date.Hours = 0
date.Minutes = 0 
console.log(date.date)

Other use include getter-only or setter-only. Data validation (before set to internal field). Alias for other field...

apple apple
  • 10,292
  • 2
  • 16
  • 36