欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

JavaScript(九)——继承

程序员文章站 2022-03-01 12:38:42
...

开始

定义了一些属性的Person()构造器。

function Person(first, last, age, gender, interests) {
  this.name = {
    first,
    last
  };
  this.age = age;
  this.gender = gender;
  this.interests = interests;
};

所有的方法都定义在构造器的原型上,比如:

Person.prototype.greeting = function() {
  alert('Hi! I\'m ' + this.name.first + '.');
};

比如我们想要创建一个 Teacher 类,就像我们前面在面向对象概念解释时用的那个一样。这个类会继承 Person 的所有成员,同时也包括:

  1. 一个新的属性,subject ——这个属性包含了教师教授的学科。
  2. 一个被更新的 greeting() 方法,这个方法打招呼听起来比一般的 greeting() 方法更正式一点。

定义 Teacher() 构造器函数

我们要做的第一件事是创建一个 Teacher() 构造器——将下面的代码加入到现有代码之下:

function Teacher(first, last, age, gender, interests, subject) {
  Person.call(this, first, last, age, gender, interests);

  this.subject = subject;
}

这在很多方面看起来都和 Person 的构造器很像,但是这里有一些我们从没见过的奇怪玩意—— call() 函数。基本上,这个函数允许您调用一个在这个文件里别处定义的函数。第一个参数指明了在您运行这个函数时想对“ this ”指定的值,也就是说,您可以重新指定您调用的函数里所有“ this ”指向的对象。其他的变量指明了所有目标函数运行时接受的参数。

所以在这个例子里,我们很有效的在 Teacher() 构造函数里运行了 Person() 构造函数(见上文),得到了和在 Teacher() 里定义的一样的属性,但是用的是传送给 Teacher() ,而不是 Person() 的值(我们简单使用这里的 this 作为传给 call()this ,意味着 this 指向 Teacher() 函数)。

在构造器里的最后一行代码简单地定义了一个新的subject属性,这将是教师会有的,而一般人没有的属性。


设置 Teacher() 的原型和构造器引用

上面只是继承了 Person() 的属性,但是其方法在其原型中,我们并没有继承。

  1. 在控制台输入以下代码
Teacher.prototype = Object.create(Person.prototype);

上面我们通过 create 方法用 Person.prototype 生成一个对象,并将其赋值给 Teacher.prototype
2. 但是上面的代码导致了一个问题,即 Teacher 原型的构造器变成了 Person 原型的构造器,这是不对的,通过下面的方式进行修改。

Object.defineProperty(Teacher.prototype, 'constructor', {
    value: Teacher,
    enumerable: false, // so that it does not appear in 'for in' loop
    writable: true });

向 Teacher() 添加一个新的 greeting() 函数

为了完善代码,您还需在构造函数 Teacher() 上定义一个新的函数 greeting() 。最简单的方法是在 Teacher 的原型上定义它—把以下代码添加到您代码的底部:

Teacher.prototype.greeting = function() {
  var prefix;

  if(this.gender === 'male' || this.gender === 'Male' || this.gender === 'm' || this.gender === 'M') {
    prefix = 'Mr.';
  } else if(this.gender === 'female' || this.gender === 'Female' || this.gender === 'f' || this.gender === 'F') {
    prefix = 'Mrs.';
  } else {
    prefix = 'Mx.';
  }

  alert('Hello. My name is ' + prefix + ' ' + this.name.last + ', and I teach ' + this.subject + '.');
};

范例尝试

在控制台输入以下代码创建一个 Teacher() 对象实例。

var teacher1 = new Teacher('Dave', 'Griffiths', 31, 'male', ['football', 'cookery'], 'mathematics');

试一下您的老师实例的属性和方法:

teacher1.name.first;
teacher1.interests[0];
teacher1.subject;
teacher1.greeting();

前面两个进入到从 Person() 的构造器 继承的属性和方法,后面两个则是只有 Teacher() 的构造器才有的属性和方法。


ECMAScript 2015 Classes

ECMAScript 2015 在JavaScript中引入了 class 语法,这让我们更方便地去写一个可以复用的类,并且这与 C++ 和 Java 中的类更加相似。下面这个部分,我们将上面 Person 和 Teacher 的例子从原型继承转到类。

下面是用 class 语法的 Person 样例

class Person {
  constructor(first, last, age, gender, interests) {
    this.name = {
      first,
      last
    };
    this.age = age;
    this.gender = gender;
    this.interests = interests;
  }

  greeting() {
    console.log(`Hi! I'm ${this.name.first}`);
  };

  farewell() {
    console.log(`${this.name.first} has left the building. Bye for now!`);
  };
}
  • constructor() 方法定义了代表 Person 类的构造器函数
  • greeting()farewell() 都是类方法,任何与该类有关的方法都可以定义在 class 里。

然后我们就可以定义对象实例

let han = new Person('Han', 'Solo', 25, 'male', ['Smuggling']);
han.greeting();
// Hi! I'm Han

let leia = new Person('Leia', 'Organa', 19, 'female', ['Government']);
leia.farewell();
// Leia has left the building. Bye for now

class语法的继承

使用 extends 关键字去创建一个子类,去告诉 JavaScript 我们基于的类。

class Teacher extends Person {
  constructor(subject, grade) {
    this.subject = subject;
    this.grade = grade;
  }
}

但这样,运行之前定义 Teacher 对象实例的语句会报如下错误。

Uncaught ReferenceError: Must call super constructor in derived class before
accessing 'this' or returning from derived constructor

这时因为,我们没有调用 Person() 的构造方法。
所以,我们对其使用 super() 操作符进行修改。

class Teacher extends Person {
  constructor(first, last, age, gender, interests, subject, grade) {
    super(first, last, age, gender, interests); // Now 'this' is initialized by calling the parent constructor.
    this.subject = subject;
    this.grade = grade;
  }
}

接下来,实例化 Teacher() 对象就没有什么问题了。

let snape = new Teacher('Severus', 'Snape', 58, 'male', ['Potions'], 'Dark arts', 5);
snape.greeting(); // Hi! I'm Severus.
snape.farewell(); // Severus has left the building. Bye for now.
snape.age // 58
snape.subject; // Dark arts

Getters and Setters

对于对象的属性的获取与修改,我们一般都会使用 GettersSetters 来完成。我们修改 Teacher 类。

class Teacher extends Person {
  constructor(first, last, age, gender, interests, subject, grade) {
    super(first, last, age, gender, interests);
    // subject and grade are specific to Teacher
    this._subject = subject;
    this.grade = grade;
  }

  get subject() {
    return this._subject;
  }

  set subject(newSubject) {
    this._subject = newSubject;
  }
}

我们使用 _ 去创建一个私有属性。