Why constructor is initialised to itself when inheriting - javascript

I found the following code for creating inheritance in Javascript in ES5. In this case, Employee is inheriting from Person.
function Employee(name, title) {
Person.call(this, name); // super(name)
this.title = title;
}
Employee.prototype = Object.create(Person.prototype);
Employee.prototype.constructor = Employee;
Employee.prototype.describe = function() {
return Person.prototype.describe.call(this) // super.describe()
+ ' (' + this.title + ')';
};
I don't understand this line:
Employee.prototype.constructor = Employee;
So Employee is already defined on the first line. Why do we need to create a constructor for it and initialise it to itself? And how does this work?

You're not creating a constructor, just pointing the property constructor at the existing one. If you didn't, it would be misleading because the constructor property inherited by instances created using new Employee would be Person, not Employee:
function Person() {
}
function Employee() {
Person.call(this);
}
Employee.prototype = Object.create(Person.prototype);
var per = new Person();
var emp = new Employee();
console.log(per.constructor === Person); // true
console.log(emp.constructor === Employee); // false?
console.log(emp.constructor === Person); // true?!?!!

Object.create gives you a new object which is linked prototype of Person function. And once you have declared the prototype of Employee, you need to add link constructor property also, thats what is done on line
Employee.prototype.constructor = Employee;

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.

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.

JavaScript __proto__ will affect the original function in the Object?

The function getBooks has been defined in the Author.prototype. But it can't be used in the Author Object. When I am using the __proto__ to inherits the Person property. Why the does the Author Object have no getBooks function? Is it the effect of __proto__?
function Person(name){
this.name = name;
}
function Author(name,books){
Person.call(this,name);
this.books = books;
}
Person.prototype.getName = function(){
return this.name;
}
Author.prototype.getBooks = function() {
return this.books;
}
var john = new Person('John Smith');
var authors = new Array();
authors[0] = new Author('Dustin Diaz',['JavaScript Design Patterns']);
authors[1] = new Author('Ross Harmes',['JavaScript Design Patterns']);
authors[0].__proto__ = new Person();
console.log(john.getName());
console.log(authors[0].getName());
console.log(authors[0].getBooks())
__proto__ is deprecated. Instead assign the prototype of the class to a new instance of the class you're trying to inherit from before adding new prototype methods to that class.
function Author(name, books) {
Person.call(this, name);
this.books = books;
}
Author.prototype = new Person();
Author.prototype.constructor = Author;
Author.prototype.getBooks = function() {
return this.books;
};
JSFiddle demo: https://jsfiddle.net/bkmLx30d/1/
you've changed the prototype of the object here authors[0].__proto__ = new Person();, so your first Author object in authors now has a prototype that's set to a Person() object .
when you do authors[0].getBooks(), authors[0] will search for getBooks() in the prototype but won't find it since Person.prototype doesn't have a getBooks() and thus give an error.

How to define properties subclasses in javascript

I am new to Javascript and am having some trouble finding the right solution. When defining a new subclass with properties, what is the correct/best practice for doing so? I pulled the code below from MDN, but it doesn't talk about how to pass in properties with inheritance. What I need is a superclass and subclass that have properties that are defined during instantiation. The properties of the superclass need to be available to all subclasses and the properties of the subclass will only belong to the subclass. Can someone please point me in the right direction?
// define the Person Class
function Person() {}
Person.prototype.walk = function(){
alert ('I am walking!');
};
Person.prototype.sayHello = function(){
alert ('hello');
};
// define the Student class
function Student() {
// Call the parent constructor
Person.call(this);
}
// inherit Person
Student.prototype = new Person();
// correct the constructor pointer because it points to Person
Student.prototype.constructor = Student;
// replace the sayHello method
Student.prototype.sayHello = function(){
alert('hi, I am a student');
}
// add sayGoodBye method
Student.prototype.sayGoodBye = function(){
alert('goodBye');
}
var student1 = new Student();
student1.sayHello();
student1.walk();
student1.sayGoodBye();
// check inheritance
alert(student1 instanceof Person); // true
alert(student1 instanceof Student); // true
Instance variables can be accessed using the this keyword. You can and should initialize instance variables in the constructor:
function Person(name) {
this.name = name;
}
var bob = new Person("Bob");
alert(bob.name);
jsFiddle
When you subclass, the instance variables of the superclass will automatically be available to the subclass.

How Do I Call An Inherited JavaScript Constructor With Parameters?

