Method is not getting passed down while doing Inheritance in JavaScript - javascript

I have a function constructor defined this way:
var Person = function (name, yearOfBirth, job) {
this.name = name;
this.yearOfBirth = yearOfBirth;
this.job = job;
}
Person.prototype.calculateAge = function () {
console.log(2016 - this.yearOfBirth);
};
Now I also have another function constructor called Teacher which I've defined this way:
var Teacher = function (name, yearOfBirth, subject) {
Person.call(this, name, yearOfBirth, "teacher");
this.subject = subject;
}
Now I create a new object called roySir this way:
var roySir = new Teacher("Roy", 1960, "English");
However when I try to do
roySir.calculateAge() I get an error saying that
"roySir.calculateAge is not a function"
How come the calculateAge function is not inherited here?
Another question I have is when I check:
roySir.hasOwnProperty("name") // true
Why is this true here? Isn't name a property of the parent class rather than an own property?

You should ensure that Teacher's prototype inherits from Person's prototype. Simply calling Person with a Teacher won't let Teacher inherit from Person's prototype methods:
var Person = function(name, yearOfBirth, job) {
this.name = name;
this.yearOfBirth = yearOfBirth;
this.job = job;
}
Person.prototype.calculateAge = function() {
console.log(2016 - this.yearOfBirth);
};
var Teacher = function(name, yearOfBirth, subject) {
Person.call(this, name, yearOfBirth, "teacher");
this.subject = subject;
}
Teacher.prototype = Object.create(Person.prototype);
var roySir = new Teacher("Roy", 1960, "English");
roySir.calculateAge();
You need the Object.create rather than Teacher.prototype = Person.prototype there so that mutations to Teacher.prototype won't undesirably change Persons that aren't Teachers - for example, if you gave Teacher.prototype a teachesClass method, you would want only Teachers to have access to that, but you wouldn't want a generic Person to have that method.
Alternatively, use ES6 and extends, which is more readable:
class Person {
constructor(name, yearOfBirth, job) {
this.name = name;
this.yearOfBirth = yearOfBirth;
this.job = job;
}
calculateAge() {
console.log(2016 - this.yearOfBirth);
}
}
class Teacher extends Person {
constructor(name, yearOfBirth, subject) {
super(...[name, yearOfBirth, subject, 'teacher']);
}
}
var roySir = new Teacher("Roy", 1960, "English");
roySir.calculateAge();
As for the name property, it's assigned to the object itself with this.name = name; - when a constructor is called, like with Person.call(this, ...), the this in the other constructor still refers directly to the object being created in the calling code - that's what call does, the first argument passed to it will be a direct reference to the this used in the other function.
The prototype chain looks like:
roySir { name, yearOfBirth, job }
above inherits from Teacher.prototype (empty)
above inherits from Person.prototype { calculateAge }

Related

How to use constructors as a prototype chain?

