我有以下代码片段代表这个问题的答案:
您正在创建一个名为Consultant的类,该类必须来自Employee类。 Consultant类必须修改继承的PayEmployee方法。必须使用重写方法创建Consultant的未来实例。
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’);
}
我真的无法绕过这个。为什么行标有" X"必要?我不能在没有这些行的情况下编写代码,它会完全一样吗?根据我的测试,它没有任何区别:
有人可以解释一下这两个代码之间的区别是什么,为什么我应该使用另一个代码呢?
提前致谢
答案 0 :(得分:4)
难道我不能在没有这些行的情况下编写代码,它会完全相同吗?
不,在一般情况下你确实需要它们。是的,在某些特定情况下,您可以在没有其中一个或两个的情况下离开。
首先,请注意该代码中有一个常见错误。这是不正确的:
Consultant.prototype = new Employee(); // INCORRECT
应该是:
Consultant.prototype = Object.create(Employee.prototype);
new Employee
做了两件不同的事情:
它创建一个使用Employee.prototype
作为其原型的对象
它运行Employee
中的代码,其作用是通过在Employee
上设置属性等来初始化this
的实例
但我们还不想要第二部分。 Consultant.prototype
不是Employee
实例,它是将成为通过new Consultant
创建的对象原型的对象(其中将是Employee
的实例,也是Consultant
的实例。 (这是JavaScript的构造函数和prototype
属性偏离标准原型继承的地方。您可以在JavaScript中执行标准的原型继承,但是当你这样做时,你不使用构造函数,prototype
属性,或new
。)
Object.create
只执行这两项任务中的第一项:使用Employee.prototype
作为原型创建对象。因此,我们最终在Consultant.prototype
上使用Employee.prototype
作为其原型的对象,但没有调用Employee
并让它运行其每个实例的初始化代码。 (我们稍后会在您标记为X的第二行中执行此操作。)
但是为什么我们不希望Employee
进行每个实例的初始化?因为Consultant.prototype
不是Employee
实例,所以将它初始化为一个实例是没有意义的。首先:如果Employee
需要用于初始化它返回的对象的参数,该怎么办?在构建Consultant.prototype
时我们会传递什么?我们没有任何特定于实例的信息来传递它。
相反,我们只需执行上面的#1(通过Object.create
),然后离开#2(调用Employee
)直到我们构建实例,方法是从{Employee.call(this)
调用Consultant
{1}}(更多信息如下)。
到标记的行:
Employee.call(this);
这一行对于让Employee
有机会对正在创建的实例进行初始化至关重要。没有它,Employee
没有机会完成它的工作:初始化实例的Employee
部分。例如,假设Employee
看起来像这样:
function Employee() {
this.paySchedule = "biweekly";
}
如果Employee.call(this);
中没有Consultant
行,那么通过new Consultant
创建的实例中将缺少该属性。
你的第二个标记线:
Consultant.prototype.constructor = Consultant;
我们需要执行此操作,因为Consultant.prototype
的{{1}}属性应指向constructor
,但如果我们不这样做,则会指向Consultant
。
它过去并不重要,因为虽然规范中定义的Employee
默认情况下是以这种方式设置的,但具体使用它的任何内容都没有。这在ES2015(又名“ES6”)中发生了变化:现在,有时使用constructor
属性(例如,在promises中)。
这是一个示例,说明constructor
具有参数的情况。我还将Employee
切换为PayEmployee
以符合JavaScript中压倒性的惯例,即只有构造函数最初被限制:
payEmployee
最后,如果我没有指出,从ES2015开始,我们会有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();
语法,如果在旧环境中有必要,可以对其进行转换,这将是我的疏忽:
class
您询问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();
是否总是错误的。让我们使用Consultant.prottype = new Employee();
来获取代码的版本,并删除用X标记的两行,并向Employee添加一个看起来应该有效的功能,但最终会导致各种混淆:< / p>
new Employee
问题是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?!
数组在staff
上,而不是每个实例,因此无论我们使用哪个实例,我们总是访问相同的数组。
这就是为什么我们不使用用于初始化实例的函数(例如,构造函数)来初始化构造函数的Consultant.prototype
属性的对象。
我应该注意,在标准的原型继承中,实际创建一个实例作为另一个实例的原型是很常见的。但JavaScript的构造函数及其prototype
属性不是标准的原型继承,它们是基于类的OOP和原型OOP的混合体。您可以在JavaScript中执行纯原型OOP(很多都可以),但是当您这样做时,您不使用构造函数,它们的prototype
属性或prototype
。您使用工厂函数和new
(通常)。