原型继承:在子“class”构造函数中调用超级构造函数

时间:2014-01-13 13:45:20

标签: javascript constructor prototype prototypal-inheritance

我试图理解这段代码,并想知道为什么我们在子“class”构造函数中调用超级构造函数:

function Person(name) {
   this.name = name;
}
// Other properties on Person prototype here...

function Employee(id, name) {
    Person.call(this, name);   // Why do we have to do this?
    this.id = id;
}
Employee.prototype = Object.create(Person.prototype);
// Other properties on Employee prototype here...

1)为什么我们有Person.call(this, name)?在“Javascript Ninja的秘密”中,他们执行prototype继承 WITHOUT 调用超级构造函数(其余代码是相同的,除了Object.create,但我理解为什么这是必要的。)

2 个答案:

答案 0 :(得分:3)

所以这是没有行Person.call(this,name)的员工(和人)的原型链:

|Person|                       |Employee|
|------|                       |--------|
|name  |                       |wage    |
|sex   |                       |id      |

   |                               |
   |                               |
   v                               v

|Person Prototype|             |Employee Prototype|
|----------------|             |------------------|
|walk()          |  <--------  |work()            |
|eat()           |             |goOnStrike()      |
|sleep()         |

   |
   |
   v

|Object|
|------|
| ***  |

每当您请求员工的财产时,JavaScript都会沿着原型链向下查找该属性。如果你这样写:

var employee = new Employee(1, "Jack White");
employee.walk();

JavaScript会在employee中查找,而不是employee.[[prototype]],然后在employee.[[prototype]].[[prototype]]中(按照图中的箭头指示),直到找到属性walk

如您所见,如果您请求属性name,JavaScript将无法找到它,因为它不在employee的原型链中。因此,您必须确保“复制”namesex等本地属性。

您可以通过使用当前Person的上下文调用Employee的构造函数来执行此操作:

function Employee(id, name) {
    Person.call(this, name);
    this.id = id;
}

基本上就像你只是从Employee构造函数中的Person构造函数内部复制所有代码一样:

function Employee(id, name) {
    this.name = name; //copied from Person
    this.id = id;
}

这导致以下设置和具有名称属性的员工:

|Person|                       |Employee|
|------|                       |--------|
|name  |                       |wage    |
|sex   |                       |id      |
                               |name    |
                               |sex     |

   |                               |
   |                               |
   v                               v

|Person Prototype|             |Employee Prototype|
|----------------|             |------------------|
|walk()          |  <--------  |work()            |
|eat()           |             |goOnStrike()      |
|sleep()         |

   |
   |
   v

|Object|
|------|
| ***  |

答案 1 :(得分:2)

不调用super的构造函数意味着子类对超类的行为有一些了解:这与封装相反。它不应该知道父类是否需要或不需要在其构造函数中使用任何类型的“设置”。为了安全起见,它应该简单地称之为并继续。

例如,如果Person总是必须在实例化时初始化一堆属性,而Employee 没有调用Person构造函数,那么依赖Person属性/方法的Employee实例操作可能无法正常运行。