文章目录
原型链继承 | 构造函数继承 | 组合继承(原型链+构造函数)) | 原型式继承 | 寄生式继承 | 寄生组合继承 |
---|---|---|---|---|---|
Child.prototype = new Parent() | function Child() {ParentParent.call(this)} | ![]() | ![]() | ![]() | ![]() ![]() |
子类所有实例都会共享父类属性 | 方法都在构造函数中定义,函数无法复用 | 调用两次父类函数 | 必须要有一个函数以另一个函数为基础 | 新对象有person的属性和方法。还有自己的方法 | 解决组合模式调用两次父类函数的问题 |
一、原型链
1. 原型关系
函数通过—prototype–指向原型对象
实例对象/原型对象–proto—指向该对象的原型
原型对象—constructor–指向构造函数
2. 确定对象之间的关系
- instanceof
son.instanceof.father //true
- isPrototypeOf
father.prototype.isPrototypeOf(son) // true
- Object.getPrototypeOf这个方法返回[[Prototype]]的值,可以获取到一个对象的原型
Object.getPrototypeOf(son) // father.prototype
二、继承
1. 原型链继承
让一个原型对象–>另一个类型的实例
方法:通过创建父类实例,并将其赋给子类的原型
function Parent() { //创建父类构造函数
this.property = true
}
Parent.prototype.getParentValue = function () { //在父类构造函数上定义方法
return this.property
}
function Child() { //创建子类构造函数
this.Childproperty = false
}
Child.prototype = new Parent() //new Parent()创建父类的实例对象,重写了子类的原型对象
Child.prototype.getChildValue = function () { //创建子类的原型方法
return this.Childproperty
}
var instance = new Child() //创建子类的实例
console.log(instance.getParentValue()) // true
存在的问题:
function Parent() {
this.colors = [‘red’, ‘blue’, ‘green’]
}
function Child() {
}
Child.prototype = new Parent()
var instance1 = new Child()
var instance2 = new Child()
instance1.colors.push(‘black’)
console.log(instance1.colors) // [“red”, “blue”, “green”, “black”]
console.log(instance2.colors) // [“red”, “blue”, “green”, “black”]
从上面代码可以看出,
- 每个子类的实例对象都可以访问父类的原型方法;
- 若其中一个子类实例改变了父类的原型对象,所有的实例都会改变
综上:所有子类实例都会共享父类的属性
2. 构造函数继承
为了解决原型链继承中,子类实例共享父类属性的问题
**方法:**在子类的构造函数中,使用apply/call来调用父类构造函数,改变对象执行的上下文
function Parent() {
this.colors = [‘red’, ‘blue’, ‘green’]
}
function Child() {
// 继承Parent
Parent.call(this)
}
var instance1 = new Child() //创建child对象实例时,就会进行初始化
var instance2 = new Child()
instance1.colors.push(‘black’)
console.log(instance1.colors) // [“red”, “blue”, “green”, “black”]
console.log(instance2.colors) // [“red”, “blue”, “green”]
可以看出每个对象实例都会有自己的属性副本
拓展:传递参数
function Parent(name) {
this.name = name
}
function Child() {
// 继承Parent
Parent.call(this, ‘Jiang’)
this.job = ‘student’
}
var instance = new Child()
console.log(instance.name) // Jiang
console.log(instance.job) // student
存在的问题:
方法都在构造函数中定义,函数无法达到复用
3. 组合继承(原型链+构造函数)
方法:
- 使用原型链实现对原型属性和方法的继承
- 借用构造函数实现对实例属性的继承
function Parent(name) { //定义父类构造函数
this.name = name
this.colors = [‘red’, ‘blue’, ‘green’]
}
Parent.prototype.sayName = function () { //父类原型方法
console.log(this.name)
}
// *1
function Child(name, job) { //继承父类
// 继承属性
Parent.call(this, name)
this.job = job
}
// *2
// 继承方法
Child.prototype = new Parent()//将父类的实例赋给子类的原型对象
Child.prototype.constructor = Parent
Child.prototype.sayJob = function() { //子类的原型方法
console.log(this.job)
}
var instance1 = new Child(‘Jiang’, ‘student’)
instance1.colors.push(‘black’)
console.log(instance1.colors) //[“red”, “blue”, “green”, “black”]
instance1.sayName() // ‘Jiang’
instance1.sayJob() // ‘student’
var instance2 = new Child(‘J’, ‘doctor’)
console.log(instance2.colors) // //[“red”, “blue”, “green”]
instance2.sayName() // ‘J’
instance2.sayJob() // ‘doctor’
4. 原型式继承
方法:借助原型基于已有对象创建新对象
function object(o) {
function F() {} //1. 创建新临时构造函数
F.prototype = o //2. 将传进来的对象作为这个构造函数的原型对象
return new F() //3. 返回这个对象的实例对象
}
再次声明:该函数返回一个实例对象,而这个对象是以传进来的对象为原型对象的实例对象
从本质上来说:该函数对传进来的对象执行了一次浅复制
var person = {
name: ‘Jiang’,
friends: [‘Shelby’, ‘Court’]
}
var anotherPerson = object(person)//创建anotehPerson为一个实例对象且他的原型对象为person
console.log(anotherPerson.friends) // [‘Shelby’, ‘Court’]
如下所示:可以看出在这个函数中临时创建了一个构造函数,但是最终返回的是这个函数的实例对象
这种方法必须要求有一个对象作为另一个对象的基础
- person作为原型对象
- anotherPerson作为实例对象
拓展:
Object.create()方法,来自ES5,规范了原型式继承
- 参数一:一个对象(作为新对象的原型对象)
- 参数二:对象(定义额外属性)可选
var person = {
name: ‘Jiang’,
friends: [‘Shelby’, ‘Court’]
}
var anotherPerson = Object.create(person)
console.log(anotherPerson.friends) // [‘Shelby’, ‘Court’]
5. 寄生式继承
寄生继承的思路和工厂模式很像(下边会介绍创建对象的模式)
即:创建一个用于封装继承过程的函数
function createAnother(o) {
var clone = Object.create(o) // 创建一个新对象实例
clone.sayHi = function() { // 给这个实例对象添加方法
console.log(‘hi’)
}
return clone // 返回这个对象
}
var person = {
name: ‘Jiang’
}
var anotherPeson = createAnother(person)
anotherPeson.sayHi()
由上面代码可以看出,这个新对象不仅有person的属性和方法,还拥有自己的方法
6. 寄生组合继承
回顾一下组合模式(原型链+构造函数):通过借用构造函数来继承属性,通过原型链来继承方法
function Parent(name) { //父类构建
this.name = name
this.colors = [‘red’, ‘blue’, ‘green’]
}
function Child(name, job) {
// 继承属性
Parent.call(this, name) // 1. 调用父类构造函数
this.job = job
}
// 继承方法(将子类的原型指向父类的实例)
Child.prototype = new Parent() // 2.调用父类构造函数
var instance = new Child()
//当创建子类实例时,其实会产生两组name和color属性,一组在实例上,一组在child原型上,只不过实例屏蔽了原型上的
所以,寄生模式就是为了规避这种问题
方法:使用寄生模式来继承父类的原型,将结果指定给子类的原型
(指定子类原型时,不再去调用父类的构造函数;我们要的无非是父类原型的一个副本)
function inheritPrototype(Child, Parent) {
var prototype = Object.create(Parent.prototype)//创建一个父类原型的副本
prototype.constructor = Child //给子类添加constructor属性
Child.prototype = prototype //让子类的原型指向这个副本
}
这个函数的作用就是将子类的原型通过这个原型副本和父类原型链接起来了
function Parent(name) {
this.name = name
this.colors = [‘red’, ‘blue’, ‘green’]
}
Parent.prototype.sayName = function () {
console.log(this.name)
}
function Child(name, job) {
// 继承属性
Parent.call(this, name)
this.job = job
}
// 继承
inheritPrototype(Child, Parent)//将子类的原型和父类的原型链接起来
var instance = new Child(‘Jiang’, ‘student’)
instance.sayName()
补充:
可以直接使用Object.create来实现,其实就是将上面封装的函数拆开,这样演示可以更容易理解
function Parent(name) {
this.name = name
this.colors = [‘red’, ‘blue’, ‘green’]
}
Parent.prototype.sayName = function () {
console.log(this.name)
}
function Child(name, job) {
// 继承属性
Parent.call(this, name)
this.job = job
}
// 继承
Child.prototype = Object.create(Parent.prototype) //将子类原型和父类原型链接起来
// 修复constructor
Child.prototype.constructor = Child//可以理解为这一步为child原型和child构造函数创建关联
var instance = new Child(‘Jiang’, ‘student’)
instance.sayName()
ES6新增方法,可以直接创建关联,而不用手动添加constructor属性
// 继承
Object.setPrototypeOf(Child.prototype, Parent.prototype)
console.log(Child.prototype.constructor === Child) // true
参考文献:https://xxxgitone.github.io/2017/06/12/JavaScript六种继承方式/