How do I call a public function from within a private function in the JavaScript Module Pattern
Asked Answered
R

2

8

How do I call a public function from within a private function in the JavaScript Module Pattern?

For example, in the following code,

var myModule = (function() {
    var private1 = function(){
        // How to call public1() here?
        // this.public1() won't work
    }

    return {
        public1: function(){ /* do something */}
    }
})();

This question has been asked twice before, with a different accepted answer for each.

  1. Save a reference to the return object before returning it, and then use that reference to access the public method. See answer.
  2. Save a reference to the public method in the closure, and use that to access the public method. See answer.

While these solutions work, they are unsatisfactory from an OOP point of view. To illustrate what I mean, let's take a concrete implementation of a snowman with each of these solutions and compare them with a simple object literal.

Snowman 1: Save reference to return object

var snowman1 = (function(){
  var _sayHello = function(){
    console.log("Hello, my name is " + public.name());
  };

  var public = {
    name: function(){ return "Olaf"},
    greet: function(){
      _sayHello();
    }
  };
  return public;
})()

Snowman 2: Save reference to public function

var snowman2 = (function(){
  var _sayHello = function(){
    console.log("Hello, my name is " + name());
  };
  var name = function(){ return "Olaf"};

  var public = {
    name: name,
    greet: function(){
      _sayHello();
    }
  };
  return public;
})()

Snowman 3: object literal

var snowman3 = {
    name: function(){ return "Olaf"},
    greet: function(){
      console.log("Hello, my name is " + this.name());
    }
}

We can see that the three are identical in functionality and have the exact same public methods.

If we run a test of simple overriding, however

var snowman = // snowman1, snowman2, or snowman3
snowman.name = function(){ return "Frosty";}
snowman.greet(); // Expecting "Hello, my name is Frosty"
                 // but snowman2 says "Hello, my name is Olaf"

we see that #2 fails.

If we run a test of prototype overriding,

var snowman = {};
snowman.__proto__ = // snowman1, snowman2, or snowman3
snowman.name = function(){ return "Frosty";}
snowman.greet(); // Expecting "Hello, my name is Frosty"
                 // but #1 and #2 both reply "Hello, my name is Olaf"

we see that both #1 and #2 fail.

This is a really ugly situation. Just because I've chosen to refactor my code in one way or another, the user of the returned object has to look carefully at how I've implemented everything to figure out if he/she can override my object's methods and expect it to work! While opinions differ here, my own opinion is that the correct override behavior is that of the simple object literal.

So, this is the real question:

Is there a way to call a public method from a private one so that the resulting object acts like an object literal with respect to override behavior?

Rangy answered 20/12, 2014 at 22:44 Comment(6)
An important note in the pitfalls you discuss: my understanding of the module pattern is that it really is an alternative to JS's prototype/OOP polymophism approach. As such, I think it makes sense for you to avoid using the two together-- I don't see that as a pitfall per se.Bellows
@EyasSH—it can be used that way, however I think it's more an artefact that can be used for a kind of inheritance rather than a deliberate feature. It seems to be used primarily for emulating private members (or not exposing things that others shouldn't play with) and also to improve performance.Bangalore
@RobG, right, but you can have private functions declared in a closure while sticking with the prototype pattern. In that case, using this is fine. My point is, if you are using the module pattern, I think looking at module<->prototype interaction weirdness is not a real reason not to use an approach. It might be a reason to stick to prototypes altogether if you think whoever is using your code will do that..Bellows
I think you should add () at the end of snowman1, snowman2 and snowman3, or otherwise they will be functions. And probably in the prototype overriding you should customize snowman.name, or otherwise I don't see how it should output "Frosty".Sharonsharona
Thanks, @Oriol, I've made the correctionsRangy
I don't understand why you'd want to call a public method from a private one in the first place. Seems like an XY problem to me.Maigre
S
2

You can use this to get the object your privileged method greet was called on.

Then, you can pass that value to your private method _sayHello, e.g. using call, apply, or as an argument:

var snowman4 = (function() {
    var _sayHello = function() {
        console.log("Hello, my name is " + this.name);
    };
    return {
        name: "Olaf",
        greet: function() {
            _sayHello.call(this);
        }
    };
})();

Now you can do

var snowman = Object.create(snowman4);
snowman.greet(); // "Hello, my name is Olaf"
snowman.name = "Frosty";
snowman.greet(); // "Hello, my name is Frosty"

And also

snowman4.greet(); // "Hello, my name is Olaf"
snowman4.name = "Frosty";
snowman4.greet(); // "Hello, my name is Frosty"
Sharonsharona answered 20/12, 2014 at 23:38 Comment(3)
This answer doesn't seem to address the question of how to call public functions from within private functions in the Module Pattern -- in particular, how should _sayHello() call name(). Wrapping the object literal in an IIFE doesn't make greet() privileged either.Rangy
@I-LinKuo Yes, it's privileged. It can access private data declared inside the self executing function.Sharonsharona
Yes, was privileged: it could access private data declared inside the self executing function. I did it with a privileged method instead of private one to simplify, but it's true it didn't answer the question directly, so I fixed it.Sharonsharona
F
1

With module pattern, you hide all the innates of an object in local variables/functions, and usually employ those in your public functions. Each time a new object is created with a module pattern, a new set of exposed functions - with their own scoped state - is created as well.

With prototype pattern, you have the same set of methods available for all objects of some type. What changes for these methods is this object - in other words, that's their state. But this is never hidden.

Needless to say, it's tough to mix those. One possible way is extracting the methods used by privates into a prototype of the module's resulting object with Object.create. For example:

var guardian = function() {
    var proto = {
        greet: function () {
            console.log('I am ' + this.name());
        },
        name: function() {
            return 'Groot';
        }
    };
    var public = Object.create(proto);
    public.argue = function() {
        privateGreeting();
    };

    var privateGreeting = public.greet.bind(public);
    return public;
};

var guardian1 = guardian();
guardian1.argue(); // I am Groot
var guardian2 = guardian();
guardian2.name = function() {
  return 'Rocket';
};
guardian2.argue(); // I am Rocket
var guardian3 = guardian();
guardian3.__proto__.name = function() {
  return 'Star-Lord';
};
guardian3.argue(); // I am Star-Lord
Father answered 20/12, 2014 at 23:39 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.