# 原型式的继承

(寄生组合继承/经典继承)

参考资料:MDN (opens new window)

如何创建一个继承自另外一个JavaScript对象的对象呢?

# 1.Person()构造器

首先我们先定义一个定义了一些属性的Person()构造器

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

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

Person.prototype.drink = function () {
	console.log(this.name + '正在喝水');
}

接下来我们需要创建一个Student类,Student类会继承Person的所有成员,且含有新的属性与更新drink()方法

# 2.Student()构造器

function Student(name,age,gender,subject){
	Person.call(this,name,age,gender);
    // .call()方法调用一个函数,其中具有一个指定的this值和分别地提供的参数
    // 通过这个方法调用父构造函数。在一个子构造函数中,可以通过调用父构造函数的call方法实现继承。
    this.subject = subject;
}

到这里,只是调用了Person的构造函数,得到了和在Student()里定义的一样的属性,且传递给了Student();

可是,以上操作只是重新定义了一次属性,并不是将他们从Person()中继承过来。

而且会发现,Student()无法调用drink方法

Student()的原型是Object

我们需要设置Student()的原型和构造器引用

# 3.Student()的原型和构造器引用

Student.prototype = Object.create(Person.prototype);

我们不妨尝试查看一下,在没有加这一段代码之前,执行下列代码的运行结果。

var s1 = new Student('张三',18,'男','犯罪心理学');
console.log(s1.__proto__)

在添加前的结果,是{ constructor:f }

而添加后的结果是: Person {}

在这里 create()方法,创建了一个和Person.prototype一样的新的原型属性值,这个属性指向一个包含属性和方法对象,然后将其作为Student.prototype的属性值,这样便意味着Student.prototype现在会继承Person.prototype的所有属性和方法。

现在,又有一个新的问题

console.log(Student.prototype.constructor)

输出的结果是什么?

答案是:变成了Person,应该是Student的,因为我们将其prototype改变了之后,变成了Person,这会有不好的影响

我们还需要将其重新指向Student

所以,我们还需要加上一段代码:

Student.prototype.constructor = Student

这样,我们就可以让Student的原型是Person,但是其构造函数依旧是Student了

# 4.注:

每一个函数对象(Function)都有一个prototype属性,并且只有函数对象有prototype属性,因为prototype本身就是定义在Function对象下的属性。

当我们输入类似 var person1 = new Person(...) 来构建对象是,JavaScript 实际上参考的是 Person.prototype指向的对象来生成person1

另外,Person()函数,是Person.prototype构造函数即:

Person === Person.prototype.constructor

在定义新的构造函数 Student 时,可以通过 function.call来调用父类的构造函数,但是这样子不能自动指定Student.prototype的值,这样Student.prototype就只能包含在构造函数里的属性,所以也就无法调用Person构造函数原型里的方法。

因此利用Object.create()方法将Person.prototype作为Student.prototype的原型对象,并且重新调整构造器的指向,使其与Student关联

这样子便实现了二者的继承关系

任何想要被继承的方法都应该定义在构造函数的prototype对象里,并且永远使用父类的prototype来创建子类的prototype,这样子才不会打乱继承结构。

# 5.Student()添加一个新的drink()方法

Student.prototype.drink = function () {
	console.log('学习' + this.subject + '的' + this.name + '正在喝水');
};

# 6.总结:

// 定义 Person 构造器
function Person (name,age) {
this.name = name;

this.age = age;
};
// drink()方法需要定义在 Person构造器的原型上
Person.prototype.drink = function () {
    console.log(this.name + '正在喝水');
};
// 定义 Student 构造器,它是继承于 Person
function Student (name,age,subject) {
    Person.call(this,name,age);		// 使用call()方法调用父构造函数
	this.subject = subject;
}

// 到这,只是调用了Person的构造函数,得到一样的属性
// 但这里只是重新定义了一次属性,而非继承自 Person
// 且会发现,通过Student()构造器构造出来的对象,无法调用drink()方法
// 这是因为,Student 的 原型链上 并没有 Person
// 两者并没有形成真正意义上的继承
// 因此,我们的下一步操作便是设定 Student的原型和构造器的引用

Student.prototype = Object.create(Person.prototype)

// Object.create() 方法 创建一个新的对象,使现有的对象来提供新创建的对象的__proto__
// 这样子,Student的原型也指定了,但是这里又有一个问题
// 我们把Person的构造函数也指定给了Student的构造函数
// 所以我们还需重新指定 Student的构造函数

Student.prototype.constructor = Student

// 到这 Student 继承于 Person 的操作算是真正意义上完成了
// 接下来我们更新一下 drink方法:
Student.prototype.drink = function () {
    console.log('正在上' + this.subject + '的' + this.name + '正在喝水');
}
function Person(name,age){
	this.name = name;
    this.age = age;
}
Person.prototype.drink = function(){
    console.log(this.name + '喝水中');
}

function Student(name,age,subject){
    Person.call(this,name,age);
    this.subject = subject;
}

Student.prototype = Object.create(Person.prototype)
Student.prototype.constructor = Student
Student.prototype.drink = function () {
    console.log('正在上' + this.subject + '的' + this.name + '正在喝水');
}

更新时间: 6/24/2021, 8:05:43 PM