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?
CodePudding user response:
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!
CodePudding user response:
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.