jkriddle
jkriddle

Reputation: 708

Javascript Private/Public Inheritence Syntax

I am having trouble combining private/public methods along with inheritance in Javascript. I think it is just a misunderstanding on my part and hopefully an easy resolution.

Here is what I have:

RB = {};
RB.Fruit = function() {
    // Public
    this.getType = function() {
        return "FRUIT";
    }
}

RB.Orange = function() {

    // Private
    function makeOrangeJuice() {
        console.log("Orange has been squeezed.");
    }

    // Public
    return {
        getName : function() {
            return "Orange";
        }
    }
}
RB.Orange.prototype = new RB.Fruit();
var o = new RB.Orange();
console.log(o.getType());

When I run this code I receive the error "Uncaught TypeError: Object # has no method 'getType'". I know that it has to do with using the "return" within the class functions (since moving the getName method out of the "return" block allows it to work), but I'd like to continue to be able to declare private/public methods within classes.

How do I modify this to allow RB.Orange to access the RB.Fruit.getType function?

Thanks!

Upvotes: 1

Views: 190

Answers (6)

HMR
HMR

Reputation: 39250

The following shows how you could implement shared private members and where to put the priviliged methods (methods that can access the shared privates);

I never found much use for this pattern and usually indicate a private being private with the name _aPrivate as Phillip already explained in his answer.

For an introduction on constructor functions, prototype, inheritance and the value of this click here.

RB = {};
RB.Fruit = function() {
}
// Public
RB.Fruit.prototype.getType = function() {
  return "FRUIT";
};

RB.Orange = function() {
  //inherit instance specific values of Fruit (there are none but there might be)
  RB.Fruit.apply(this,arguments);
};
//inherit shared members from the prototype of Fruit
RB.Orange.prototype = Object.create(RB.Fruit.prototype);
//repair constructor to be Orange instead of Fruit
RB.Orange.prototype.constructor = RB.Orange;
//shared privates and privileged methods (methods that can access the privates)
// go in the following IIFE function body.
(function(){
    //private version of makeOrangeJuice
    var makeOrangeJuice = function () {
      //the value of 'this' here isn't the Orange instance
      //if you need it then pass it with the public version of
      //makeOrangeJuice or use makeOrangeJuice.call(this) in the
      //public version
      console.log("Orange has been squeezed.");
    };
    //public version of makeOrangeJuice
    RB.Orange.prototype.makeOrangeJuice=function(){
      //call private makeOrangeJuice function
      makeOrangeJuice();
    }
}());
//non privileged member, in getName the private version of makeOrangeJuice
//doesn't exist you can call the public version with this.makeOrangeJuice
RB.Orange.prototype.getName = function() {
 return "Orange";
};
var o = new RB.Orange();
console.log(o.getType());
o.makeOrangeJuice();

Upvotes: 1

plalx
plalx

Reputation: 43718

The main problem

When you return a non-primitive value from a constructor function, that non-primitive value is returned rather than the default returned instance you would expect when invoking it with the new keyword.

E.g.

function A() { return {}; }
new A() instanceof A; //false

Therefore you could simply change your code to something like:

RB.Orange = function() {

    // Private
    function makeOrangeJuice() {
        console.log("Orange has been squeezed.");
    }

    this.getName = function ()  {
        return 'Orange';
    };

    //priviledged function which uses a private member
    this.someOtherFunction = function () {
        makeOrangeJuice();
    };
};

Some inefficiencies in your code

Why not using the prototype?

Functions that aren't priviledged should not be declared within the constructor function. In other words, functions that do not access private variables should not be created in the constructor function because they do not have to and it's extremely inefficient to do so. Why? Because a new function is being created everytime the constructor is called.

Instead you should make use of the Constructor.prototype to share your public functions between all instances.

E.g.

function Person(name) {
    this.name = name;
}

Person.prototype.sayName = function () {
    console.log('My name is ' + this.name);
};

new Person('Foo Bar').sayName();

Use Object.create rather than new for inheritance when possible.

