1、继承
在其他面向对象的语言,不如JAVA
和C++
中都有继承的概念。那么什么是继承呢?用简单的例子来解释一下:你父亲给你留了很多的钱,则你就继承了你父亲的钱。这就是简单的继承。
在javascript
中没函数没有签名,所有无法实现接口继承。但在ECMAScript
中却是支持实现继承的。怎么实现?
1.1、原型链继承
在javascript
中将原型链作为实现继承的主要方法。如果对原型链不理解的,可以阅读我之前的文章https://blog.csdn.net/weixin_42714574/article/details/105642837
基本思想:利用原型让一个引用类型继承另一个引用类型的属性和方法。
让我们简单的回顾构造函数、原型和实例的关系:每个构造函数都有一个原型对象prototype
,原型对象都包含一个指向构造函数的指针prototype.constructor
,而实例都包含一个指向原型对象的内部指针__proto__
。
那么,假如我们让原型对象等于另一个类型的实例,结果会怎么样呢?
//父级
function Super() {
this.Supername = 'super'
}
//父类型原型方法
Super.prototype.getSuperName = function () {
return false
}
// 子类型
function Sub() {
this.Subname = 'sub'
}
// 将父类型的实例指向了子类型的原型
Sub.prototype = new Super() //==> var superInstance = new Super()
// 子类型的原型方法
Sub.prototype.getSubName = function () {
return true
}
//创建子类型实例
var subInstance = new Sub()
console.log(subInstance.getSuperName()) //false
这就是实现了简单继承,创建父类型的实例,那么父类型的实例就拥有了__proto__
指向了父类型的prototype
原型,那么将这个实例的指向赋给了子类型的原型,则子类型原型的指向就变为了父类型的原型。下面进行简单验证:
console.log(Sub.prototype.__proto__) //Super { getSuperName: [Function] }
console.log(superinstance.__proto__) //Super { getSuperName: [Function] }
实现的本质:是重写原型对象,在Sub
构造函数创建的一开始就产生了一个prototype
原型。在继承以后,构造函数的原型不在指向一开始的那一个了,代之一个新类型的实例。
这样我们就可以使用Super.prototype
上的全部方法和属性,在我们给子类型添加了新方法后,则父类型并不能共享,这是子类型独有的。
此外还是要主要构造器的指向,要注意subInstance.constructor
现在的指向是Super
,这是因为原型被重写的原因
console.log(subInstance.constructor) //[Function: Super]
要习惯性的将构造器指向指回原来的构造器
Sub.prototype = new Super()
Sub.prototype.construtor = Sub
console.log(subInstance.constructor) //[Function: Sub]
1.2、同名方法的问题
在此前的例子上Super
的原型上有一个getSuperName
的方法,那么子类型定义一个同名方法会怎么样?
Sub.prototype.getSuperName = function(){
return true
}
var subInstance = new Sub()
var superInstance = new Super()
console.log(subInstance.getSuperName()) // true
console.log(subInstance.getSuperName()) // false
在子类型上定义了一个与父类型同名的方法,那么子类型的方法就会把父类型的同名方法覆盖掉。
那么后定义的getSuperName
方法实际上只是子类型的方法,并不会影响父类型的方法。
1.3、组合继承方法
在之前的文章有写过原型对象和构造函数方法单独使用产生的问题。在这里简单的提一下。
原型链产生的问题:引用类型的原型属性会被所有的实例共享,当一个实例修改了原型属性的值,则所有实例上的值都会改变,这与实际不符。
构造函数模式:** 避免了原型链遇到的问题,但定义的方法很难被复用**
采用两种模式的组合,就很好的解决了上述问题:
一般来说:原型链实现共享的属性和方法的定义,构造函数实现对实例属性的继承
function Super(name) {
this.name = name
this.colors = ['red', 'blue', 'black']
}
Super.prototype.sayName = function () {
console.log(this.name)
}
function Sub(name, age) {
//借调了Super的构造函数,类似于java当中的 super(this)
Super.call(this, name)
this.age = age
}
Sub.prototype = new Super()
Sub.prototype.constructor = Sub
Sub.prototype.sayAge = function () {
console.log(this.age)
}
var instance1 = new Sub('Nicholas', 29)
instance1.colors.push('yellow')
console.log(instance1.colors) //[ 'red', 'blue', 'black', 'yellow' ]
instance1.sayName() //Nicholas
instance1.sayAge() //29
var instance2 = new Sub('Grey', 18)
console.log(instance2.colors) //[ 'red', 'blue', 'black' ]
instance2.sayAge() //18
instance2.sayName() //Grey
通过这种组合继承的方法,很好的解决了冲突问题。创建的两个实例的属性和方法互不影响。