DanRedux
DanRedux

Reputation: 9359

How to create shared, privileged, public methods?

Some methods of one of my classes right now are public, but can access private variables (they are privileged). This is because they are created in the class constructor, so their closure has access to the object closure.

What I would like to avoid, though, is the memory and performance overhead of creating new privileged methods every time. So I want to share them.

Is there any way to put privileged methods into a prototype?

Example was requested:

function Person(age) {       // age is private
    this.grow = function() { // grow is now public, but can access private "age"
        age += 1;
    }
}

dan = new Person(10);
dan.grow();
dan.age;                    // undefined

This works, I have a public method "grow" that can access the private variable "age", but grow has to be recreated for each object.

The more performant way is:

function Person(age) { // age is private
    this.age = age;    // but this.age is public
}
Person.prototype.grow = function() {
    this.age += 1;
}

dan = new Person(10);
dan.grow();
dan.age;              // 11

This shares the "grow" method, but now age is public.

Upvotes: 2

Views: 181

Answers (3)

Aadit M Shah
Aadit M Shah

Reputation: 74234

Yes, it is indeed possible. However it requires a little bit of trickery:

var createTree = require("functional-red-black-tree");

var Person = (function () {
    var tree = createTree(), id = 0;

    return function (age) {
        tree.insert(id, {
            age: age
        });

        this.id = id++;

        this.grow = grow;

        this.destroy = destroy;
    };

    function grow() {
        tree.get(this.id).age++;
    }

    function destroy() {
        tree.remove(this.id);
    }
}());

We use functional red black trees to efficiently insert, remove and get the private properties of an object in O(log n) time. Hence for example say you create 2251799813685248 instances of Person at a time. It will still only require 51 operations to insert, remove and get objects from the tree.

You can use it as follows:

var dan = new Person(10);
dan.grow();
dan.age;                  // undefined
dan.destroy();            // frees shared memory

However I wouldn't recommend this approach because:

  1. It unnecessarily complicates things.
  2. If you forget to call destroy then you will waste a lot of memory.
  3. Every "privileged" function has an additional overhead.
  4. Changes made to the secret object are not reflected on the private variables.

Instead I would recommend that you just use public properties for everything. There's really no good reason to use private properties at all. What are you scared of?


Edit: If alll you want is to prevent your private properties from being printed out via console.log then you can make them non-enumerable:

function Person(age) {
    Object.defineProperty(this, "age", {
        enumerable: false,
        writable: true,
        value: age
    });
}

Person.prototype.grow = function () {
    this.age++;
};

Now the age property (although public) will not appear in for in loops or via console.log. In short you get the best of both worlds.

As I said in the comments, there's absolutely no need to use the shared privileged method hack. Simply make all your variables public and non-enumerable.

In addition prefix them with an underscore to indicate that they should not be tampered with. All good JavaScript programmers use this convention.

Upvotes: 1

Xotic750
Xotic750

Reputation: 23492

You could do something like this (without ES6), though I don't consider it a good solution.

var Person = (function () {
    var id = 0,
        data = {},
        key = Math.random();

    function Person(age) {
        var thisId = id;

        data[id] = age;
        id += 1;

        this.getId = function(check) {
            if (check !== key) {
                return undefined;
            }

            return thisId;
        };
    }

    Person.prototype.grow = function () {
        var thisId = this.getId(key);

        data[thisId] += 1;
        console.log(data[thisId]);

        return this;
    };

    Person.prototype.destroy = function () {
        var thisId = this.getId(key);

        data[thisId] = null;
        delete data[thisId];
    };

    return Person;
}());

var dan = new Person(10);

dan.grow();
console.log(dan.age); // undefined
console.log(dan.getId()); // undefined

on jsFiddle

Added by @DanRedux:

function Person(age) {
 this.private = {age:age}; }
Person.prototype.grow = function() {
  this.private.age += 1; }

dan = new Person(10);
dan.grow();
dan.age; // undefined
dan.private.age; // 11

Upvotes: 1

Bergi
Bergi

Reputation: 665070

Well, as you have said, privileged methods are created by placing them in the constructor scope. By that definition, they cannot be shared amongst instances; otherwise they wouldn't be able to access the instance-specific scope.

Upvotes: 0

Related Questions