Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JavaScript: Is it worth making variables private and defining getters/setters?

I'd like to start by saying that I understand that JavaScript is a Classless language. My background is in Java, C++, and Objective-C which are all classic OOP languages that support Classes.

I'm expanding into Web Development and have been experimenting with JavaScript and learning its Patterns. Right now I'm working with the Constructor Pattern that simulates Classes with in JavaScript.

So this is my "practice" class:

function Car( model, year, miles ) {
    this.model = model;
    this.year = year;
    this.miles = miles;

    var privateVarTest = 10;

    this.getPrivateVarTest = function() {
        return privateVarTest;
    }

    this.setPrivateVarTest = function( value ) {
        privateVarTest = value;
    }
}

Car.prototype.toString = function() {
    return this.model + " is a " + this.year + " model and has " + 
           this.miles + " miles.";
}

var myCar = new Car( "Ford Focus", "2006", "65,000" );
document.getElementById('notepad').innerHTML += '</br> Testing </br>';
document.getElementById('notepad').innerHTML += myCar.toString() + '</br>';
document.getElementById('notepad').innerHTML += myCar.model + '</br>';
document.getElementById('notepad').innerHTML += myCar.getPrivateVarTest() + '</br>';
myCar.setPrivateVarTest( 20 );
document.getElementById('notepad').innerHTML += myCar.getPrivateVarTest() + '</br>';

Now I like using the prototype way of defining functions, as it doesn't instantiate a new version of the function for each Car Object created. However, in classic OOP languages we make our variables private and create public functions/methods to set and get these variables as needed.

JavaScript being Classless there is no private or public key word for this use, so I thought I'd experiment with a method of "faking" a private variable, and that's when found that using var instead of this essential makes it unaccessible out side of the constructor, but I was able to define getters and setters that would allow me to.

Now finaly to my question, sorry about the long wind up. For Best Practices from experinced JavaScript programmers, would you make all variables private to follow the standards of other OOP languages, and set getters and setter (which can not be prototyped forcing a creation for each Object), or avoid them as much as possible since the this keyword basicly lets you get and set all you want, and ONLY use private for hard coding some internal data needed for the class?

Thank you for taking the time to read this and providing to the discussion, I'm really just trying to get a feel for the standards that are used as Best Practices by experinced Web Developers.

like image 724
defaultNINJA Avatar asked Dec 05 '12 14:12

defaultNINJA


1 Answers

General OOP

I'm in the camp that getters and setters are largely completely pointless and silly regardless of what language you're writing code in.

For the most part, exposed properties should be rare since any property of an object should typically be within the object's domain so only the object should actually change its own internals as a side-effect of other actions, not because some other object directly told it to change something. There are exceptions I'm sure (there always are) but I can't remember the last time I needed to make one.

Furthermore, when properties are exposed, the only reason to expose with a method is because you either can't just expose the property due to language constraints (Java) or because some validation or notification has to happen when you change that property. Just tacking on methods Java-bean-style that do nothing more than actually alter or return properties does nothing to preserve encapsulation. You might as well just make the property public if you can.

But the real problem with wanting to get/set everything willy-nilly from all over the place is that you've basically just written chained procedural code and called it OOP. You still have a long winding series of things that can only be reasoned about in terms of one happening after the other. With OOP, the idea is to avoid that long winding spaghetti chain so you can view your architecture more from the perspective of larger constructs that own specific domains interacting with each other at key points. Without that, you're perhaps reducing the spaghetti a touch by at least categorizing your functions under namespaces so it's easier to know where to look for stuff but you're not really leveraging the key wins that OOP can provide your architecture.

The real value of private or in JS's case local constructor/factory-closur vars is signalling intent. If it's exposed, something external really should be changing it. If it isn't, then you've made it clear that the var is only the object's business.

JS OOP

My advice is to forget class-emulation in JS. It's completely unnecessary. Prototypes are elegant and easy once you understand them. Think of a constructor's prototype property as a kind of a backup object. If you call a method on an instance that it doesn't have, the next step is to check the instance's constructor's prototype object property. If that object doesn't have it, then its constructor's prototype object gets checked and so on until you finally reach the core Object constructor's prototype.

It's because of that lookup process that you can add new methods to a constructor on the fly and have all instances "inherit" it after they've been built but it's not really inheritance so much as a fallback process.

Inheritance in JS is stupid-easy. That doesn't mean you should do a ton of it though. Long chains of cascading inheritance is regarded as an anti-pattern in any language for good reason and due to the way the callback process works, it can also really kill perf if you're hammering on the call object through like 18 levels of prototypes for every little thing in JS. I would say prefer composite objects to inheritance and when inheritances seems like a wiser option, check yourself any time you're tempted to inherit through more than 2-3 prototype links in the chain.

Oh, and one JS gotcha to look out for on local instance vars in the constructors as private properties: that's just JS's closure rules within a function scope context in action really. Methods declared in the prototype or outside of the constructor function can't access those internal vars. Constructor functions invoked with the new keyword change the rules of what 'this' accesses and they leave an instance behind but are otherwise executed JS functions in every other way.

Other flavors of crazy but also crazy-powerful worth understanding in JS OOP are the apply, call, and now bind methods. I tend to see these more as things you'd want in a factory but they are very powerful.

Once you've mastered JS OOP, start understanding JS from a functional perspective and you'll discover it has a really powerful 1-2 punch combo going on. We can do just about anything very easily and with a minimum of code in JS. The design tradeoff is performance (which modern JIT compilers are handling surprisingly well) and that it gives you plenty of rope to hang yourself with. I prefer the rope. The self-lynching is no fun but that's part of the learning/developing better instincts process which happens much faster as a result and leads to more maintainable code in the long haul. Whereas Java basically forces OOP implementation but due to being overly protectionist in regards to devs doing dumb things to themselves, results in community wide adoption of practices that run completely counter to the whole point of OOP.

The short version:

  • Stop getting/setting a lot if you do, regardless of language. It drastically reduces the win factor of implementing OOP in the first place.
  • Prototypes are really simple, elegant, and powerful. Tinker with them. Learn them. But be warned. Classes might start to feel archaic, clumsy, and overwrought in comparison (although to be fair, completely necessary in non-interpreted languages).
  • To make JS work well for you, self-learn the crap out of whatever aspect of it you happen to be dealing with. The rewards in terms of raw elegant linguistic power are more than worth the time spent. JS is closer to Scheme than the languages you listed being familiar with so it's weird but it's not being weird arbitrarily or without design principles in mind and JS's dominating success in web UI is no accident, regardless of what people telling everybody we're "stuck with it" would have you believe.
  • Full disclosure: I don't love Java.

Update:

The es6 class keyword changes virtually nothing about OOP in JS. It's 100% syntax-sugar. IMO, the use of the word "class" isn't doing newcomers any favors but there are advantages/disadvantages to all three styles of object constructor/creation and object instantiation in JS and they're all worth knowing/understanding. Those three approaches are functions as constructors, Object.create, and now the class keyword.

like image 148
Erik Reppen Avatar answered Oct 08 '22 07:10

Erik Reppen