user1244179
user1244179

Reputation: 21

Private JS function, What is wrong with this?

  1. Is there anything wrong with writing public functions for an object inside its constructor function?
  2. Is there anything wrong with adding functions to prototype chain inside constructor function?
  3. Alternatives to this?
var obgect = function (constructorOptions) {
    var privateFunction1 = function (v1) {
        return 'You said: ' + v1;
    };
    this.willHaveAccessToPrivate = function () {
        return 'This function will have access to privateFunction1(), but will I be on prototype chain?';
    };
    obgect.prototype.anotherPublicFunction = function () {
        var s = privateFunction1('I have access to you.');
        return 'Adding on prototype from within the constructor.';
    };
};
obgect.prototype = new Object();
obgect.prototype.outsidePublicFunction = function () {
    var s = privateFunction1('I will fail here because you are not accessible.');
    return 'Standard practice';
};
var instance = new obgect({
    'gurusWanted': true,
        'hurtsMyHead': true,
        'discouragesMe': false
});
var outside = instance.outsidePublicFunction(); // will fail here.
var anotherInside = instance.anotherPublicFunction(); // will work!

Upvotes: 0

Views: 67

Answers (1)

T.J. Crowder
T.J. Crowder

Reputation: 1074475

1. Is there anything wrong with writing public functions for an object inside its constructor function?

No, it's fairly common practice. It does mean that every instance gets its own copy of each function, which could have some memory impact, but unless you're creating thousands of them that's not necessarily an issue. You just have to know you're doing it.

2. Is there anything wrong with adding functions to prototype chain inside constructor function?

Yes, big time. It creates cross-talk between instances, because an instance created earlier will end up using the function assigned to the prototype by an instance created later. Very, very much something to avoid. Consider this much simpler example:

function Example(data) {
    var privateData = data;
    Example.prototype.doSomethingWithPrivateData = function() {
        console.log(privateData);
    };
}

var e1 = new Example(1);
e1.doSomethingWithPrivateData(); // "1"
var e2 = new Example(2);
e2.doSomethingWithPrivateData(); // "2"

So far, so good, right? But:

e1.doSomethingWithPrivateData(); // "2"

Oops! e1 is using e2's private data now, because e2 was created after e1 and rewired the prototype.

Functions on the prototype cannot have access to "private" functions created the way you're creating them (in the constructor).

3. Alternatives to this?

That's probably too broad a question to answer, but the rules above should help you decide how to proceed.

But if having private features of instances is an important thing to you, there is a pattern you can use for it while still getting function reuse. I detail it in this blog post, but here's the gist:

In ES6, we'll have "private Name objects" which can be used as property keys, so you can store data in instances that cannot be accessed unless you have that special Name object. Now, we can't do that in ES5, but we can get close by using a Name function that returns semi-random names, like this:

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 here's how we have (nearly) private members using that:

// Nearly-private properties
// ***No `import` here (once the final form is determined, we'll probably be able to feature test for it)
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

Nothing outside Foo can readily use the nearly-private data, because the property name keeps changing.

Upvotes: 4

Related Questions