# 继承
# 构造函数继承
经典继承:通过call()
来实现继承 (相应的, 你也可以用apply
)。
function Parent(name){
this.name = name;
}
Parent.prototype.age = 330;
Parent.prototype.login = function () {
console.log('用户名字' + this.name)
}
function Child(name){
Parent.call(this, name);
this.type = 'child';
}
const p1 = new Child('Libai')
p1.name; // "Libai"
p1.age; // undefind
p1.login(); // p1.login is not a function
当然,如果继承真地如此简单,那么本文就没有存在的必要了,本继承方法也存在明显的缺陷—— 构造函数式继承并没有继承父类原型上的方法。
# 原型链继承
function Parent(){
this.name = 'Libai';
this.play = [1, 2, 3]
}
Parent.prototype.getName = function () {
console.log(this.name);
}
function Child(){
}
Child.prototype = new Parent()
Child.prototype.constructor = Child;
const p1 = new Child()
p1.getName() // 'Libai'
看似没有问题,父类的方法和属性都能够访问,但实际上有一个潜在的不足。举个例子:
const p1 = new Child();
p1.play.push(4);
console.log(p1.play); // [1, 2, 3, 4]
const p2 = new Child();
console.log(p2.play); // [1, 2, 3, 4]
- 明明我只改变了s1的play属性,为什么s2也跟着变了呢?很简单,因为两个实例使用的是同一个原型对象,引用类型的属性被所有实例共享。
- 在创建 Child 的实例时,不能向Parent传参
# 组合继承
原型链继承和经典继承双剑合璧。
组合式继承是 JS 最常用的继承模式,但组合继承使用过程中会被调用两次:一次是创建子类型的时候,另一次是在子类型构造函数的内部。
function Parent (name) {
this.name = name;
this.colors = ['red', 'blue', 'green'];
}
Parent.prototype.getName = function () {
console.log(this.name)
}
function Child (name, age) {
Parent.call(this, name); // 第一次
this.age = age;
}
Child.prototype = new Parent(); // 第二次
Child.prototype.constructor = Child;
const child1 = new Child('kevin', '18');
child1.colors.push('black');
console.log(child1.name); // kevin
console.log(child1.age); // 18
console.log(child1.colors); // ["red", "blue", "green", "black"]
const child2 = new Child('daisy', '20');
console.log(child2.name); // daisy
console.log(child2.age); // 20
console.log(child2.colors); // ["red", "blue", "green"]
# 寄生组合继承
function Parent (name) {
this.name = name;
this.colors = ['red', 'blue', 'green'];
}
Parent.prototype.getName = function () {
console.log(this.name)
}
function Child (name, age) {
Parent.call(this, name);
this.age = age;
}
Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child;
const child1 = new Child('kevin', '18');
console.log(child1);
这种方式的高效率体现它只调用了一次Parent
构造函数,并且因此避免了在Parent.prototype
上面创建不必要的、多余的属性。
与此同时,原型链还能保持不变;因此,还能够正常使用instanceof
和isPrototypeOf
。
开发人员普遍认为寄生组合式继承是引用类型最理想的继承范式。
# ES6 extends
Class
可以通过extends
关键字实现继承,这比ES5
的通过修改原型链实现继承,要清晰和方便很多。
class Parent {
constructor(name) {
this.name = name;
}
doSomething() {
console.log('parent do something!');
}
sayName() {
console.log('parent name:', this.name);
}
}
class Child extends Parent {
constructor(name, parentName) {
super(parentName);
this.name = name;
}
sayName() {
console.log('child name:', this.name);
}
}
const child = new Child('son', 'father');
child.sayName(); // child name: son
child.doSomething(); // parent do something!
const parent = new Parent('father');
parent.sayName(); // parent name: father