Javascript Private Function Assistance

386 Views Asked by At

I am having a problem grasping how to make javascript functions/variables private. How would I need to edit this to make the functions/variables private in something like this.

<html>
<head>
<style>
#panel, .flip {
  font-size: 16px;
  padding: 10px;
  text-align: center;
  background-color: #4CAF50;
  color: white;
  border: solid 1px #a6d8a8;
  margin: auto;
}

#panel {
  display: none;
}
</style>
</head>
<body>

<p class="flip" onclick="myFunction()">Click to show panel</p>

<div id="panel">
  <p>This panel contains a div element, which is hidden by default (display: none).</p>
  <p>It is styled with CSS and we use JavaScript to show it (display: block).</p>
  <p>How it works: Notice that the p element with class="flip" has an onclick attribute attached to it. When the user clicks on the p element, a function called myFunction() is executed, which changes the style of the div with id="panel" from display:none (hidden) to display:block (visible).</p>
  <p>You will learn more about JavaScript in our JavaScript Tutorial.</p>
</div>

<script>
function myFunction() {
  document.getElementById("panel").style.display = "block";
}
</script>

</body>
</html>
2

There are 2 best solutions below

2
On BEST ANSWER

You can make functions "private" by wrapping them in a certain scope.

In your example that means wrapping it in an immediately invoked function expression:

(function () {
  function myFunction() {
    document.getElementById("panel").style.display = "block";
  }

  // myFunction is only available within the outer function context.
})();

This also means that the following will no longer work:

<p class="flip" onclick="myFunction()">Click to show panel</p>

myFunction is no longer publicly available, thus this will now throw an error when clicked. You could however set it through addEventListener when you are still in the same scope as myFunction:

(function () {
  function myFunction() {
    document.getElementById("panel").style.display = "block";
  }

  for(const p of document.querySelectorAll("p.flip")) {
    p.addEventListener("click", myFunction);
  }
})();
3
On

You can't.

JavaScript doesn't have any notion of access modifiers (beyond export in a module, but <script> elements with inline scripts are not JavaScript modules).

Additionally, the private modifier in OOP languages only makes sense for class/struct types, not free-functions (as all free functions are globally scoped), so the idea of private function myFunction() { ... } is meaningless.

Presently in the JavaScript ecosystem, when working with a JavaScript class (which is just syntactic sugar for a prototype declaration) it's commonplace to denote "private" properties (including functions) by using a leading underscore - but this is a convention and not a language feature, nor does it prevent a function from being called from another script:

class Foobar {

    doSomething() { // <-- "public"
        // ...
    }

    _doSomethingElse() { // <-- the leading underscore is a hint to consumers not to use this property directly, but they can still call it if they want to.
        // ...
    }
}

var f = new Foobar();
f.doSomething();
f._doSomethingElse(); // <-- nothing stops a consumer from calling this function-property.

Workaround:

Note that you can have a JavaScript object (using a class or just a POJO) with inaccessible anonymous functions provided that you can wire them up in a way that doesn't expose them - but the downside to this approach is that your own code can't directly invoke them either. This approach can be used to set-up event handlers without polluting the global namespace:

class Foobar {

    constructor() {

        (function() { // This is an IIFE

            const button = document.getElementById( 'foobar' );
            button.addEventListener( 'click', function() {
                alert("clicked!);
            } );

        })();
    }

}



In the above code, the IIFE and the click event-handler functions now cannot be invoked directly because they don't have a name and after the script runs they won't be in-scope.