1

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!

jkriddle
  • 708
  • 1
  • 6
  • 15
  • I see that you selected another answer. At least make sure that you undertstand all the concepts explained in mine. – plalx Nov 20 '13 at 12:17

6 Answers6

2

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).

ruakh
  • 175,680
  • 26
  • 273
  • 307
  • I don't want to be picky since I actually used the same explanation myself for a long time, but maybe you are interested nevertheless: The constructor function doesn't actually return anything implicitly and it doesn't create the new instance. The `new` operator creates the new instance, applies the constructor function on it, and then, if the function doesn't return an object, the `new` operator returns the new instance (otherwise the object return by the constructor function). I'm not criticizing your explanation, just FYI. – Felix Kling Nov 20 '13 at 03:04
  • @FelixKling: Well, I never said that the constructor creates the instance. But as for "doesn't actually return anything implicitly", you're right, what I wrote is misleading. Instead of "a constructor implicitly returns [...] *but* it can override", I should have written "a constructor call implicitly returns [...] *but* the constructor can override". (Unless you don't consider `new Date()` to be a "constructor call"?) I'll update my answer accordingly. – ruakh Nov 20 '13 at 03:08
  • Don't see why getName has to be in the constructor body instead of on the prototype. It is not a privileged method that needs to access private instance specific values is it? – HMR Nov 20 '13 at 03:14
  • @HMR Totally agree... see me answer. – plalx Nov 20 '13 at 03:25
  • @HMR: I guess I'm assuming the OP isn't genuinely trying to create fruits, oranges, etc., but rather that he simplified his code, or else that this is a figuring-things-out exercise. So I figured that the real version of this method really does require private information. – ruakh Nov 20 '13 at 06:46
2

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();
plalx
  • 42,889
  • 6
  • 74
  • 90
  • I used a slightly different way of making shared privates. Just wrap the privates and the privileged methods in an IIFE. The constructor itself doesn't need to be included in the closure scope unless you plan you to do something like `_privateMethod()` in the constructor. Constructor functions and prototypes are very hard to learn especially when 50% of the answers and documentation is wrong or has inferior/resource hungry patterns. Thank you for providing a complete and quality answer. – HMR Nov 20 '13 at 03:43
  • @HMR I agree that it isin't the easiest thing at first to grasp the prototypal concepts and that's one of the reason JavaScript is so misunderstood (and sometimes hated). I slighlty favor encapsulating the whole thing in an IIFE for the sake of simplicity and flexibility, but at that point it's a personnal preference I believe ;) Thanks for your comments! – plalx Nov 20 '13 at 03:55
1

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;
}());
philwills
  • 985
  • 4
  • 8
1

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();
Community
  • 1
  • 1
HMR
  • 37,593
  • 24
  • 91
  • 160
  • Thank you HMR! There are a bunch of great responses in this thread, but your solution followed the restrictions I made and also used the original example to demonstrate. – jkriddle Nov 20 '13 at 09:29
0

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());
Rob Dawley
  • 244
  • 3
  • 5
  • I think the OP is trying to use prototype for inheritance instead of copy and pasting all the Fruit methods in the return value of Orange. http://stackoverflow.com/a/16063711/1641941 – HMR Nov 20 '13 at 03:10
0

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

  • RB.Fruit.prototype.getType never gets called because Fruit instances have their own getType (this.getType in Fruit body). Why is getName of Orange not on Orange.prototype? – HMR Nov 20 '13 at 03:08
  • @HMR I believe he simply forgot to remove the one from within the constructor ;) – plalx Nov 20 '13 at 03:24
  • The link ref uses bad example of setting the prototype (using new Parent() to set Child.prototype), when it finally does use a helper function to set it up it'll copy members of Parent.prototype to Child.prototype one at the time. Child instances aren't actually of type Parent (childInstance instanceof Parent is false). When adding to Parent.prototype after calling the helper function these added members are not in Child and last but not least; It only copies functions. Primitive default values on the prototype are not copied to Child. – HMR Nov 20 '13 at 04:14
  • @plalx I'm not so sure of that. There is a deliberate cramming of all members in constructor body and no use of prototype. Then in Orange the `Fruit.call(this);` is used to get instance specific members of Fruit and run it's initialization (inheritance 50% complete). When removing getType from Fruit's constructor body the line `o.getType()` will fail because inheritance has only been set up for 50%. I see the 50% setup in many answers but usually the `Parent.apply(this,arguments);` is omitted. – HMR Nov 20 '13 at 04:15