Suppose that I have a javascript constructor:
function Person(name) {
this.name = name;
this.hello = function () { return "It's a-me, " + name + "!"; };
}
the Person "type" has a convenient method, hello that I would like to re-use on another type Student. I would like for a Student to have the following structure:
function Student(name) {
this.name = name;
this.hello = function () { return "It's a-me, " + name + "!"; };
this.books = [];
}
One option is to use the code for Student as-is above. This is sub-optimal for the usual reasons, such as that if I want it to mirror the Person type, then I have to manually keep their code in sync. Anyway, this is not good.
A second option (from this answer) is to do something like:
function Student(name) {
Person.call(this, name);
this.books = [];
}
When I mario = new Student("mario") I get the following:
Object { name: "mario", hello: hello(), books: [] }
I've successfully achieved the inheritance that I wanted, but this has the unfortunate property of placing all of the desired properties into my object. Notably, for example, there is a "hello" property on mario. It would be nice if that "hello" property could be looked up in the prototype chain.
How can I neatly create a prototype chain given the relevant object constructors?
When you create an object with new, the this value of your constructor function is set to the object, and that object's prototype is set to the prototype of the constructor Function being called.
That's why your properties are currently being added to the created object.
function Student {
this.name = name
}
const student = new Student('John')
// is (almost) equivalent to the following
const student = {}
student.name = 'John'
But if you want to add properties to the prototype instead, so that you can use inheritance, then in ES5 Javascript you can do so by assigning properties directly to the prototype of your constructor function.
function Person(name) {
this.name = name;
}
// Person is a function
// Its prototype is an instance of Object, and it has no properties
// i.e. something like Person.prototype = new Object()
Person.prototype.hello = function() {
return 'It is I, ' + this.name
}
// Now Person.prototype has one property, "hello", which is a function.
function Student(name) {
Person.call(this, name)
this.books = [];
}
// Student is a function
// Its prototype is also an instance of Object with no properties
// the following is the magic line
Student.prototype = Object.create(Person.prototype)
// We replace the prototype of the Student function with a new object, but
// Object.create() allows us to set the prototype to an existing object, in this case Person.prototype,
// Person.prototype is itself an instance of Object, and we previously
// added the "hello" function to it as a property.
const student = new Student('John')
// So what happens here?
// First a new object is created, and its prototype is set to Student.prototype
// Then we call Person.call(this)
// Which executes the body of the Person function
// So you get the properties on the object itself through the body of the Person and Student functions
// And you get the shared functionality through the prototype chain of instance -> Student.prototype -> Person.prototype -> Object.prototype
Hope that helps!
You can use prototyping method or class sugar method as you want.
Here is a simple example :
function Student(name) {
this.name = name;
this.books = [];
}
Student.prototype.hello = function(){
return "It's a-me, " + this.name + "!";
}
Student.prototype.addBook = function(book){
this.books.push(book);
}
Student.prototype.getBooks = function(){
return this.books;
}
let mario = new Student("Mario");
console.log(mario.hello());
mario.addBook("prototyping");
mario.addBook("chain");
console.log(mario.getBooks());
class Person {
constructor(name) {
this.name = name;
this.books = [];
}
hello(){
return "It's a-me, " + this.name + "!";
}
addBook(book){
this.books.push(book);
}
getBooks(){
return this.books;
}
}
let luigi = new Person("Luigi");
console.log(luigi.hello());
luigi.addBook("classSugar");
luigi.addBook("classType");
console.log(luigi.getBooks());
For longer chains use Object.assign, here is an example of making a GradStudent that is both a Student and a Person and has the personality of a Comedian and also has the properties and methods of a 4th class GameCharacter:
(function() {
//Person
function Person(name) {
this.name = name;
this.helloString = "Hello my name is "
}
Person.prototype.name = "Bob";
Person.prototype.hello = function() {
return this.helloString + this.name;
};
//Student
function Student(name, books) {
Person.call(this, name);
this.books = books;
}
Student.prototype = Object.create(Person.prototype);
Student.prototype.constructor = Student;
Student.prototype.books = ["math","reading"];
//Comedian
function Comedian(name) {
Person.call(this,name);
};
Comedian.prototype = Object.create(Person.prototype);
Comedian.prototype.constructor = Comedian;
Comedian.prototype.hello = function() {
return "I don't know what my parents where thinking when they name me Squat, just kidding, my name is " + this.name;
};
//GameCharacter
function GameCharacter(power) {
this.power = power;
};
GameCharacter.prototype = new Object();
GameCharacter.prototype.constructor = GameCharacter;
GameCharacter.prototype.gainPower = function(power) {
this.power += ", "+power;
};
GameCharacter.prototype.statePower = function() {
return this.power;
};
//GradStudent
function GradStudent(name, books, degree) {
Comedian.call(this, name);
Student.call(this,name,books);
GameCharacter.call(this, "jumping");
this.degree = degree;
this.gainPower("flying");
}
GradStudent.prototype = Object.create(Student.prototype);
Object.assign(GradStudent.prototype, Comedian.prototype, GameCharacter.prototype);
GradStudent.prototype.constructor = GradStudent;
var gradStudent = new GradStudent("Bill",["C", "C++", "JavaScript"], "B.S.");
console.log(gradStudent.hello() + " I have a " + gradStudent.degree +" I am studying " + gradStudent.books.toString() + ". \n In a game I play my power's are "+ gradStudent.statePower() + ". \n Is gradStudent also a Student? " + (gradStudent instanceof Student) + "" );
var otherStudent = new Student("Jennifer" ,["english", "science"]);
console.log(gradStudent.books.toString() + " " + otherStudent.books.toString());
})();
GradStudent is an instance of Student, it's a type of Student, and can also do all the things Comedian and GameCharacter does. The value of Object.assign is that kind of multiple inheritance.
I can accomplish such a thing with the following:
function Student(name) {
Object.setPrototypeOf(this, new Person(name));
this.books = [];
}
However, I'm not familiar enough with javascript to know what possible problems might arise with this solution. Coming from other OO style languages, it feels weird for the prototype of mario to be an actual instance of a Person, but I suppose everything in js is an instance, in some sense, so this might just be bias on my part.

Unable to use property from constructor

