Being a longtime classical inheritance OO programmer I'm very comfortable with the use of constructors and creating objects that accept required parameters as constructor arguments. For example, an object that sends alerts related to an order might look like:
var orderNotifier = function(orderId, notifier, recipients)
{
this.notifyApproved = function()
{
// use the notifier object passed as ctor param to send notifications
// related to orderId to recipients
}
this.notifySomeOtherEvent = function() { // use the ctor params again }
}
// then use it like
var on = new orderNotifier(12345, new BasicNotifier(), someArrayOfEmails);
on.notifyApproved();
So that is a contrived example but exemplifies (IMHO) the value of a parameterized constructor. Specifically:
Appreciating that the constructor creation pattern in JavaScript doesn't very well support information hiding, I was attracted to the Immediately Invoked Function Expressions (IIFE) pattern with its closures and stronger access control. Now I've run up against the fact that I can't pass object construction parameters using the IIFE pattern, or at least I don't understand how I can.
I know you can pass parameters to the anonymous function like so:
(function(param){})(someVar);
But that isn't the same as explicitly creating a new object and passing parameters to the constructor. By the very nature of the IIFE pattern I don't necessarily have the data to pass to the object yet.
My IIFE version of the above would like like:
var orderNotifier = (function()
{
var privateHelperMethod = function() { return 'blahblahblah'; };
return {
notifyApproved: function(orderId, notifier, recipient)
{
// use the notifier object passed as ctor param to send notifications
// related to orderId to recipients
var msg = privateHelperMethod()
},
notifySomeOtherEvent: function(orderId, notifier, recipient)
{
// use the ctor params again
var msg = privateHelperMethod();
}
};
}}();
So I must ask experienced, progressive JavaScript pros: Using standard ECMAScript 5 language features, what is the best practice (or maybe even just a common practice) of creating an object with the IIFE pattern and providing object state in a single operation? (Other than exposing a setState() or similar method.) In other words: How can I have my cake and eat it too?
Your IIFE constructor pattern creates a singleton (only one object, ever). It doesn't expose a constructor so it can't be used to create more objects. As such, you can pass any info you want into the singleton via the arguments to the IIFE or just code those arguments right into the implementation.
This is why I wanted you to show us what design pattern you were actually talking about because this particular pattern isn't a general purpose constructor and isn't designed to create multiple objects. It creates a singleton object. That's what it is best for. If the returned object exposed functions that could be used as constructors, then they could take arguments just like other constructors.
I don't see a problem here. I don't see that there is an issue to be solved.
Per your comments, if you want to pass non-static data into the IIFE, then you either have to locate the IIFE after the creation of the non-static data (so that data is available before the IIFE runs), select a different design pattern (e.g. use a traditional constructor) or use an IIFE that creates a constructor (which you can call later), rather than an object.
For example, here's an IIFE that creates a constructor:
var OrderNotifier = (function()
{
// private stuff would go here
// shared by all instances
return function(/* constructor args go here */) {
// per-instance private vars here
return {
notifyApproved: function(orderId, notifier, recipient)
{
// use the notifier object passed as ctor param to send notifications
// related to orderId to recipients
},
notifySomeOtherEvent: function(orderId, notifier, recipient)
{
// use the ctor params again
}
};
}
}}();
// sometime later in your code
var notifier = new OrderNotifier(/* args here */);
The two options you provide essentially have two different uses (and also, the first method is best used with a prototype).
Prototypal object creation
function OrderNotifier(orderId, notifier, recipients)
{
this.orderId = orderId;
this.notifier = notifier;
this.recipients = recipients;
}
OrderNotifier.prototype.notifyApproved = function()
{
// use the notifier object passed as ctor param to send notifications
// in the form of this.notifier
// related to orderId to recipients
// accessed through this.orderId and this.recipients
}
OrderNotifier.prototype.notifySomeOtherEvent = function() {
// use the ctor params again
}
and used
var on = new OrderNotifier(12345, new BasicNotifier(), someArrayOfEmails);
on.notifyApproved();
This approach is basically for creating objects which are going to have their own unique values but share the same configuration and prototype (behaviors). Using an IIFE (Immediately Invoked Function Expression) is really for a different operation. It can be useful for protecting the global namespace from variable pollution. If there is some work that needs to be done, but the variables shouldn't stick around, then that is where IIFE's shine. In addition, they are very useful for closing over values. However, an IIFE generally runs once and is gone, and as a result it does not need instantiation. Some variables can be passed in if they are to be used or closed over in the function, but in general, the entire IIFE is gone once executed (although a common pattern is to have it modify global state for a library - jQuery does this).
Edit
In your late edit, you create a closure for "private stuff would go here", but it is too abstract of an example to really highlight any benefit. Private stuff could go there, but really, it would be static and could simply have been moved into notifyApproved or notifySomeOtherEvent. It is really a misuse of the IIFE because this could simply be
var orderNotifier = {
notifyApproved: function(orderId,notifier,recipient){
//store "private" information here
},
notifySomeOtherEvent : function(orderId,notifier,recipient){
//store "private" information here
}
};
It would be possible for you to go back, and edit in the IIFE to accept values on load to be passed in and then stored as "private", but the only benefit of doing that would be to close over the values at the time that the script is first executing.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With