前言
此文章只为记录分享本人的一些浅薄理解,同时希望可以给一些困惑的朋友些许启发。
原型链
说到继承就不得不先说说原型链
构造函数、原型和实例的关系:每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个指向原型对象的内部指针。那么假如我们让原型对象等于另一个类型的实例,结果会怎么样呢?显然,此时的原型对象将包含一个指向另一个原型的指针,相应的,另一个原型中也包含着一个指向另一个构造函数的指针。假如另一个原型又是另一个类型的实例,那么上述关系依然成立,如此层层递进,就构成了实例与原型的链条。这就是所谓原型链的基本概念。(此段出自《JavaScript 高级编程设计》)
总结:原型链是一种关系,原型与实例的一种指向关系,它可以是一级,也可以是多级。
继承的方法
每一个对象都会有个一个 __proto__ 属性,每一个函数都会有个 prototype 属性。
构造函数可以通过 prototype 查找它的原型,对象可以通过 __proto__ 查找它的原型。
1.原型链继承
// 超类
function SuperType () {
this.property = true
}
SuperType.prototype.getSuperValue = function () {
return this.property
}
// 子类
function SubType () {
this.subproperty = false
}
// 继承了 SuperType
SubType.prototype = new SuperType()
原型链继承的缺点:
1、当我们修改继承自父类的属性时,会使其他实例的该属性也一起被修改了。
2、在创建子类型的实例时,不能向超类型的构造函数中传递参数。
2.构造函数继承
解决了原型链继承中修改继承自父类属性时,影响了其他实例的数据问题
// 超类
function SuperType () {
this.colors = ['red', 'blue', 'green']
}
function SubType () {
// 继承了 SuperType
SuperType.call(this)
}
var instance1 = new SubType()
instance1.colors.push('black')
console.log(instance1.colors)
// 'red,blue,green,black'
var instance2 = new SubType()
console.log(instance2.colors)
// 'red,blue,green'
解决了子类型不能向父类型传递参数的问题
// 超类
function SuperType (name) {
this.name = name
}
// 子类型
function SubType () {
SuperType.call(this, 'yzh')
this.age = 18
}
var instance = new SubType()
console.log(instance.name)
// 'yzh'
console.log(instance.age)
// '18'
构造函数继承的缺点:
1、方法都在构造函数中定义,函数无法复用
3.组合继承
组合继承又称为伪经典继承,指的是将原型链和构造函数继承组合到一起,从而发挥二者之长的一种继承模式。
// 父类
function SuperType (name) {
this.name = name
this.colors = ['red', 'blue', 'green']
}
SuperType.prototype.sayName = function () {
console.log(this.name)
}
// 子类
function SubType (name, age) {
// 继承属性
SuperType.call(this, name)
this.age = age
}
// 继承方法
SubType.prototype = new SuperType()
// 此处将 constructor 重新指向了 SubType,否则会指向 SuperType
SubType.prototype.constructor = SubType
SubType.prototype.sayAge = function () {
console.log(this.age)
}
var instance1 = new SubType('ye', 18)
instance1.colors.push('black')
console.log(instance1.colors) // 'red,blue,green,black'
instance1.sayName() // 'ye'
instance1.sayAge() // 18
var instance2 = new SubType('zh', 20)
console.log(instance2.colors) // 'red,blue,green'
instance2.sayName() // 'zh'
instance2.sayAge() // 20
组合继承的缺点:
1、无论什么情况下,都会调用两次超类型构造函数:一次是在创建子类型原型的时候;另一次是在子类型构造函数内部
4.原型式继承
这种方式并没有使用严格意义上的构造函数,先创建一个临时性的构造函数,然后将传入的对象作为这个构造函数的原型,最后返回了这个临时类型的一个新实例。
function object (o) {
function F () {}
F.prototype = o
return new F()
}
var person = {
name: 'yzh',
friends: ['天猫精灵', '小爱同学']
}
var anotherPerson1 = object(person)
anotherPerson1.name = 'zh'
anotherPerson1.friends.push('Siri')
var anotherPerson2 = object(person)
anotherPerson2.name = 'Jhon'
anotherPerson2.friends.push('小艺')
console.log(person) // '天猫精灵,小爱同学,Siri,小艺'
Object.create() 方法规范了原型式继承,此方法将传入的对象属性进行了一次浅拷贝
5.寄生式继承
function createAnother (original) {
var clone = object(original)
clone.sayHi = function () {
console.log('hi')
}
return clone
}
和构造函数模式一样,不能做到函数复用而降低效率
6.寄生组合式继承
核心思想:解决组合继承中调用两次超类型构造函数的问题
// 解决组合继承中第一次调用 SuperType 超类型
function inheritPrototype (subType, superType) {
var prototype = object(superType.prototype) // 创建对象
subType.constructor = subType // 增强对象
subType.prototype = prototype // 指定对象
}
// 超类型
function SuperType (name) {
this.name = name
this.colors = ['red', 'blue', 'green']
}
SuperType.prototype.sayName = function () {
console.log(this.name)
}
// 子类型
function SubType (name, age) {
SuperType.call(this, name)
this.age = age
}
inheritPrototype(SubType, SuperType)
SubType.prototype.sayAge = function () {
console.log(this.age)
}
7.class继承
class继承使用 ES6 新增的语法 extends 来实现继承,它比上述的继承方法更清晰、方便。
// 超类型
class SuperType {
constructor (name) {
this.name = name
this.colors = ['red', 'blue', 'green']
}
sayName () {
console.log(this.name)
}
}
// 子类型
class SubType extends SuperType {
constructor (name, age) {
super(name)
this.name = name
this.age = age
}
sayAge () {
console.log(this.age)
}
}
var instance1 = new SubType('ye', 18)
instance1.colors.push('black')
console.log(instance1.colors) // 'red,blue,green,black'
instance1.sayName() // 'ye'
instance1.sayAge() // 18
var instance2 = new SubType('zh', 20)
console.log(instance2.colors) // 'red,blue,green'
instance2.sayName() // 'zh'
instance2.sayAge() // 20