IFFE often confused to read and personally, I have no idea why they have become so popular. I think the code should be easy to read and concise. Trying to mimic language behavior that is not part of the language specification is often a very stupid idea.
For example, JavaScript does not support multiple inheritance, polymorphism, or many other interesting paradigms. Therefore, many times we see people trying to create these crazy ways of sorting “kind”, having polymorphism or private members, etc. In JS. I think this is a mistake.
I am currently working as a kind of hobby project in the high-performance JS data structures library (I try to beat Google's closure and many others). Based on the background of C ++ and Java, I always like material classes, and I love inheritance, etc. Etc. Let me share some code snippets with you. At first, I thought I was smart because I wrote things like this:
function __namespace(n, v) { return {"meta":{"namespace":n,"version":v}}; } var FJSL = FJSL == undefined ? new __namespace("Fast JavaScript Library", 0.1) : FJSL; __using = function(parent, child) { clazz = new child(); clazz.super = new parent(); if (clazz.super == undefined) return clazz; for (a in clazz.super) { for (b in clazz) { if (a == "constructor" || b == "constructor") continue; if (clazz[b] === clazz.super[a]) continue; if (a == b && typeof clazz[b] != typeof clazz.super[a]) throw "Typesafety breached on '" + a + "' while trying to resolve polymorphic properties."; if (a == b && typeof clazz[b] == typeof clazz.super[a]) { clazz["_"+a] = clazz.super[a]; } else if (clazz[a] == undefined) { clazz[a] = clazz.super[a]; } } } return clazz; };
And I used it like this (in the simple queue example):
FJSL.Array = function() { this.data = []; this.contains = function(idx, element) { for (var i = idx; i < this.data.length; i++) { if (this.data[i] === element) return i; } return -1; } this.size = function() { return this.data.length; } } FJSL.Queue = function() { return __using(FJSL.Array, function() { this.head = 0; this.tail = 0; this.enqueue = function(element) { this.data[this.tail++] = element; }; this.dequeue = function() { if (this.tail == this.head) return undefined; return this.data[this.head++]; }; this.peek = function() { return this.data[this.head]; }; this.size = function() { return this.tail - this.head; }; this.contains = function(element) { return this._contains(this.head, element); }; } )};
You will notice how I am in far-fetched inheritance (Array is used in the queue, har har, I'm smart). However, this is an absolutely insane expression for a) to read and b) to understand . I could not help but recall this meme:

Let me show you functionally equivalent code if I don’t try to make all this fancy before and after processing:
FJSL.Queue = function(opts) { this.options = opts; this.head = 0; this.tail = 0; this.data = []; }; FJSL.Queue.prototype = { add : function(element) { this.data[this.tail++] = element; }, enqueue : function(element) { this.data[this.tail++] = element; }, dequeue : function() { if (this.tail == this.head) { return undefined; } return this.data[this.head++]; }, peek : function() { return this.data[this.head]; }, size : function() { return this.tail - this.head; }, contains : function(element) {
Obviously, I will have to duplicate the prototype constructor for any other structures that can use the array, but what I'm trying to do is much clearer, even a novice JS programmer can tell what is happening. Not only that, but if people want to change the code, they know exactly where to go and what to do.
My suggestion doesn't go crazy, trying to get JS to behave like C ++ or Java. It will never be. And yes, you can fake the inheritance of both private / public / protected members, but JS was never meant to be. I think the consequences of having such bloated code (which tries to mimic non-standard behavior) greatly burden high-performance web applications, etc.
In short, I suggest using an object literal:
var public_statics = { public_func: function () {}, public_var: "hello" }
Easy to understand, easy to modify and easy to expand. If your system is fragile enough to crash and burn, if someone accidentally changes a private variable, you just need to document it.