Reputation: 708
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
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
Reputation: 43718
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();
};
};
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();
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
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
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
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
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
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