Most inheritance patterns using the new keyword were done this way because the language was lacking another way of setting up the prototype chain of an object, but now that we have Object.create, your should use it. Using the new keyword for inheritance the way you did has some undesired side-effects like running the constructor function. There are ways to avoid these side effects by using an intermediate empty function but why not simply use Object.create?

E.g. (based on the above example)

function BadPerson(name) {
    //call parent constructor
    Person.call(this, name + ' the bad');
}

BadPerson.prototype = Object.create(Person.prototype);
BadPerson.prototype.constructor = BadPerson; //fix constructor

Private functions can also be shared!

Note that private functions that do not access private variables can also be shared. You can make use of the module pattern to create a scope for them.

E.g.

var Person = (function () {

    //private function used in a functionnal style
    function _validateName(name) {
        console.log('Validating the name in functionnal style');
    }

    //private function used in an OO style
    function _validateNameOO() {
        console.log('Validating the name in a OO style');
    }

    function Person(name) {
        this.name = name;
    }

    Person.prototype.validateNameBothWays = function () {
        _validateName(this.name);

        _validateNameOO.call(this);
    };

    return Person;
})();

new Person().validateNameBothWays();

Upvotes: 2

ruakh
ruakh

Reputation: 183240

In JavaScript, a constructor call implicitly returns the newly-constructed instance, but the constructor can override that default behavior by explicitly returning a different object. For example, if you define a "constructor" Foo like this:

function Foo() {
    return new Date();
}

then the statement foo = new Foo() will set foo to a new Date, not a new Foo.

If I understand correctly what you want, you just need to change this:

    return {
        getName : function() {
            return "Orange";
        }
    }

(whereby your "constructor" returns a completely fresh object, with only a getName method, and no relation to the object under construction) to this:

    this.getName = function() {
        return "Orange";
    };

(whereby it adds a getName method to the object under construction, and still allows that object to be returned).

Upvotes: 2

philwills
philwills

Reputation: 995

You need to assign the functions to the prototype of your objects, if you want them to be inherited.

RB = {};
RB.Fruit = function() {};
RB.Fruit.prototype.getType = function() {
    return 'Fruit';
};

RB.Orange = function() {};
RB.Orange.prototype = new RB.Fruit();
RB.Orange.prototype.getName = function() {
    return 'Orange';
};

If you really need to use privates, and can't just label things as private using conventions like the _name, then you'll need to move the functions that will use the privates into the constructor with the private members.

If they're not instance specific, you can (and should) wrap this whole thing with an immediate function.

(function() {
    // All previous code here

    window.RB = RB;
}());

Upvotes: 1

Shakti Shrestha
Shakti Shrestha

Reputation: 56

try this code.

RB = {};
RB.Fruit = function() {
    // Public
    this.getType = function() {
        return "FRUIT";
    }
}
RB.Fruit.prototype.getType = function() {
        return "FRUIT";
    };
RB.Orange = function() {
    RB.Fruit.call(this);
    // Private
    function makeOrangeJuice() {
        console.log("Orange has been squeezed.");
    }

    this.getName = function() {
            return "Orange";
        };
    this.getJuice = function(){
            makeOrangeJuice();
        };

};
var o = new RB.Orange();
//calling the super-call's function
console.log(o.getType());
//public function
o.getJuice();
//trying to access private function
o.makeOrangeJuice();

For more detail on the code ojbect oriented javscript plz check below link http://mckoss.com/jscript/object.htm

Upvotes: 0

Rob Dawley
Rob Dawley

Reputation: 254

Here is one way that you could do it:

var RB = {};
RB.Fruit = function() {
    // Public
    this.getType = function() {
        return "FRUIT";
    }
}

RB.Orange = function() {
    // Private variable
    var fruit = new RB.Fruit();

    // Private function
    function makeOrangeJuice() {
        console.log("Orange has been squeezed.");
    }

    // Public object with accessor
    return {
        getName : function() {
            return "Orange";
        },
        getType: fruit.getType
    }
}

var o = new RB.Orange();
console.log(o.getType());

Upvotes: 0

Related Questions