Entity Black
Entity Black

Reputation: 3491

Is it possible to define priviledge method outside of the constructor?

Well I have this constructor with one secret and one method:

function Keeper(get) {
    var secretPower = 'wisdom';

    this.get = get ? get : function () { 
        return 'Its secret power is: ' + secretPower; 
    }
    // now this is privileged method only in case that there are no arguments?
}

Now I will make two instances, one has privileged method...

var yourKeeper = new Keeper();
yourKeeper.get();       // "Its secret power is: wisdom"

... but the another one is different. It can touch context around it, but not privates of the constructor ...

var myKeeper = new Keeper(function() { 
        return 'Its secret power is: ' + secretPower; 
    });
myKeeper.get();         // ReferenceError: secretPower is not defined

... also this doesn't work as I want:

myKeeper.get = function() { 
    return 'Its secret power is: ' + secretPower; 
}
myKeeper.get();         // ReferenceError: secretPower is not defined

Of course it won't work, because secretPower is global variable in these cases, so:

var secretPower = 'none';
myKeeper.get();         // "Its secret power is: none"

So is it possible to define priviledge method outside of the constructor? How?

Can it be done with eval? (I know... it is evil... Im just interested)

Upvotes: 2

Views: 239

Answers (3)

claustrofob
claustrofob

Reputation: 4984

I would never actually do it this way but here is a solution with eval:

function test(){
    var private = 'hello';

    this.say = function(){

    }

    this.setSay = function(func){
        eval('this.say = ' + func.toString());
    }
}

var x = new test();
x.setSay(function(){
    console.log(private + ' world');
});
x.say();

example http://jsfiddle.net/claustrofob/AwMd3/

And here is an example related to your Keeper object:

function Keeper(get) {
    var secretPower = 'wisdom';
    var getFunc = function () { 
        return 'Its secret power is: ' + secretPower; 
    };

    var evalFunc = function(variable, val){
        eval(variable + ' = ' + val.toString());
    };

    this.__defineSetter__("get", function(val){
        evalFunc('getFunc', val);
    });

    this.__defineGetter__("get", function(val){
        return getFunc;
    });

    if (get !== undefined){
        evalFunc('getFunc', get);   
    }
}

here we define setter and getter for your get method so you can init your objects this way:

var x = new Keeper(function () { 
    return 'Its secret power is: ' + secretPower; 
});

and this way:

var x = new Keeper();
x.get = function () { 
    return 'Its secret power is: ' + secretPower; 
};

live example http://jsfiddle.net/claustrofob/yu6VJ/

Upvotes: 1

Aadit M Shah
Aadit M Shah

Reputation: 74234

Yes it's possible to define privileged methods outside of the constructor. Here is how to do it:

var Keeper = (function (key) {
    function Keeper(get) {
        var private = {
            secretPower: "wisdom"
        };

        this.getPrivate = function (k) {
            if (k === key) return private;
        };

        this.get = get || defaultGet;
    }

    function defaultGet() {
        var private = this.getPrivate(key);
        return "The secret power is: " + private.secretPower;
    }

    return Keeper;
}({}));

Here is how it works:

  1. We create a namespace by creating an Immediately Invoked Function Expression (IIFE).
  2. We create a object called key. This object is private to the namespace we just created. Hence only privileged functions and the constructor can access it.
  3. Inside the constructor we create an object called private which holds the private state of each instance.
  4. We also create a privileged method called getPrivate which takes an argument k and only returns the private object if k is the key we created in step 2. Hence only privileged functions can access the private object.
  5. Privileged functions which want to access the private state of the object must call getPrivate(key) to get the private state object.

Note: This method is only useful if you have more than one privileged function which needs to access the private state of the object. In addition the call the getPrivate introduces a (very) small performance overhead to privileged functions.


BTW if you want to write clean object-oriented code in JavaScript then take a look at the augment library. The above code will looks like this using augment:

var Keeper = Object.augment(function (key) {
    this.constructor = function (get) {
        var private = {
            secretPower: "wisdom"
        };

        this.getPrivate = function (k) {
            if (k === key) return private;
        };

        this.get = get || defaultGet;
    };

    function defaultGet() {
        var private = this.getPrivate(key);
        return "The secret power is: " + private.secretPower;
    }
}, {});

Upvotes: 2

David Mulder
David Mulder

Reputation: 27020

The very idea behind the scoping in javascript is that this shouldn't be possible, what I would advice is adding a function getPrivate within the scope of the constructor and use that to access 'private' variables.

function Keeper(get) {
    var semiPrivates = {
        secretPower:'Wisdom'
    }

    this.getPrivate = function (variable){
        return semiPrivates[variable];
    }

    this.get = get ? get : function () { 
        return 'Its secret power is: ' + semiPrivates["secretPower"]; 
    }
}

Upvotes: 2

Related Questions