5 ms·
This is unrelated to JS classes, but is there a good reason for using getters/setters for some properties but not for other? I've always had trouble understandi
by epidemian 11y ago
This is unrelated to JS classes, but is there a good reason for using getters/setters for some properties but not for other? I've always had trouble understanding why some people prefer this style. For instance, given the article's Circle definition (any of them), accessing a circle's radius is different than accessing its area:
let c = new Circle(10);
c.radius // => 10; good
c.area // Nope, that returns a function.
c.area() // => 314.15... OK
c.radius() // Raises a TypeError, 10 is not a function
Presumably, the author declared the area accessor as a function instead of a getter because it does some other computation beyond returning a stored value. But a different implementation of Circle could instead store the area and compute the radius from that[1]. In that case, would we then have to change the way of accessing the circle's properties to `c.area` and `c.radius()`?
I think it'd be much better to define these properties so accessing them has a uniform syntax: either `c.area` and `c.radius`, or `c.area()` and `c.radius()`[2]. Isn't in fact the purpose of adding getters and setters to the language to allow users to define how a property is accessed?
[1]: That might make sense if it turns out that asking for a circle's area is much more common than asking for its radius, and it's done so frequently that it impacts performance; or if we prefer to have circles with nice round numbers for areas in our application and don't care too much about their radii.
[2]: Yeah, i know that there is a named principle for this. In fact i always thought that adding getters and setters was motivated by the Uniform Access Principle.
- dcherman 11y agoThis is not a hard and fast rule and I have no idea if there is any pattern, but I reserve getters for trivial properties like `firstName + " " + lastName;` If it does any non-trivial amount of work, then I always use a function. The reason for that is in codebases that I've worked on in the past, getters were heavily overused and people ended up writing code that while at a glance seemed entirely reasonable, it was actually doing an absurd amount of redundant work and was causing performance issues. I feel that requiring the `()` to invoke it as a function at least hints that "this does stuff" rather than hiding the implementation in a getter. Does that make sense?
- epidemian 11y ago> I feel that requiring the `()` to invoke it as a function at least hints that "this does stuff" rather than hiding the implementation in a getter. Does that make sense? It does. But don't you feel that making that distinction makes the code more inflexible for (arguably) little gain? To put a concrete example, let's consider an "items left" property of a TODO list: class TodoList { // ... itemsLeft() { return _.count(this.todos, todo => !todo.done) } } itemsLeft was defined as method because it's computed by iterating a list, so we deem it non-trivial. But let's say that later on we discover that a better internal structure for our TodoList is to have the unfinished and finished todo's in different lists, so our itemsLeft becomes "trivial": class TodoList { // ... itemsLeft() { return this.unfinishedTodos.length } } If using getters is preferable for trivial properties, should we then change itemsLeft to be a getter and refactor the existing code that uses it? Besides, there's the problem of having to draw a line between trivial and non-trivial code (e.g., "is string concatenation trivial?", "but it's O(n+m) on the lengths of the strings!", etc). Using always the same style for accessors (either plain-old methods or getters), regardless of how complex the underneath code is, frees us from having to make this consideration in the first place and also leaves the door open for changing an object's implementation without changing its public interface. --- I feel this perception probably depends on the language background tough. E.g., in languages like Ruby you always interact with objects via messages, so you never have this distinction of whether something is computed or not from a the caller's perspective (or at least not in a syntactic level). But it's weird that this distinction is made in JS though, as many of the classic JS APIs have computed properties that "do stuff", and quite a lot! (e.g., Node#textContent). My guess is that most likely it's a result of the language lacking user-definable getters and setters for such a long time.
- dsp1234 11y agoI don't know the author's intention specifically, but I generally use a pattern of: 1.) If a value read/write, then it's a property via getters/setters 2.) If a value is computed, then it's a function Specific to this case, area is 'generally' a computed value of other attributes of a 'shape': c.radius() = 10 'Bad, overwrote the radius function c.area = 314.15 'Ok, radius is settable from just area. But doesn't match the area signature of other "shape"-like objects. let rect = new Rectangle(10, 30); c.area = 200 'Bad, which do we change, width or height?
- jshen 11y agoWhen you are unsure, make it a function. I have frequently regretted making things fields, and I have never regretted making it a function.