I'm creating two ES js Classes (Person, Teacher)
class Person {
constructor (name = "no name") {
this.name = name
}
Teacher inherit from Person
class Teacher extends Person {
constructor(name, degree){
super(name)
this.degree = degree;
}
}
In the teacher constructor, I have a new property called degree.
when I create a new property called Full name that takes name and degree. Degree shows as undefined. Although when I log object teacher property is there. It looks like a delay issue. But, shouldn't I be able to access property right away?
class Person {
constructor(name = 'no name') {
this.name = name;
}
}
class Teacher extends Person {
constructor(name, degree) {
super(name);
this.degree = degree;
}
fullname = this.degree + this.name;
printFullName() {
console.log(this.fullname);
}
}
let person = new Teacher('Adam', 'MS.');
console.log(person);
person.printFullName(); // undefined Adam
https://repl.it/#adbarani/TrimBrilliantPaintprogram#index.js
This is the behaviour specified in the MDN documentation:
Public instance fields are added with Object.defineProperty() either at construction time in the base class (before the constructor body runs), or just after super() returns in a subclass.
Your code example is in the second case. The order of execution is thus:
Execute super() in case your class is a subclass
Define the public instance fields on this
Execute the rest of the constructor
If you need a work around, then don't define fullname with the field syntax, but assign to this.fullname as part of the constructor function.
Update fullname to a getter to compute the property value when it is accessed:
class Teacher extends Person {
constructor(name, degree) {
super(name);
this.degree = degree;
}
get fullname() {
return this.degree + this.name;
};
printFullName() {
console.log(this.fullname);
}
}
It sounds like you want fullname to be a property, why not set it in the constructor then?
class Teacher extends Person {
constructor(name, degree) {
super(name);
this.degree = degree;
this.fullname = this.degree + this.name;
}
printFullName() {
console.log(this.fullname);
}
}
Otherwise just change printFullName to do the work for you:
printFullName() {
console.log(this.degree + this.nam);
}
Hopefully that helps!

How do I override a parent's method in Javascript?

let's suppose we have an object Person as follows:
function Person(name){
this.name = name;
this.greeting = function(){
alert("Hi I'm " + this.name);
}
}
and its child
function Teacher(name, subject){
Person.call(this, name);
this.subject = subject;
Teacher.prototype = Object.create(Person.prototype);
}
I tried to override greeting method as follows:
Teacher.prototype.greeting = function(){
alert("Hello my name is " + this.name + " and I teach " + this.subject);
}
but teacher1.greeting() invokes Person's method and not Teacher's one, as you can see here:
Where's the mistake?
UPDATED ANSWER:
Now that I'm home and on a laptop, I see the bug. You set the Teacher prototype in the wrong place.
So you needed to do this:
// Incorrect
function Teacher(first, last, age, gender, interest, subject) {
Person.call(this, first, last, age, gender, interest);
this.subject = subject;
Teacher.prototype = Object.create(Person.prototype);
}
// Correct
function Teacher(first, last, age, gender, interest, subject) {
Person.call(this, first, last, age, gender, interest);
this.subject = subject;
}
Teacher.prototype = Object.create(Person.prototype);
Because of this, every time you instantiated a new instance of Teacher, you would override it’s prototype with Person. So no matter what you set the Teacher's prototype to, it was getting overwritten.
In your first class greeting function on Person function prototype.
If you use this in constructor function, then you refer to newly created object, not to function's prototype. This is correct way if you want to override prototype function:
function Person(name){
this.name = name;
}
Person.prototype.greeting = function(){
alert("Hi I'm " + this.name);
}
You can compare this code with yours by checking value of Person.prototype.greeting.
That's why you couldn't override it with Teacher class, cause greeting function was overwritten by Person constructor every time you create Teacher object and call it's constructor.
The problem is that you define greeting as an instance method in Person. It should be a method of Person.prototype. Why? Because when you reference a property of an instance the interpreter always first checks for existence of instance properties, then the constructor prototype properties of an instance.
So this should work:
const someTeacher = new Teacher("Peter", "Jones", 26, "Male", "Functional Programming", "Math");
someTeacher.greeting();
function Person(first, last, age, gender, interest) {
this.name = {
first,
last
};
this.age = age;
this.gender = gender;
this.interest = interest;
// set the Person.prototype greeting method
// if not already done
if (!Person.prototype.greeting) {
Person.prototype.greeting = function() {
console.log("Hi I'm " + this.name.first);
};
}
}
function Teacher(first, last, age, gender, interest, subject) {
Person.call(this, first, last, age, gender, interest);
this.subject = subject;
// set Teachers prototype to Person
// if not already done
if (!Teacher.prototype.greeting) {
// override [greeting] first. If you do this after
// pointing the prototype to Person, it wil not
// work, probably because in that case you're
// overwriting Person.prototype.greeting which is
// a nogo
Teacher.prototype.greeting = function() {
console.log("Hello my name is " + this.name.first + " and I teach " + this.subject);
};
// set prototype to Person
Teacher.prototype = Person;
}
}

JS inheritance - MDN article

given MDN JS Inheritance article, we have these lines
My question is, why use Object.create and not just Person.prototype?
I understand the need to link prototypes.
But here is console example rendering the call to Object.create in fact not connecting the inherited methods:
Why is that? is it mistake in the article?
Teacher.prototype = Person.prototype
That sets the prototye of the teachers to the same object as the persons prototype. So if you change that:
Teacher.prototype.hi = () => alert("hi");
Then that exists both on teachers and persons:
new Person().hi();
Thats not what you want when creating a subclass. If you do
Teacher.prototype = Object.create( Person.prototype );
You create a new object that inherits the persons prototype. Now the properties do not exist on the object itself, but they are inherited. That getOwnPropertyNames returns nothing does not mean that the properties are not inherited but the opposite: They just don't exist on the object itself, but on its parent.
new Teacher().greeting(); // works :)
The problem with Teacher.prototype = Person.prototype is that then, there is no actual inheritence going on - both prototypes will reference the same object. If you proceed to add a function to Teacher's prototype, for example getClassTaught(), that will mutate Person.prototype, which should not have that method.
function Person(name) {
this.name = name;
}
Person.prototype.getName = function() { return this.name; };
function Teacher(name, className) {
this.name = name;
this.className = className;
}
Teacher.prototype = Person.prototype;
Teacher.prototype.getClassTaught = function() { return this.className; };
const person = new Person();
console.log('getClassTaught' in person);
You also wouldn't be able to shadow Person functions without replacing them entirely. For example, if there's a greeting() function on Person.prototype, and you assign another greeting() function to Teacher.prototype, you'll be overwriting the function on Person.prototype - other persons calling greeting() may not work anymore, because the function is now Teacher-specific, rather than Person-generic.
function Person(name) {
this.name = name;
}
Person.prototype.getName = function() { return this.name; };
Person.prototype.greeting = function() { return 'Hi, I am ' + this.name; };
function Teacher(name, className) {
this.name = name;
this.className = className;
}
Teacher.prototype = Person.prototype;
Person.prototype.greeting = function() { return 'Hi, I am Teacher ' + this.name; };
const person = new Person('Bob');
console.log(person.greeting());
getOwnPropertyNames only shows you the property names directly on the object itself - it does not show inherited property names. When you use Object.create(Person.prototype), greeting is inherited from the Person prototype; it's not directly on Teacher.prototype, so it doesn't show up in getOwnPropertyNames.

right way to make class function in javascript

I have the below code:
function Class () {
this.method = function () {
alert('method');
};
}
new Class().method();
And it works fine, but as I understand, for each new object will be created the function. Is there right way to do this?
​
Place initialization of instance varibales into the Class function, and shared methods and variables in prototype property:
function Class (var1, var2) {
this.var1 = var1;
this.var2 = var2;
}
Class.prototype.method = function () {
alert(this.var1 + ' ' + this.var2);
};
new Class('var1', 'var2').method();
​
My approach is almost identical to Speransky's, but I re-declare the prototype object instead of adding methods directly to it.
// Declare the Constructor function.
function MyClass (name, age) {
// Assign any member properties here.
this._name = name;
this.age = age;
}
// Redefine the prototype object to add methods.
MyClass.prototype = {
// Re-point the Constructor function as it will be overwritten.
constructor: MyClass,
// Custom method which all instances of `MyClass` will inherit.
sayHello: function () {
return "My name is " + this._name + ", how do you do?";
}
};
Usage:
var foo = new MyClass("Dave", 22);
foo.sayHello(); // "My name is Dave, how do you do?"
foo.age; // 22
If you wanted to point the MyClass prototype at another object (to setup a simple inheritance model) then you could make use of a mixin, similar to Underscore's extend method:
function BaseClass(name) {
this._name = name;
}
BaseClass.prototype = {
constructor: BaseClass,
sayHello: function () {
return "My name is " + this._name + ", how do you do?";
}
}
class MyClass (name, age) {
// Call the BaseClass constructor function in the context of `this`
BaseClass.call(this, name);
this.age = age;
}
// Mixin the BaseClass's protptype into the MyClass prototype and delcare
// the new methods.
_.extend(MyClass.Prototype, BaseClass.prototype, {
constructor: MyClass,
getAge: function () {
return this.age;
}
});

Categories

Resources