I'm new to JS and have a question about inheritance. How are inherited properties accessed through the child class?
Lets say I have the following classes:
function ParentClass(parentArg) {
this.parentProperty = parentArg;
}
function ChildClass(childArg1, childArg2) {
this.prototype = new ParentClass(childArg1);
this.childProperty = childArg2;
}
And at some point an object is created of ChildClass, like so:
var foo = new ChildClass('value 1', 'value 2');
Then how can a parent property be accessed from the instance of the child? The following returns 'undefined'.
var parentValue = foo.parentProperty;
Much appreciated.
Cheers
If you want to initialize the parentProperty, you have to do it like this
function ParentClass(parentArg) {
this.parentProperty = parentArg;
}
function ChildClass(childArg1, childArg2) {
ParentClass.call(this, childArg1);
this.childProperty = childArg2;
}
And to actually inherit Parent't methods, you might want to do
ChildClass.prototype = Object.create(ParentClass.prototype);
After making these changes,
var foo = new ChildClass('value 1', 'value 2');
console.log(foo);
# { parentProperty: 'value 1', childProperty: 'value 2' }
foo.prototype.parentProperty If you want to get value 1 back
The problem is because you can't assign to a prototype property before the object definition is declared:
Doesn't work:
function Parent() {
this.parentProperty = "test";
}
function Child() {
// This doesn't work because the prototype is set inside of the definition
this.prototype = new Parent();
}
var child = new Child();
alert(child.parentProperty);
Works
function Parent() {
this.parentProperty = "test";
}
function Child() {
// Define prototype outside of function constructor
}
Child.prototype = new Parent();
var child = new Child();
alert(child.parentProperty);
Also notice that in the second example we assign to Child.prototype and not new Child().prototype
For ES6, you use extends, eg
class A { prop = "prop" }
class B extends A{}
new B();
Related
How would we polyfill es6 class methods into ES5?
I am reading a book and it says the following:
class Ninja {
constructor(name) {
this.name = name;
}
swingSword() {
return true;
}
}
is the same as
function Ninja(name) {
this.name = name;
}
Ninja.prototype.swingSword = function() {
return true;
};
I am just asking why are we adding the swingSword on the prototype and not inside the constructor function?
Because the function should be on the object and not on the prototype chain.
Am i right or wrong?
It should be on the prototype, methods are not per-instance data. Can't think of any language that implements it that way, the whole idea of classes is to have a whole class of objects that have the same set of methods.
If it was put it inside the constructor function, it would be a unique function per instance made with the constructor. e.g, 1000 objects == 1000 functions, per "method".
Adding the function to just the object would only work for a Ninja. To create a class that extends Ninja, for example Kunoichi, you would normally copy the Ninja prototype. Unfortunately, because swingSword is not in the prototype, your Kunoichi cannot swing swords.
You must add the function in prototype to allow the class to be extended.
If we add a method to the prototype, only one instance of that method exists in memory, and it’s shared between all objects created from the constructor.
If we add the swingSword method directly to the Ninja constructor function, then every object would have its own copy of that method, taking up more memory.
var $class = function ($superclass, config) {
// All classes have a superclass with the root
// of this $class hierarchy being Object.
var self = function (config) {
// Object.assign or $.extend or ...
config && Object.assign(this, config);
};
self.prototype = new $superclass(config);
return self;
};
var A = $class(Object, {
sayWhat: "Hello, I'm an A",
say: function () {
console.log(this.sayWhat);
}
});
var B = $class(A, {
sayWhat: "Hello, I'm a B"
});
var C = $class(B, {
say: function () {
console.log("C SAYS: " + this.sayWhat);
},
superSay: function () {
// how to call a superclass method
B.prototype.say.call(this);
}
});
var a = new A();
a.say(); // Hello, I'm an A
var b = new B();
b.say(); // Hello, I'm a B
var c = new C();
c.say(); // C SAYS: Hello, I'm a B
// create a "one-off" object
var d = new C({
sayWhat: "I'm special!",
say: function () {
console.log("hey!");
}
});
d.say(); // hey!
d.superSay(); // I'm special!
C.prototype.say.call(d); // C SAYS: I'm special!
I am struggling accessing a property that is set on a child object and accessing it via method on its prototype.
var Parent = function () {
this.getColor = function () {
return this.color;
};
};
var Child = function () {
this.color = red;
};
Child.prototype = new Parent;
var Test = new Child();
console.log(Test.getColor());
=> undefined
Any and all assistance is appreciated.
Here's how I'd do it
function Parent(color) {
function getColor() {
return color;
}
// export public functions
this.getColor = getColor;
}
Now for the Child
function Child(color) {
Parent.call(this, color);
}
Child.prototype = Object.create(Parent.prototype, {
constructor: {value: Child}
});
Let's see it work
var c = new Child("red");
c.getColor(); // "red";
Explanation:
The important bits of the Child constructor
Make sure to call the Parent constructor with the context of the Child instance (this)
Parent.call(this, color);
Setup the Child.prototype based off of the Parent.prototype
Child.prototype = Object.create(Parent.prototype, {
constructor: {value: Child}
});
You can see the node.js implementation of util.inherits uses a similar method.
This somewhat complicated line does two things for you. 1) It avoids invoking the parent constructor unnecessarily, 2) It sets the constructor property properly.
var c = new Child("red");
c instanceof Child; // true
c instanceof Parent; // true
c.constructor.name; // "Child"
But using your code, you would see
var c = new Child("red");
c instanceof Child; // true
c instanceof Parent; // true
c.constructor.name; // "Parent"
This may or may not be a concern for you, but depending on how you want to use your parent/child objects, it may be hard to programmatically differentiate which objects are from the Parent constructor and which ones are from the Child constructor.
Ok, let's see another way to do it by assigning the color property on the object itself
function Parent(color) {
this.color = color;
}
We'll add the getColor method directly to the Parent.prototype
Parent.prototype.getColor = function getColor() {
return this.color;
};
The Child constructor will stay the same. Keep in mind we'll use the same inheritance pattern we used above
function Child(color) {
Parent.call(this, color);
}
Child.prototype = Object.create(Parent.prototype, {
constructor: {value: Child}
});
Lastly, let's get the color using our getColor method
var c = new Child("red");
c.getColor(); // "red"
Or you could access the property on the object directly
c.color; // "red"
My question is not about the difference between object's members and prototype members. I understand that. I think it is similar like C# object members and static members on the class.
My question is about difference between members on constructor function and on prototype object. Comparing to C# they both are "static". So what is the difference? I only observed, that prototype members can be called the same way on instances directly, or on Constructor.prototype. The constructor function members can be called only on constructor function.
When to use which approach?
To illustrate this, imagine I need count of Persons.
Example using constructor function members:
function Person () {
Person.countOfCreatedPersons = (Person.countOfCreatedPersons || 0) + 1;
}
Person.Count = function () {
return Person.countOfCreatedPersons;
}
var p = new Person();
alert(Person.Count());
Example using prototype members:
function Person () {
Person.prototype.countOfCreatedPersons = (Person.prototype.countOfCreatedPersons || 0) + 1;
}
Person.prototype = {
Count: function () {
return this.countOfCreatedPersons;
}
}
var p = new Person();
alert(Person.prototype.Count()); // or p.Count()
When you add a property to the prototype of an object, every object that inherits from that prototype has the property:
function Ob(){};
Ob.prototype.initialised = true;
var ob1 = new Ob();
alert(ob1.initialised); //true!
alert(Ob.initialised); //undefined;
If you add it to the constructor, is like a static property. Instances won't have acces to them.
function Ob2(){};
Ob2.initialised = true;
var ob2 = new Ob2();
alert(ob2.initialised); //undefined
alert(Ob2.initialised); //true
Besides, if you add a method to the prototype, the this variable inside the method will point to your object (the instance of the class you've created with new). This is not true for class methods:
function Obj() {
this.value = 1;
}
Obj.prototype.getValue = function() {
return this.value;
};
Obj.getValue = function() {
return this.value;
};
var ob3 = new Obj();
alert(ob3.getValue()); //'1'!
alert(Obj.getValue()); //undefined!
Hope this explains.
Is there a better way of having a class inherit prototype methods from another class and still be able to define new prototype methods on the class that inherits than this:
var ParentConstructor = function(){
};
ParentConstructor.prototype = {
test: function () {
console.log("Child");
}
};
var ChildConstructor = function(){
ParentConstructor.call(this)
};
ChildConstructor.prototype = {
test2: "child proto"
};
var TempConstructor = function(){};
TempConstructor.prototype = ParentConstructor.prototype;
ChildConstructor.prototype = new TempConstructor();
ChildConstructor.prototype.constructor = ChildConstructor;
var child = new ChildConstructor();
child.test();
console.log(child.test2)
console.log(child, new ParentConstructor());
Which isn't working, because I lose my test2 prototype method / property when I add the inheritance from my ParentConstructor.
I've tried other ways to extend the prototype methods of a class with some prototype props form other classes but I have failed each time, because I couldn't find a way not to override the previous methods each time.
I have also tried the var Child = Object.create(Parent.Prototype), but when I define new props I lose the parent props.
Setting up inheritance should take place before you define new properties on the prototype of ChildConstructor. And when you define new prototype properties, you also shouldn't override the whole prototype property. Instead, you can simply add new properties, like you already did with the constructor property:
ChildConstructor.prototype = new ParentConstructor();
ChildConstructor.prototype.constructor = ChildConstructor;
ChildConstructor.prototype.test2 = "child proto";
The best example I can think of comes from:
http://robertnyman.com/2008/10/06/javascript-inheritance-how-and-why/
function Being() {
this.living = true;
this.breathes = function () {
return true;
};
}
function Robert() {
// Robert.prototype = new Being(); /* edit */
this.blogs = true;
this.getsBored = function () {
return "You betcha";
};
}
Robert.prototype = new Being();
Robert.prototype.newMethod = function() {
console.log('new method executed');
return this;
}
Note this example, has been updated, the first comment below is directed at the first code I had up, which contained the prototype inside the Robert method.
I have a nested object in javascript like this one:
{
nameRoot: "my object",
sub: {
nameSub: "my sub object"
}
}
I want to access nameRoot from a function defined in sub.
Using a function i would have defined something like:
var self = this;
and used self but how can I do this in a literal object?
The following code allows you to link to a parent element and avoid the parent showing up in a for-in loop.
var parent = { a: 1 };
var child = { b: 2 };
Object.defineProperty(
child, 'parent',
{ value: parent,
enumerable: false }
);
parent.child = child;
child.performAction = function() {
console.log(this.parent.a) // 1
}
So the best way to do this is w/ function scope.
function myFunc(){
this.nameRoot = "my object";
}
then you could do something like:
var func = new myFunc();
func.sub = new myFunc();
func.sub.nameRoot = "my sub object";
Obviously there are smarter ways to do it (e.g. pass the name through the function params) but this is the general pattern.