I have the following code snippet which represents the answer to this question:
You are creating a class named Consultant that mustinherit from the Employee class. The Consultant class must modify the inherited PayEmployee method. Future instances of Consultant must be created withthe overridden method.
function Employee() {}
Employee.prototype.PayEmployee = function ( ){
alert(‘Hi there!’);
}
function Consultant () {
Employee.call(this); // X
}
Consultant.prototype = new Employee();
Consultant.prototype.constructor = Consultant; // X
Consultant.prototype.PayEmployee = function ()
{
alert(‘Pay Consultant’);
}
I really can't wrap my head around this. Why are lines marked with "X" necessary? Couldn't I just write the code without these lines and it would be exactly the same? According to my tests it would make no difference:
Can someone explain me what the difference between these two codes is, and what why I should use one over the other?
Thanks in advance
Couldn't I just write the code without these lines and it would be exactly the same?
No, you do need both of them in the general case. Yes, in some specific cases, you can get away without one or both of them.
First off, note that that code has a common error in it. This is incorrect:
Consultant.prototype = new Employee(); // INCORRECT
It should be:
Consultant.prototype = Object.create(Employee.prototype);
new Employee
does two distinct things:
It creates an object that uses Employee.prototype
as its prototype
It runs the code in Employee
, whose job is to initialize an instance of Employee
by setting up properties and such on this
But we don't want that second part yet. Consultant.prototype
isn't an Employee
instance , it's an object that will become the prototype of an object created via new Consultant
(which will be an instance of Employee
, and also of Consultant
). (This is where JavaScript's constructor functions and prototype
property depart from standard prototypical inheritance. You can do standard prototypical inheritance in JavaScript, but when you do, you don't use constructor functions, the prototype
property, or new
.)
Object.create
just does the first of those two tasks: Creates an object using Employee.prototype
as its prototype. So we end up with an object on Consultant.prototype
that uses Employee.prototype
as its prototype, but without calling Employee
and having it run its per-instance initialization code. (We do that later, in the second line you marked with an X.)
But why don't we want Employee
to do its per-instance initialization? Because, again, Consultant.prototype
isn't an Employee
instance, so it doesn't make sense to initialize it as one. For one thing: What if Employee
requires parameters it uses to initialize the object it returns? What would we pass it when constructing Consultant.prototype
? We don't have any instance-specific information to pass it at that point.
So instead, we just do #1 above (via Object.create
), and leave #2 (calling Employee
) until we're constructing an instance, by calling Employee.call(this)
from Consultant
(more below).
To your marked lines:
Employee.call(this);
This line is essential to give Employee
its chance to do its initialization of the instance being created. Without it, Employee
hasn't had a chance to do its job: Initialize the Employee
parts of the instance. For instance, suppose Employee
looks like this:
function Employee() {
this.paySchedule = "biweekly";
}
Without the Employee.call(this);
line in Consultant
, that property would be missing from an instance created via new Consultant
.
Your second marked line:
Consultant.prototype.constructor = Consultant;
We need to do this because Consultant.prototype
's constructor
property should point to Consultant
, but if we don't do this, it will point to Employee
.
It didn't used to matter much, because although constructor
was defined in the specification to be set up this way by default, nothing in the specificaton used to use it. That changed in ES2015 (aka "ES6"): Now, the constructor
property is used sometimes (for instance, in promises).
Here's an example illustrating the situation where Employee
has parameters. I've also switched PayEmployee
to payEmployee
to be in keeping with the overwhelming convention in JavaScript that only constructor functions are initially-capped:
function Employee(name, title) { this.name = name; this.title = title; } Employee.prototype.payEmployee = function() { console.log("Time to pay " + this.name + " (" + this.title + ")"); }; function Consultant(name) { Employee.call(this, name, "Consultant"); } Consultant.prototype = Object.create(Employee.prototype); Consultant.prototype.constructor = Consultant; Consultant.prototype.payEmployee = function() { console.log("Time to pay " + this.name + " (" + this.title + ") -- remember, you're paying the gross, consultants handle their own tax."); }; var e = new Employee("Joe Bloggs", "Engineer"); e.payEmployee(); var c = new Consultant("John Smith"); c.payEmployee();
And finally, I'd be remiss if I didn't point out that as of ES2015, we have class
syntax for this, which can be transpiled if necessary for older environments:
class Employee { constructor(name, title) { this.name = name; this.title = title; } payEmployee() { console.log("Time to pay " + this.name + " (" + this.title + ")"); } } class Consultant extends Employee { constructor(name) { super(name, "Consultant"); } payEmployee() { console.log("Time to pay " + this.name + " (" + this.title + ") -- remember, you're paying the gross, consultants handle their own tax."); } } const e = new Employee("Joe Bloggs", "Engineer"); e.payEmployee(); const c = new Consultant("John Smith"); c.payEmployee();
You asked about whether Consultant.prottype = new Employee();
is always wrong. Let's take a version of the code using new Employee
for that, and removing the two lines you marked with X, and add a feature to Employee that seems like it should work, but ends up leading to all kinds of confusion:
function Employee() { this.staff = []; } Employee.prototype.payEmployee = function() { console.log("Paying employee"); }; function Consultant(name) { } Consultant.prototype = new Employee(); Consultant.prototype.payEmployee = function() { console.log("Time to pay " + this.name + " (" + this.title + ") -- remember, you're paying the gross, consultants handle their own tax."); }; var jill1 = new Employee(); jill1.staff.push("Bob"); jill1.staff.push("Jane"); var john1 = new Employee(); john1.staff.push("Russell"); john1.staff.push("Mohammed"); console.log("Jill's staff (1): " + jill1.staff.join(", ")); console.log("John's staff (1): " + john1.staff.join(", ")); // so far so good, but what if we use Consultant instead? var jill2 = new Consultant(); jill2.staff.push("Bob"); jill2.staff.push("Jane"); var john2 = new Consultant(); john2.staff.push("Russell"); john2.staff.push("Mohammed"); console.log("Jill's staff (2): " + jill2.staff.join(", ")); console.log("John's staff (2): " + john2.staff.join(", ")); // ??? how is it they both have all four staff?!
The problem is that the staff
array is on Consultant.prototype
, not each instance, and so regardless of which instance we use, we're always accessing the same array.
That's why we don't use functions intended to initialize instances (eg, constructor functions) to initialize the object for a constructor function's prototype
property.
I should note that in standard prototypical inheritance, it would be common and normal to actually create an instance to be a prototype of another instance. But JavaScript's constructor functions and their prototype
property are not standard prototypical inheritance, they're a hybrid of class-based OOP and prototypical OOP. You can do pure prototypical OOP in JavaScript (and many do), but when you do, you don't use constructor functions, their prototype
property, or new
. You use factory functions and Object.create
(usually).
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.