Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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?

like image 559
I-Lin Kuo Avatar asked Dec 20 '14 22:12

I-Lin Kuo


People also ask

Can we call public method from private method?

An object user can use the public methods, but can't directly access private instance variables. You can make methods private too. Object users can't use private methods directly. The main reason to do this is to have internal methods that make a job easier.

How do you make a function public in JavaScript?

publicMethod = function () {...}; in a constructor function creates a public own method to every instance created by using that constructor.

Does JavaScript have public and private methods?

JS have no privacy, there is no private or public access modifier.

How do you make a function private in JavaScript?

In a JavaScript class, to declare something as “private,” which can be a method, property, or getter and setter, you have to prefix its name with the hash character “#”.


1 Answers

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"
like image 196
Oriol Avatar answered Oct 23 '22 05:10

Oriol