Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JavaScript - Accessing Private Instance Variable from Prototype Method

Tags:

javascript

[Yes, I have read several answers to similar questions, but didn't really get the answer I'm looking for, so I'm going to ask my question anyway.]

In the code below, how can I place the methods setSecret and tellSecret in Secret's prototype while still maintaining access to the private instance variable _secret, and also producing the same output?

I tried this (see jsbin) which placed the methods in the prototype, but changed the output.

function Secret() {

    // ===== private =====

    var _secret;

    // ===== public =====

    this.setSecret = function (secret) {
        _secret = secret;
    };

    this.tellSecret = function () {
        console.log(_secret);
    };
}

var secretA = new Secret();
var secretB = new Secret();

secretA.setSecret("AAA");
secretB.setSecret("BBB");

setTimeout(function () {
    console.log("Secret A");
    secretA.tellSecret();

    console.log("Secret B");
    secretB.tellSecret();
}, 1000);


// ===== output =====

Secret A
AAA
Secret B
BBB
like image 261
RBR Avatar asked Mar 21 '23 17:03

RBR


2 Answers

Simply put, you shouldn't use private variables with prototype methods. Trying to mix the two requires awful workarounds, and there are better alternatives.
Here's an explanation why. (This is an excerpt from a similar answer: https://stackoverflow.com/a/21522742/272072)

Prototypal Methods

In JavaScript, prototype methods allows multiple instances to share a prototype method, rather than each instance having its own method.
The drawback is that this is the only thing that's different each time the prototype method is called.
Therefore, any "private" fields must be accessible through this, which means they must also be publicly accessible. So, the best we can do is to stick to naming conventions for _private fields.

Mixing with Private Variables

When you use a closure to create a private variable, you cannot access it from a prototypal method unless it's exposed through the this variable. Most solutions, therefore, just expose the variable through method, which means that you're exposing it publicly one way or another.

Just use conventions for _private fields

So, I think using _private fields makes the most sense, even though they're still public. It makes debugging easier, provides transparency, could improve performance, and so that's what I usually use.
Stick to conventions for _private fields and everything goes great.
And I just don't understand why JS developers try SO hard to make fields truly private.

like image 55
Scott Rippey Avatar answered Mar 23 '23 08:03

Scott Rippey


this one is related to Alon's answer, but by implementing a WeakMap, doesn't reveal an identifying index and won't accumulate un-used objects. While a better solution in terms of efficiency, it's not as good an answer in terms of compatibility. WeakMaps are supported in FireFox and Chrome and Node.JS, so i feel they are worth mentioning.

see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap for more info.

var Secret = (function(){

   var secrets=new WeakMap();

  function Secret() {

    // ===== private =====

    secrets.set(this, null);

   // ===== public =====

   }

    Secret.prototype.setSecret = function (secret) {
       secrets.set(this, secret);
    };

    Secret.prototype.tellSecret = function () {
        console.log(secrets.get(this));
    };


   return Secret;

}());


var secretA = new Secret();
var secretB = new Secret();

secretA.setSecret("AAA");
secretB.setSecret("BBB");

setTimeout(function () {
    console.log("Secret A", secretA  );
    secretA.tellSecret();

    console.log("Secret B", secretB );
    secretB.tellSecret();
}, 1000);
like image 22
dandavis Avatar answered Mar 23 '23 08:03

dandavis