Greetings,
After reading the following article I have a question:
https://developer.mozilla.org/en/Introduction_to_Object-Oriented_JavaScript
In the inheritance example, the Person constructor doesn't take any parameters. How would this same example look if I were to add one and call it from the Student constructor?
Thanks!
Well, a way that you can re-use the logic of the Person constructor is invoking it with call or apply, for example:
function Person(gender) {
this.gender = gender;
}
function Student(gender) {
Person.apply(this, arguments);
}
Student.prototype = new Person(); // make Student inherit from a Person object
Student.prototype.constructor = Student; // fix constructor property
var foo = new Student('male');
foo.gender; // "male"
foo instanceof Student; // true
foo instanceof Person; // true
If you want to prevent the execution of the Person constructor when is called without arguments (like in the line: Student.prototype = new Person();), you can detect it, e.g.:
function Person(gender) {
if (arguments.length == 0) return; // don't do anything
this.gender = gender;
}
Accepted answer seems to be incorrect. Based on what Mozilla says about OO JavaScript, correct way to do it is:
var Person = function(firstName) {
this.firstName = firstName;
};
function Student(firstName, subject) {
// Call the parent constructor, making sure (using Function#call)
// that "this" is set correctly during the call
Person.call(this, firstName);
// Initialize our Student-specific properties
this.subject = subject;
};
// Create a Student.prototype object that inherits from Person.prototype.
// Note: A common error here is to use "new Person()" to create the
// Student.prototype. That's incorrect for several reasons, not least
// that we don't have anything to give Person for the "firstName"
// argument. The correct place to call Person is above, where we call
// it from Student.
Student.prototype = Object.create(Person.prototype); // See note below
// Set the "constructor" property to refer to Student
Student.prototype.constructor = Student;
// Example usage:
var student1 = new Student("Janet", "Applied Physics");
As you can clearly see, Mozilla specifies that it is a common error to use "new Person()" to create the Student.prototype. Hence accepted answer is misleading.
I have actually tested this in my ongoing project and Mozilla's way is correct, while above answer does not work.
// define the Person Class
function Person(name) {
this.personname = name;
}
Person.prototype.walk = function(){};
Person.prototype.sayHello = function(){
alert (this.personname );
};
https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/constructor
The full code:
<script>
// define the Person Class
function Person(name) {
this.personname = name;
}
Person.prototype.walk = function(){};
Person.prototype.sayHello = function(){
alert (this.personname );
};
// define the Student class
function Student() {}
// inherit Person
Student.prototype = new Person("test");
// correct the constructor pointer because it points to Person
Student.prototype.constructor = Student;
// replace the sayHello method
Student.prototype.sayHello = function(){
alert('hi, I am a student and my name is \'' + this.personname + '\'' );
}
// add sayGoodBye method
Student.prototype.sayGoodBye = function(){
alert('goodBye');
}
var student1 = new Student();
student1.sayHello();
student1.sayGoodBye();
</script>
By all other comments I created an example which works for me. Since I did not use prototype explicitly I hope I am not missing an important point.
// variable for tracking instantiations and checking the uniqueness of the objects
var instances = 0;
var Generic = function() {
this.instanceId = ++instances;
this.toString = function() {return 'Generic [iid='+ this.instanceId +']'};
console.log('constructor-invoke: Generic ('+ this.instanceId +')');
};
var SpecificName = function(inName) {
Generic.call(this);
this.getName = function() { return inName; };
var superToString = this.toString.bind(this); // binds the inner function 'this' to this SpecificName instance
this.toString = function() {
return 'SpecificName [iid='+ this.instanceId +', name='+ this.getName() +', super.toString='+ superToString() +']'
}
console.log('constructor-invoke: SpecificName ('+ this.instanceId +')');
};
var SpecificNames = function(inFirstName, inLastName) {
SpecificName.call(this, inLastName +', '+ inFirstName );
var superToString = this.toString.bind(this);
this.toString = function() {
return 'SpecificNames [iid='+ this.instanceId +', name='+ this.getName() +', super.toString='+ superToString() +']'
}
console.log('constructor-invoke: SpecificNames ('+ this.instanceId +')');
};
var g = new Generic();
var sn = new SpecificName('Run Forest Run');
var sns = new SpecificNames('Forest','Gump');
console.log('g: '+ g.toString());
console.log('sn: '+ sn.toString());
console.log('sns: '+ sns.toString());
leads to this output:
constructor-invoke: Generic (1)
constructor-invoke: Generic (2)
constructor-invoke: SpecificName (2)
constructor-invoke: Generic (3)
constructor-invoke: SpecificName (3)
constructor-invoke: SpecificNames (3)
g: Generic [iid=1]
sn: SpecificName [iid=2, name=Run Forest Run, super.toString=Generic [iid=2]]
sns: SpecificNames [iid=3, name=Gump, Forest, super.toString=SpecificName [iid=3, name=Gump, Forest, super.toString=Generic [iid=3]]]

Categories

Resources