1

I'm asked to make a Javascript Module that acts as counter of browser events and is not possible to modify the actual counted events (Private method).

var CountingLogger = (function() {

var secret ={};
var uid = 0;
function CountingLogger(events) {
    secret[this.id = uid++] = {};
    // Store private stuff in the secret container
    // instead of on `this`.
    secret[this.id].events = events || 0;
}

CountingLogger.prototype.log = function (eventName) {
    alert(secret[this.id].events[eventName]);
    if (typeof secret[this.id].events[eventName] === 'undefined') {
        secret[this.id].events[eventName] = 0;
    }
    secret[this.id].events[eventName]++;
    var count = secret[this.id].events[eventName];
    $('#logContents').append('<li>' + eventName + ': ' +  count + '</li>');
};

return CountingLogger;
}());

in the main.js I define: var logger = new CountingLogger(); then call logger.log('EventName');

And should appear as callback a counter "EventName" + Counter, so Eventname: 1, SecondEvent: 4... But in the log always shows 'WhateverEvent' undefined. Can anybody please have an idea how this can be solved? Regards

5
  • 1
    seems here secret[this.id].events = events || 0; you want secret[this.id].events = events || {}; Commented Jun 30, 2015 at 14:01
  • 1
    .events is used as if it was an object, but seems to be a number. Commented Jun 30, 2015 at 14:01
  • 2
    Those secret objects pose a severe memory leak… I would advise against using such a pattern. Commented Jun 30, 2015 at 14:02
  • @Bergi Just curious, the alternative would be something along the lines of secret[this.event2index[eventName]] where secret is an array and event2index an object that gives each eventName a specific index in the array? Edit: actually nvm. Commented Jun 30, 2015 at 14:08
  • Thanks @Grundy this actually fixed the counting issue! Is weird but I don't see many ways to really hide the events variable. Thanks a lot! Commented Jun 30, 2015 at 14:10

2 Answers 2

5

You don't want secrets at the level you have it. As Bergi said, it's a memory leak.

(Note: When I say "truly private" below, remember that nothing is truly private if someone's using a debugger. "Truly private" below just makes it's impossible for anyone to write code that, using a reference to the object, gets or modifies the private data.)

ES7 will have truly private properties. Until then, you basically have three options:

  1. Truly private data, held via closures over the call to the constructor

  2. "Keep your hands off" properties via a property naming convention

  3. Very-hard-to-use properties that, while not private, are very hard to write code against

Truly Private Data

If you want truly private information on a per-instance basis, the standard way to do that is using variables in the constructor, and define methods that need that private data within the constructor:

function Foo() {
    var secret = Math.floor(Math.random() * 100);
    this.iWillTellYouASecret = function() {
        console.log("The secret is " + secret);
    };
}
Foo.prototype.iDoNotKnowTheSecret = function() {
    console.log("I don't know the secret");
};

Yes, that means a new iWillTellYouASecret function is created for every instance, but it (and the secret) are also reclaimed when that instance is removed from memory, and a decent JavaScript engine can reuse the function's code across those function objects.

"Keep your hands off" Properties

But most of the time, you don't need truly private information, just information saying "keep your hands off me." The convention for that in JavaScript is properties beginning with an _. Yes, that means that code with access to instances can use or change that property's value, but that's actually true in most languages that have "truly private" properties as well, such as Java, via reflection.

Very-Hard-to-Use Properties

If you want to make life harder on people trying to use your private data, you can use this trick from an old blog article of mine *(it was written back when ES6 was going to have true privacy via Name objects; since then, Name has turned into Symbol and it isn't used for privacy anymore):

First, you create a Name psuedo-class:

var Name = function() {
    var used = {};

    function Name() {
        var length, str;

        do {
            length = 5 + Math.floor(Math.random() * 10);
            str = "_";
            while (length--) {
                str += String.fromCharCode(32 + Math.floor(95 * Math.random()));
            }
        }
        while (used[str]);
        used[str] = true;
        return new String(str); // Since this is called via `new`, we have to return an object to override the default
    }

    return Name;
}();

Then you use this pattern for your private instance properties:

// Nearly-private properties
var Foo = (function() {
    // Create a random string as our private property key
    var nifty = new Name();

    // Our constructor    
    function Foo() {
        // We can just assign here as normal
        this[nifty] = 42;
    }

    // ***On ES5, make the property non-enumerable
    // (that's the default for properties created with
    // Object.defineProperty)
    if (Object.defineProperty) { // Only needed for ES3-compatibility
        Object.defineProperty(Foo.prototype, nifty, {
            writable: true
        });
    }
    // ***End change

    // Methods shared by all Foo instances
    Foo.prototype.method1 = function() {
        // This method has access to `nifty`, because it
        // closes over the private key
        console.log("Truly private nifty info: " + this[nifty]);
    };
    Foo.prototype.method2 = function() {
        // Also has access, for the same reason
        console.log("Truly private nifty info: " + this[nifty]);
    };

    return Foo;
})();

var f = new Foo();
f.method1(); // Can use nifty!
f.method2(); // Can too! :-)
// Both `method1` and `method2` are *reused* by all `Foo` objects

Now, the name of your private property is different every time your code runs.

This still isn't private. It's just harder to find.

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks for the reply. I made this implementation just for exercise because of a test, so makes more sense say that truly private is not really possible and is recommended using underscore for properties. Thanks
0

Private methods in javascript should only be used for encapsulation purposes.

It's impossible to prevent someone from manipulating any method in javascript.

A user can simply place a debug point on your private method and start to manipulate your private logic.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.