前面总结了一下js数组、字符串身上的方法。
这篇来总结一下js中继承的方法
1. 原型链继承
原型链继承是通过将子类型的原型设置为父类型的实例来实现的。所有实例共享父类型的引用属性,但也因此存在共享数据导致的问题。
function Parent() {
this.name = 'Parent';
this.colors = ['red', 'blue', 'green'];
}
Parent.prototype.sayName = function() {
console.log(this.name);
};
function Child() {}
Child.prototype = new Parent();
let child1 = new Child();
child1.colors.push('black');
console.log(child1.colors); // ["red", "blue", "green", "black"]
child1.sayName(); // Parent
let child2 = new Child();
console.log(child2.colors); // ["red", "blue", "green", "black"] 共享了colors数组
这种方式的关键是这段代码
Child.prototype = new Parent(),创建Parent的实例,然后赋值给Child.prototype.
缺点:子类型实例共享父类型的引用属性,修改一个实例的属性会影响其他实例。此外,无法向父类型传递参数。
2. 借用构造函数(经典继承)
通过在子类型的构造函数中调用父类型的构造函数,可以实现父类型的属性继承。每个实例都有独立的属性,但无法继承父类型原型上的方法。
function Parent(name) {
this.name = name;
this.colors = ['red', 'blue', 'green'];
}
function Child(name) {
Parent.call(this, name); // 借用Parent的构造函数
}
let child1 = new Child('child1');
child1.colors.push('black');
console.log(child1.colors); // ["red", "blue", "green", "black"]
let child2 = new Child('child2');
console.log(child2.colors); // ["red", "blue", "green"]
这里主要是在子构造函数Child中通过Parent.call(this, name)调用了父构造函数Parent,这样使得Child的实例能够拥有自己的name属性和独立的colors数组,而不是简单地从Parent的原型链上继承这些属性,从而确保了每个Child实例的数据独立性,避免了实例间的属性污染。
缺点:无法继承父类型原型链上的方法。
3. 组合继承(原型链 + 借用构造函数)
组合继承结合了原型链继承和借用构造函数的优点,通过原型链继承方法,通过构造函数继承属性。
function Parent(name) {
this.name = name;
this.colors = ['red', 'blue', 'green'];
}
Parent.prototype.sayName = function() {
console.log(this.name);
};
function Child(name, age) {
Parent.call(this, name); // 第二次调用Parent构造函数
this.age = age;
}
Child.prototype = new Parent(); // 第一次调用Parent构造函数
Child.prototype.constructor = Child;
let child1 = new Child('child1', 18);
child1.colors.push('black');
console.log(child1.colors); // ["red", "blue", "green", "black"]
child1.sayName(); // "child1"
let child2 = new Child('child2', 20);
console.log(child2.colors); // ["red", "blue", "green"]
child2.sayName(); // "child2"
缺点:Parent 构造函数被调用了两次,造成了多余的属性存在于子类型的原型链上。
4. 原型式继承
原型式继承通过对一个已有对象进行浅复制来实现。
function object(o) {
function F() {}
F.prototype = o;
return new F();
}
let parent = {
name: 'Parent',
colors: ['red', 'blue', 'green']
};
let child = object(parent);
child.name = 'Child';
child.colors.push('black');
console.log(child.name); // "Child"
console.log(child.colors); // ["red", "blue", "green", "black"]
let child2 = object(parent);
console.log(child2.colors); // ["red", "blue", "green", "black"]
核心实现是function object(o) {}利用一个空对象作为中介,将某个对象直接赋值给空对象构造函数的原型。
缺点:与原型链继承相同,共享引用类型属性。
5. 寄生式继承
寄生式继承通过创建一个封装继承过程的函数,对对象进行扩展并返回该对象。
function createAnother(original) {
let clone = Object.create(original); // 创建新对象
clone.sayHi = function() {
console.log('Hi');
};
return clone;
}
let parent = {
name: 'Parent',
colors: ['red', 'blue', 'green']
};
let child = createAnother(parent);
child.sayHi(); // "Hi"
缺点:无法实现函数的复用,每次都要重新定义方法。
6. 寄生组合继承
寄生组合继承通过借用构造函数继承属性,通过原型链继承方法,但避免了多余属性的创建。
function inheritPrototype(child, parent) {
let prototype = Object.create(parent.prototype); // 创建父类原型的副本
prototype.constructor = child;
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, age) {
Parent.call(this, name);
this.age = age;
}
inheritPrototype(Child, Parent);
let child1 = new Child('child1', 18);
child1.colors.push('black');
console.log(child1.colors); // ["red", "blue", "green", "black"]
child1.sayName(); // "child1"
let child2 = new Child('child2', 20);
console.log(child2.colors); // ["red", "blue", "green"]
child2.sayName(); // "child2"
优点:避免了组合继承中多余属性的创建,且保留了借用构造函数的优点。
7. ES6 类继承
在 ES6 中,引入了 class 关键字,使得继承的实现变得更加直观和简洁。类继承通过 extends 关键字来实现,子类可以通过 super() 调用父类的构造函数。
代码示例:
javascript
复制代码
class Parent {
constructor(name) {
this.name = name;
this.colors = ['red', 'blue', 'green'];
}
sayName() {
console.log(this.name);
}
}
class Child extends Parent {
constructor(name, age) {
super(name); // 调用父类的构造函数
this.age = age;
}
sayAge() {
console.log(this.age);
}
}
let child1 = new Child('child1', 18);
child1.colors.push('black');
console.log(child1.colors); // ["red", "blue", "green", "black"]
child1.sayName(); // "child1"
child1.sayAge(); // 18
let child2 = new Child('child2', 20);
console.log(child2.colors); // ["red", "blue", "green"]
child2.sayName(); // "child2"
child2.sayAge(); // 20
优点:
- 语法更加简洁明了,类的定义和继承更加直观。
- 可以在子类构造函数中使用
super()调用父类的构造函数,从而继承父类的属性和方法。 - 支持静态方法和静态属性的定义。
这个类继承的方法就感觉更像传统面向对象语言的继承方式。如c++、java差不多。