JS inheritance - MDN article - javascript

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.

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.

Why constructor is initialised to itself when inheriting

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;

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.

Why use prototype for methods instead of this.methodName [duplicate]

This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
Advantages of using prototype, vs defining methods straight in the constructor?
Why use:
Person.prototype.toString = function() { return this.name; }
over
Function Person(name) {
this.name = name;
this.toString = function() {
return this.name;
}
}
Well, you should use prototypes because of code reuse and inheritance.
Basically, if you bind a method to the this keyword, you are providing that method to only that particular instance, while with prototype, you write the method for all instances of that class.
ex:
function Person(name) {
this.name = name;
this.toString = function() {
return this.name;
}
}
var bob = new Person('Bob');
var jim = new Person('Jim');
jim.toString = function() {
return "I have amnesia, I forgot my name!";
};
Now, although bob and jim are both persons (instances of the same class), they behave differently, because they have their own set of rules (methods) that they rely on.
If you were to use a prototype:
function Person(name) {
this.setName(name);
}
Person.prototype = {
name : 'default name',
setName : function(name) {
this.name = name;
},
toString : function() {
return this.name;
}
};
var bob = new Person('Bob');
var jim = new Person('Jim');
Person.prototype.toString = function() {
return "I have amnesia, I forgot my name!";
};
Now, all of your persons behave the same.
Using prototypal inheritance is benefic for code reuse and it won't load the memory with unnecessary duplicate things. + Updating classes is muuuch more easy this way.
One reason is because it will update/add that function to objects of that type that have already been created.
function Person(name) {
this.name = name;
}
var person = new Person("Test");
alert(person.toString()); // alerts [object Object]
Person.prototype.toString = function() {
return this.name;
};
alert(person.toString()); // alerts Test
http://jsfiddle.net/28puy/
In javascript, methods are objects. In your second Person constructor, you're creating a new instance of the toString function for each instance of Person. By using the prototype object, there is just one instance of the toString function that will be shared among all instances of Person.

Javascript: the variable from [user-defined prototype] is not available

I play javascript with the book Professional JavaScript for Web Developers. I practice an example in section 6.2.6. the codes are listed below:
function creatPrototype(subType, superType)
{
function subTypePrototype(){};
subTypePrototype.prototype = superType.prototype;
subTypePrototype.constructor = subType;
subTypePrototype.str = "say";
return new subTypePrototype();
}
function Person(name, age)
{
this.name = name;
this.age = age;
}
Person.prototype.say = function(){
writeln("bill say");
}
function itMan(name, age){
Person.apply(this, arguments);
}
itMan.prototype = creatPrototype(itMan, Person);
var Bill = new itMan("bill", 25);
writeln(itMan.prototype.str); //expect "say"
writeln(Person.prototype == itMan.prototype.prototype); //expect true
Bill.say(); //expect "bill say"
the result is:
undefined
False
bill say
Why?
itMan.prototype.str is suppose to "say"
Person.prototype AND itMan.prototype.prototype should point to a same object
Bill.say() run correctly, so the prototype chain is OK.
You have to think about which property belongs to the constructor function and which one belongs to the instance. prototype is a property of the function, but constructor and str should be both properties of the instance.
This should do it:
function createPrototype(subType, superType)
{
function subTypePrototype(){};
subTypePrototype.prototype = superType.prototype;
var newPrototype = new subTypePrototype();
newPrototype.constructor = subType;
newPrototype.str = "say";
return newPrototype;
}
But, as you are also passong subType, you can actually assign the prototype directly:
function inherit(subType, superType)
{
function tconstr(){};
tconstr.prototype = superType.prototype;
subType.prototype = new tconstr();
subType.prototype.constructor = subType;
subType.prototype.str = "say";
}
and then just call it with
inherits(itMan, Person);
Person.prototype AND itMan.prototype.prototype should point to a same object
Remember that prototype is a property of a function, not of objects. But itMan.prototype is a an object. You cannot access an objects prototype unless you explicitly refer to it (but I would not do so).
With ECMAScript 5, there is a way to get the prototype, using Object.getPrototypeOf [MDN]. This only works in newer browsers though.
Here is a working example of your code.
There were some mistake with the code, try this code
function creatPrototype(subType, superType)
{
function subTypePrototype(){
this.prototype = superType.prototype;
this.constructor = subType;
this.str = "say";
}
return new subTypePrototype();
}
function Person(name, age)
{
this.name = name;
this.age = age;
}
Person.prototype.say = function(){
document.writeln("bill say");
}
function itMan(name, age){
Person.apply(this, arguments);
}
itMan.prototype = creatPrototype(itMan, Person);
var Bill = new itMan("bill", 25);
document.writeln(itMan.prototype.str); //expect "say"
document.writeln(Person.prototype == itMan.prototype.prototype); //expect true
Bill.prototype.say(); //expect "bill say"
In your code, you were not using this object so itMAn had no variable str,
you were using
subTypePrototype.constructor = subType;
and subTypePrototype.prototype = superType.prototype;
therefore Bill.say was working and Person.prototype == itMan.prototype.prototype was not working.
Prototype is a reserved keyword and you have to be very careful while using it
Articles on prototype keyword
You have several issues in your code.
Then within your createSubPrototype :
return new subTypePrototype();
You are creating a function and then you are returning the executed function result where you should return a pointer to your function like
return subTypePrototype;
But you should not create a function cause it seems you want to retrieve parent "prototype" :)
So indeed your code should look more like :
function createPrototype(subType, superType)
{
subTypePrototype = superType.prototype;
subTypePrototype.constructor = subType; //- beware this is wrong !!!
subTypePrototype.str = "say";
return subTypePrototype;
}
Check the line I have marked as wrong, by doing so you are updating both sub & parent type
How to do it :
That being said if you want to extend an object I would advise you to use existing libs. (jQuery, Mootools, etc ). Here a sample on how to do it properly

Categories

Resources