jro jro - 3 months ago 17
Javascript Question

Point of module pattern?

The main benefit of modules (that I've heard) is that they hide private variables.

var Module = (function() {
var privateProperty = 'foo';

return {
publicProperty: '...',
publicMethod: function(args) { ... }
}
})();


But the IIFE isn't necessary to do this. If you just remove it, privateProperty would be hidden anyway. So why use the IIFE? I'm trying to understand the rationale.

EDIT:

I keep reading that privateProperty without IIFE would be part of the global scope. I think this is wrong.

If I do the following:

console.log(privateProperty); // => Reference Error: privateProperty isn't defined


I get a reference error. If I do this:

console.log(Module.privateProperty) // => undefined


I get undefined. If I do this:

var mod = new Module();
console.log(mod.privateProperty); // => undefined


I get undefined. Global scope can't access local scope in JS.

EDIT 2:

test.js

var test = (function() {
var privateProperty = 'foo';

return {
publicProperty: 'bar',
}
})();


test.js

var test1 = function() {
var privateProperty = 'foo';

return {
publicProperty: 'bar',
}
};


index.html
...

<script src="./test.js"></script>
<script src="./test1.js"></script>

<script>
console.log(test.privateProperty); // => undefined
console.log(test1.privateProperty); // => undefined
</script>


When I try the above, I don't have access to privateProperty in either case. Where is the name collision people are talking about? What is IIFE solving?

Answer

privateProperty would be hidden anyway

No, without the IIFE, privateProperty would be a property of the global scope.

Unless you're talking about a Module-loader, which (behind the scenes) basically does the same as the IIFE, it wraps the whole file in a function, a bit like:

var factory = Function("require, module, exports, global", yourFileBody );

and then calls the factory with the proper values; that's also the reason you have these mechanics; because they are injected as function arguments.

That's how these "private" properties don't pollute the global Namespace.

Edit:

I tried an example without any module.exports, but I still don't understand what problem point IIFEs are solving. I posted the example in Edit 2

test1 is a factory, not a module. Let's remove the factory and extract the generated Module, and let's make some minor changes so that this private state makes sense. (turn publicProperty into a function "speak" and actually use the private property/value/state there)

//the generted Module boils down to:
var test1 = { 
    name: "test1",
    speak() { 
        return this.name + " says " + private;
    } 
};
//and the private state also has to be kept somewhere:
var private = "Hello World";

Now let's check the Module

console.log("test1", test1);
console.log("test1.speak()", test1.speak());
console.log("test1.private", test1.private);

Fine, everything as expected
But wait, what's this?

console.log(
    "Look here:",
    private,
    this.private,
    window.private
)

Oh no, someone exposed my private property! Everybody can see it.
What might happen if some other Script also defines a private property?

var private = "Bye Folks";
var test1 = { 
    name: "test2",
    speak() { 
        return this.name + " says " + private;
    } 
};

console.log("test2", test2);
console.log("test2.speak():", test2.speak());

Fine, fine. What about ...

console.log("test1.speak():", test1.speak());

Oh No, test1 is broken. It's supposed to say "Hello World" ... I'd wish there was a way to make my private property really private so that other's don't mess around with it.

https://jsfiddle.net/crkguf6b/

@jro, do you now understand? Such factories encapsulate the private state so that it doesn't pollute the global namespace, and that it can't get messed up by some different code; while exposing only a public API. The IIFE you started your question with, is actually an anonymous factory that gets immediately invoked to create exactly one instance of this Object/Module, and then gets GC. And as shown at the top, Module loader do it the same way. They create these factories behind the scenes (or in a preprocessing step) out of your JS-files, and invoke them when necessary.

Conclusion:

It's not intrinsic to the language, that privateProperty is private. It's "artificial". Without the module loader that wraps your JS-file in a function, without the factory, and without the IIFE, privateProperty ain't private.

Maybe this will change with ES6 Modules. Maybe it will get an intrinsic part of JS, that each file is seen as a separate Module, and therefore a plain var foo; in the file won't end up in the global Namespace, but at the moment it's not.

Comments