I am new to oop in Javascript and just need to get to grips with this.
If I have a class with properties
function myClass(){
this.foo=null;
}
I then use inheritance to create a child class
myChild.prototype = new myClass();
function myChild(){
alert(this.foo);
}
How do I set the property of foo when instantiating the child class e.g I want to alert 'bar'. I don't want to simply pass 'bar' to myChild as I have a list of properties to set that are relevant to a method in myClass and not myChild.
var newChild = new myChild();
You can actually find the answer to this question also in my answer to previous question and it is similar to inheritance in other languages.
If you extend a class, the constructor function of the child class has to accept its own arguments and the one for the parent class. So assuming you have:
function Parent(a) {
this.foo = a;
};
// and
function Child(b, a) {
Parent.call(this, a); // executes the parent constructor for this instance
this.bar = b;
alert(this.foo);
};
inherits(Parent, Child);
(the implementation of inherits can be found in this answer).
Inside Child you have to call the construtor of the parent class and pass the parameters, similar how you do it in Java or Python.
If you have many parameters, then you can make use of the arguments object, to make things a bit easier:
function Parent(a, b, c, d) {...};
function Child(e, f) {
// c and d are parameters for `Child`
// arguments[0] == e
// arguments[1] == f
// all other arguments are passed to Parent, the following
// creates a sub array arguments[2..n]
Parent.apply(this, [].slice.call(arguments, 2);
/...
}
// later
var child = new Child(e, f, a, b, c, d);
In general, myChild.prototype = new myClass(); is not a good inheritance pattern as most of the time, classes expect some arguments. This won't execute the parent constructor for each instance but only once for all instances.
You could just set the property in the child's constructor like so:
myChild.prototype = new myClass();
function myChild(){
this.foo = "bar";
}
Is that what you want?
Or if you want to be flexible about what foo contains in each instance you could just set it right after instantiation of the child class:
var child = new myChild();
child.foo = "bar";
Parameterize your construction method.
function myClass(foo){
this.foo=foo;
}
myChild.prototype = new myClass('bar');
function myChild(){
alert(this.foo);
}
var newChild = new myChild();
or:
function myClass(){
this.foo=null;
}
myChild.prototype = new myClass();
myChild.prototype.foo = 'bar';
function myChild(){
alert(this.foo);
}
var newChild = new myChild();
Related
In this JavaScript code, when the instantiation of new child causes its constructor to execute, the create method does not seem to creating the parent object. The child does not seem to inherit the parent's member function m.
function parent() {
parent.prototype.a =2;
parent.prototype.m = function() {
return this.a++;
}
}
function child() {
child.prototype = Object.create(parent.prototype);
child.prototype.constructor = parent;
parent.call(this);
}
var c = new child();
alert(child.prototype.m()); // 2
alert(child.prototype.m()); // 3
alert(c.m()); // FAILS!!!
Your prototype methods and inheritance should be defined outside of your functions...
function parent() {
}
parent.prototype.a =2;
parent.prototype.m = function() {
return this.a++;
}
function child() {
parent.call(this);
}
child.prototype = Object.create(parent.prototype);
var c = new child();
alert(child.prototype.m()); // 2
alert(child.prototype.m()); // 3
alert(c.m()); // 4
Here is basically what happens when you run new child:
var obj = Object.create(child.prototype);
child.call(obj);
return obj;
Inlining your constructor would look like:
var obj = Object.create(child.prototype);
// inline
child.prototype = Object.create(parent.prototype);
child.prototype.constructor = parent;
parent.prototype.a =2;
parent.prototype.m = function() {
return this.a++;
}
// end inline
return obj;
Do you notice that nothing in the constructor modifies obj in any way? While you modifying child.prototype and parent.prototype, this has no effect on obj, since created a new object with child.prototype = Object.create(parent.prototype), where obj inherits from the old child.protoype object.
Here is a simplified example of what you are doing:
var proto = {};
var obj = Object.create(proto);
proto = {};
proto.m = function() {};
It should be pretty clear that assigning to proto.m has no effect on obj, since we assigned a new object o proto beforehand.
There have been many question about OOP in JS on Stack Overflow already, you can easily search for them. I also recommend to read the MDN article about OOP in JS and Benefits of using `Object.create` for inheritance
It looks like you are misunderstanding how to setup custom objects in JavaScript. Normally you would add your methods to the prototype outside the constructor, doing it inside will reset the properties every time the constructor is called.
function parent() {
}
parent.prototype.a = 2;
parent.prototype.m = function() {
return this.a++;
}
function child() {
parent.call(this);
}
child.prototype = Object.create(parent.prototype);
child.prototype.constructor = parent;
var c = new child();
alert(child.prototype.m()); // 2
alert(child.prototype.m()); // 3
alert(c.m()); // 4
In this case, it is failing because you create the new prototype for the child.
Object.create(parent.prototype);
Before you set your m method it in the parent constructor.
We're all familiar with the following polyfill to create objects in JavaScript:
function inherit(C, P) {
var F = function () {};
F.prototype = P.prototype;
C.prototype = new F();
}
One downside of this is that you will not have access to the parent's own members. Child instances will only have access to the prototype chain.
So I think the best inheritance solution is as follows:
var inherit = (function () {
var F = function () {};
return function (C, P) {
F.prototype = P.prototype;
C.prototype = new F();
C.prototype.constructor = C;
}
}());
function Parent(a){
this.a = a;
}
Parent.prototype.a_proto = function(){return "a_proto";}
function Child(a,b){
Parent.apply(this,[a]);
this.b = b;
}
inherit(Child, Parent);
var c = new Child("a","b");
console.log(c.a); // a
console.log(c.b); // b
console.log(c.a_proto()); // a_proto
delete c.a;
console.log(c.a); // undefined
The reason why I like the above solution is because first you use the apply function to copy the parent's own members to the child's own members, therefore allowing true inheritance of the parent's own members. Second, the child also has a link to the prototype chain, and can therefore inherit prototype properties of the parent…and since we use a proxy, the child cannot modify the parent's prototype.
I was also introduced to the concept of an uber method in the book JavaScript Patterns. Basically, in the uber method the author says we "add a reference to the original parent". This is like having access to the superclass in other languages and could be handy on occasion." Here's the implementation:
function inherit(C, P) {
var F = function () {};
F.prototype = P.prototype;
C.prototype = new F();
C.uber = P.prototype;
}
I don't see how adding a reference to the original parent is good. Now the child is able to modify the parent's prototype, which is a bad thing. What is this person talking about, we now have access to superclass like in other languages? I thought using apply and the prototype chain already has given access to the superclass. Are there really any benefits to having a reference to the parent's prototype?
function Parent (arg1, arg2) {
alert(arg1);
this.member1 = arg1;
this.member2 = arg2;
};
Parent.prototype.update = function () {
// parent method update
};
function Child (arg1, arg2, arg3) {
Parent.call(this, arg1, arg2);
this.member3 = arg3;
};
Child.prototype = new Parent;
Child.prototype.update = function () {
// overwritten method update
};
function init () {
var childObject = new Child(false, false, false);
childObject.update();
}
The result are two alerts with
undefined
false
Why does the alert occurs two times? I already searched, but haven't found anything yet + don't know what to search for.
The result should be one alert with 'false', or am i wrong?
Thx alot!
By using the constructor of Parent to create the prototype for Child, the constructor is being called which is your first alert of undefined.
In order to create a prototype that still uses the same prototype chain, but doesn't call the parent constructor as the prototype is created, you need to add another step in between.
Child.prototype = (function() {
var Base = function() {};
Base.prototype = Parent.prototype;
return new Base();
}());
This will create an anonymous function (called Base) that has the prototype set to be the prototype of the Parent class, the Child prototype is then assigned to a new Base which will preserve the inheritance, but doesn't call the constructor of Parent as the prototype chain is created.
There is one alert when you create a new object of Parent Child.prototype = new Parent; and one when you create new object of child, var childObject = new Child(false, false, false);
I'm trying to override methods of an object but still call the prototype's original method using Object.getPrototypeOf(). This works great the first time, but if the method is overridden more than once there are problems.
This code results in a stack overflow:
A =
{
blurg: function()
{
puts('hey there')
}
}
B = (function buildB(){
var obj = Object.create(A)
obj.blurg = function()
{
Object.getPrototypeOf(this).blurg.apply(this, arguments)
puts('hai!!!!')
}
return obj
})()
b = (function buildb(){
var obj = Object.create(B)
obj.blurg = function()
{
Object.getPrototypeOf(this).blurg.apply(this, arguments)
puts('bye bye')
}
return obj
})()
b.blurg()
jsFiddle
The problem is that I want to call the prototype's method with the current object as this. This causes problems when that method does the same thing.
Am I going about this the wrong way? Is there a way I could create a helper function for making sure the correct prototype is pulled up? I'm a bit at a loss.
The problem is that in JavaScript, by nature, this always refers to the bottom down object instance in a prototype chain, so when you override methods in a hierarchical structure like above, this.prototype.someMethod() refers to the exact base class of the object instance, it doesn't seem to be a problem when you have maximum of two levels of hierarchy, however when you define three levels of hierarchical structure or more, recursion is inevitable! here's how:
A: grand super class
B: super class - inherits from A (B.prototype = A)
C: class - inherits from B (C.prototype = B)
a: instance of A (defines someMethod)
b: instance of B (defines someMethod, calls A.someMethod through Object.getPrototypeOf(this))
c: instance of C (defines someMethod, calls B.someMethod through Object.getPrototypeOf(this))
When b.someMethod is called, it can successfully call A's someMethod (Object.getPrototypeOf(this) returns A when called by b)
However when c.someMethod is called, it first calls b.someMethod, which in turn calls b.someMethod because Object.getPrototypeOf(this) always returns B when called by c! And here's where stack overflow occurs.
To resolve this, try to store a base class reference whenever you define a new sub-class, avoid using this when calling a super-class method:
A =
{
blurg: function () {
console.log('hey there')
}
};
B = (function buildB() {
var obj = Object.create(A);
var base = Object.getPrototypeOf(obj);
obj.blurg = function () {
base.blurg.apply(this, arguments);
console.log('hai!!!!')
}
return obj
})();
C = (function buildb() {
var obj = Object.create(B);
var base = Object.getPrototypeOf(obj);
obj.blurg = function () {
base.blurg.apply(this, arguments);
console.log('bye bye');
}
return obj
})();
C.blurg();
if I have
myClass.prototype.subClass=
{
foo:false
}
when I create a new instance on an object how can I set foo to a different value each time, so how do I refer to that sub-class here
var newObj = new myclass();
Objects and arrays are not something which should be added to the prototype unless you want to share them with all instances.
As soon as you want properties of these objects to be different for each instances, you have to assign the object in the constructor (or in any other function) to the specific instance:
this.subClass = {
foo: true
// potentially other properties
};
That said, there might be cases were having a "default" object in the prototype might be reasonable, but you should not write to it.
Assigning the object in the constructor instead does not duplicate code and allows you to change it for each instance individually.
Update:
If you don't want to change the original constructor, you can either just add a new function to the prototype and call it whenever you instantiate an object:
MyClass.prototype.init = function() {
this.subClass = {
//...
};
};
and
var obj = new MyClass();
obj.init();
Or you really create a new constructor function:
function MySubClass() {
MyClass.apply(this, arguments);
// now create that object for each instance
this.subClass = {
foo: someValue
};
}
inherits(MySubClass, MyClass);
where inherits is defined as:
function inherits(Child, Parent) {
var Tmp_ = function() {};
Tmp_.prototype = Parent.prototype;
Child.prototype = new Tmp_();
Child.prototype.constructor = Child;
}
Then you will use MySubClass instead of MyClass to create the instances.
I think you missunderstand how prototype inheritence works because this question is a bit odd. But you should be able to access the foo property like any other:
newObj.subClass.foo = newValue