首页 文章

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

提问于
浏览
1

我试图理解这段代码,并想知道为什么我们在子“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) ?在"Secrets of the Javascript Ninja"中,它们执行 prototype 继承 WITHOUT 调用超级构造函数(其余代码是相同的,除了 Object.create ,但我明白为什么需要这样做) .

2 回答

  • 2

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

    |Person|                       |Employee|
    |------|                       |--------|
    |name  |                       |wage    |
    |sex   |                       |id      |
    
       |                               |
       |                               |
       v                               v
    
    |Person Prototype|             |Employee Prototype|
    |----------------|             |------------------|
    |walk()          |  <--------  |work()            |
    |eat()           |             |goOnStrike()      |
    |sleep()         |
    
       |
       |
       v
    
    |Object|
    |------|
    | ***  |
    

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

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

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

    如您所见,如果您请求属性 name ,JavaScript将无法找到它,因为它不在 employee 的原型链中 . 所以你必须确保"copy"本地属性,如 namesex .

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

    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;
    }
    

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

    |Person|                       |Employee|
    |------|                       |--------|
    |name  |                       |wage    |
    |sex   |                       |id      |
                                   |name    |
                                   |sex     |
    
       |                               |
       |                               |
       v                               v
    
    |Person Prototype|             |Employee Prototype|
    |----------------|             |------------------|
    |walk()          |  <--------  |work()            |
    |eat()           |             |goOnStrike()      |
    |sleep()         |
    
       |
       |
       v
    
    |Object|
    |------|
    | ***  |
    
  • 3

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

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

相关问题