Javascript原型构造函数说明

时间:2017-03-28 07:05:57

标签: javascript function oop object constructor

我有以下代码片段代表这个问题的答案:

您正在创建一个名为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"必要?我不能在没有这些行的情况下编写代码,它会完全一样吗?根据我的测试,它没有任何区别:

有人可以解释一下这两个代码之间的区别是什么,为什么我应该使用另一个代码呢?

提前致谢

1 个答案:

答案 0 :(得分:4)

  

难道我不能在没有这些行的情况下编写代码,它会完全相同吗?

不,在一般情况下你确实需要它们。是的,在某些特定情况下,您可以在没有其中一个或两个的情况下离开。

首先,请注意该代码中有一个常见错误。这是不正确的:

Consultant.prototype = new Employee(); // INCORRECT

应该是:

Consultant.prototype = Object.create(Employee.prototype);

new Employee做了两件不同的事情:

  1. 它创建一个使用Employee.prototype作为其原型的对象

  2. 它运行Employee中的代码,其作用是通过在Employee上设置属性等来初始化this的实例

  3. 但我们还不想要第二部分。 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(通常)。