JavaScript Encapsulation
On this blog I’ve brought up encapsulation a few times, with languages like Java and C, and the lack of it in Groovy/Grails, as well as the lack of enforced encapsulation in Python.
Refresh on Groovy & Python
Honestly, Groovy and Grails are the worst offenders when it comes to encapsulation. Groovy pretends to allow for private methods (taking the private keyword), but in reality those “private” methods are actually “public.” There’s ways around it, but the fact the Java syntax is usable and yet at the same time not working, has lead many people to the erroneous conclusion that Groovy (like Java) has enforced encapsulation.
Groovists will say that Groovy is creating Java getters and setters under the hood and that is why you can reference/call a private method from outside the class. Which, in my way of thinking, is rather like saying you put locks on my house, but gave everyone in the neighborhood a copy of the key.
Python on the other hand is at least honest to the developer. In the Python world private methods and fields are not enforced. Instead privacy is annotated with double underscores __my_private_method().
Double underscores in Python can be prepending, appending or both, signifying different aspects of a method or field. What all of these double underscores have in common is a visual warning for the developer to really think, “should I be calling this directly?” You can still call it, and every IDE will reference the __methods of an instantiated or inherited class – it is up to the developer if they choose to call it.
JavaScript and Privacy
When I started reading JavaScript files, I would get tripped up over embedded functions within functions. Now I understand the reasoning behind it… it’s a useful design pattern to maintain encapsulation.
Module Pattern
This is a design pattern within JavaScript… and it can be written different ways depending if you’re using ES5 or ES6. In ES6 I can use a module design pattern like so:
%MINIFYHTML0b649704ea01e4997698aec32847f18e23%function example(x) {
const privateField = "hello"
function outputParam() {
console.log(`${privateField}, ${x}`)
}
return outputParam()
}
example("Brian"); // >> hello, Brian
example.outputParam(); // TypeError
Code language: JavaScript (javascript)
In ES5 you could write the module pattern as a function expression…
var exampleES5 = function(x) {
var privateField = "hello";
function outputParam() {
console.log(`${privateField}, ${x}`)
}
return outputParam()
};
exampleES5("Warner"); // I can pass the param to the variable, as though the variable is a function name...
Code language: JavaScript (javascript)
This is a JavaScript way of getting encapsulation.
In the code examples I wrote above, I can call this public function (example or expressYourself) and get a result, but I can not directly access the functions within (i.e. outputParam() is not public or callable.) To directly call the embedded function would result in a TypeError (TypeError: example.outputParam is not a function.)
Likewise the field/variable definition inside the function is also private… to attempt to access it directly will result in an “undefined” result.
/**
* Created by bwarner on 6/5/19.
*/
function example(x) {
const privateField = "hello"
function outputParam() {
console.log(`${privateField}, ${x}`)
}
return outputParam()
}
example("Brian");
console.log(example.privateField) // undefined
Code language: JavaScript (javascript)
IIFE
Another way to encapsulate data/fields and logic from being directly callable is through the ES6 IIFE (“ifee”) construct.
IIFE stands for Immediately Invoked Function Expression – and it does just that… it runs the code within it, and isn’t run again (unless the app/file is called again.)
(function(){
console.log('IIFE Ran...')
})(); // Notice the appended ()
Code language: JavaScript (javascript)
Above is an IIFE. As the JavaScript file is read, this will execute the code within. This isn’t something we reference to call again, it runs once and it’s done.
The appended parens allow this to be invoked. Without it, the method will not run. It looks weird to me (and perhaps to you) but that is required to allow this to execute. Bellow is another example:
(function(name){
console.log(`Hello ${name}`)
})('Barker Finch'); // Notice the invoking parenthesis pass in the value for hte param "name."
Code language: JavaScript (javascript)
Above I have an IIFE that has data passed as a parameter through the appended (), into the IIFE function parameter “name.” When the above code is run, it will output “Hello Barker Finch.”
As this is anonymous, it be definition can not be called to invoked to get any functions or values within.