1、原型链
继承
如果想深入了解更多原型的内容,可以参考之前的文章
function Parent(value) {
this.parentVal = 'parent'
}
Parent.prototype.getParentValue = function() {
return this.parentVal
}
function Child() {
this.childVal = 'child'
}
Child.prototype = new Parent()
var child = new Child()
child.getParentValue() // 'parent'
这里 Child 继承了 Parent,实现的本质是重写原型对象 Child.prototype,代之以一个新类型的实例 new Parent()。
so,原来存在于 Parent 的实例中的所有属性和方法,现在也存在于 Child.prototype 中了
关系图如下:
原型链的问题
:
1、包含引用类型值的原型属性
会被所有实例共享
在通过原型来实现继承时,原型实际上会变成另一个类型的实例。于是,原先的实例属性也就顺理成章地变成了现在的原型属性了,这样就可能引起问题,见下🌰:
function Parent(value) {
this.foods = ['apple', 'banana']
}
function Child() {
this.childVal = 'child'
}
Child.prototype = new Parent()
var child1 = new Child()
var child2 = new Child()
child1.foods.push('pear')
child1.foods // ["apple", "banana", "pear"]
child2.foods // ["apple", "banana", "pear"]
在实现原型链的过程中,这里的 foods 属性从Parent的实例属性转化为Child的原型属性了,所以Child的所有实例都会共享foods,一改全改
2、不能传递参数
没有办法在不影响所有对象实例的情况下,给超类型的构造函数传递参数
2、借用构造函数实现继承
在子类型构造函数中通过 call() “借调”父类型的构造函数,可以解决原型中包含引用类型值所带来问题,见下🌰:
function Parent(value) {
this.foods = ['apple', 'banana']
}
function Child() {
//继承了 Parent
Parent.call(this)
}
Child.prototype = new Parent()
var child1 = new Child()
var child2 = new Child()
child1.foods.push('pear')
child1.foods // ["apple", "banana", "pear"]
child2.foods // ["apple", "banana"]
还支持传递参数,见下🌰:
function Parent(name) {
this.name = name
}
function Child() {
//继承了 Parent
Parent.call(this, 'sally')
this.age = 23
}
Child.prototype = new Parent()
var child = new Child()
child.name // 'sally'
child.age // 23
构造函数继承的问题
:
方法都在构造函数中定义,无法实现函数复用。而且,在父类型的原型中定义的方法,对子类型而言也是不可见的,结果所有类型都只能使用构造函数模式
3、组合继承
将原型链和借用构造函数的技术组合到一块,从而发挥二者之长的一种继承模式
function Parent(value) {
this.val = value
}
Parent.prototype.getValue = function() {
console.log(this.val)
}
function Child(value) {
Parent.call(this, value)
}
Child.prototype = new Parent()
const child = new Child(1)
child.getValue() // 1
以上继承的方式核心是在子类的构造函数中通过 Parent.call(this) 继承父类的属性
,然后改变子类的原型为 new Parent() 来继承父类的函数
。
优点: 构造函数可以传参,不会与父类引用属性共享,可以复用父类的函数
缺点: 在继承父类函数的时候调用了父类构造函数,导致子类的原型上多了不需要的父类属性,存在内存上的浪费。
4、寄生组合继承
这种继承方式对组合继承进行了优化
function Parent(value) {
this.val = value
}
Parent.prototype.getValue = function() {
console.log(this.val)
}
function Child(value) {
Parent.call(this, value)
}
Child.prototype = Object.create(Parent.prototype, {
constructor: {
value: Child,
enumerable: false,
writable: true,
configurable: true
}
})
const child = new Child(1)
child.getValue() // 1
以上继承实现的核心就是将父类的原型赋值给了子类,并且将构造函数设置为子类,这样既解决了无用的父类属性问题,还能正确的找到子类的构造函数。