JavaScript(九)——继承
开始
定义了一些属性的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
的所有成员,同时也包括:
- 一个新的属性,
subject
——这个属性包含了教师教授的学科。 - 一个被更新的
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()
的属性,但是其方法在其原型中,我们并没有继承。
- 在控制台输入以下代码
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
对于对象的属性的获取与修改,我们一般都会使用 Getters
和 Setters
来完成。我们修改 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;
}
}
我们使用 _
去创建一个私有属性。