学习链接
继承方式
1.原型链继承
关键
Child.prototype = new Parent();
完整
function Parent() {
this.name = 'kevin';
}
Parent.prototype.getName = function () {
console.log(this.name);
}
function Child() {}
Child.prototype = new Parent();
const child1 = new Child();
child1.getName(); // kevin
问题
- 引用类型的属性被所有实例共享(
this.arr = [1,2]
) - 创建
Child
实例时,无法向Parent
传参
补充
Child.constructor === Function
Child.prototype.constructor === Parent
child1.constructor === Parent
2.盗用构造函数继承
关键
Parent.call(this);
完整
function Parent() {
this.names = ['kevin', 'daisy'];
this.getName = function() {
console.log(this.names);
}
}
function Child() {
Parent.call(this);
}
const child1 = new Child();
child1.names.push('yayu');
child1.getName(); // ["kevin", "daisy", "yayu"]
const child2 = new Child();
child2.getName(); // ["kevin", "daisy"]
优点:
避免了引用类型的属性被所有实例共享
可以在
Child
中向Parent
传参
缺点:
子类不能访问父类原型上定义的方法
方法都在构造函数中定义,每次创建实例都会创建一遍方法
3.原型链 + 盗用构造函数的组合继承
关键
Child.prototype = new Parent();
Parent.call(this);
完整
function Parent() {
this.names = ['kevin', 'daisy'];
}
Parent.prototype.getName = function() {
console.log(this.names);
}
function Child() {
Parent.call(this);
}
Child.prototype = new Parent();
const child1 = new Child();
child1.names.push('yayu');
child1.getName(); // ["kevin", "daisy", "yayu"]
const child2 = new Child();
child2.getName(); // ["kevin", "daisy"]
优点
- 融合原型链继承和盗用构造函数继承的优点,是 JavaScript 中最常用的继承模式。
缺点
- 调用了两次父类构造函数,生成了两份实例
4.原型式继承
关键
function createObj(o) {
function F() {}
F.prototype = o;
return new F();
}
// 或
Object.create(o);
完整
const person = {
name: 'kevin',
friends: ['daisy', 'kelly']
}
const person1 = createObj(person);
const person2 = createObj(person);
person1.name = 'person1'; // 定义在自身 覆盖了原型同名属性
console.log(person2.name); // kevin
person1.friends.push('taylor');
console.log(person2.friends); // ["daisy", "kelly", "taylor"]
优点
- 原型式继承非常适合不需要单独创建构造函数,但仍然需要在对象间共享信息的场合
缺点
- 包含引用类型的属性始终都会共享相应的值,这点跟原型链继承一样
5.寄生式继承
关键
function createObj(o) {
const clone = Object.create(o); // 共享了属性
clone.sayName = function() {
console.log(this.name);
}
return clone;
}
完整
const person = {
name: "Nicholas",
friends: ["Shelby", "Court", "Van"]
};
const anotherPerson = createObj(person);
anotherPerson.sayName(); // "Nicholas"
优点
- 适合主要关注对象,而不在乎类型和构造函数的场景
缺点
- 跟借用构造函数模式一样,每次创建对象都会创建一遍方法
6.寄生组合式继承
关键
function createObj(o) {
function F() {}
F.prototype = o;
return new F();
}
// 或
Object.create(o);
function inheritPrototype(child, parent) {
const prototype = createObj(parent.prototype);
prototype.constructor = child;
child.prototype = prototype;
}
完整
function Parent() {
this.names = ['kevin', 'daisy'];
// console.log('调用了 Parent 构造函数');
}
Parent.prototype.getName = function() {
console.log(this.names);
}
function Child() {
Parent.call(this);
}
// Child.prototype = new Parent(); // 这里调用了 Parent 构造函数
// 对比
// const F = function () {};
// F.prototype = Parent.prototype; // 这里没调用 Parent 构造函数
// Child.prototype = new F();
inheritPrototype(Child, Parent);
const child1 = new Child();
child1.names.push('yayu');
child1.getName(); // ["kevin", "daisy", "yayu"]
const child2 = new Child();
child2.getName(); // ["kevin", "daisy"]
优点
这种方式的高效率体现它只调用了一次 Parent
构造函数,并且因此避免了在 Parent.prototype
上面创建不必要的、多余的属性。
与此同时,原型链还能保持不变;因此,还能够正常使用 instanceof
和 isPrototypeOf
。开发人员普遍认为寄生组合式继承是引用类型最理想的继承范式。
Class 继承
class Person {
//调用类的构造方法
constructor(name, age) {
this.name = name;
this.age = age;
}
//定义一般的方法
showName() {
console.log("调用父类的方法")
console.log(this.name, this.age);
}
}
let p1 = new Person('kobe', 39);
console.log(p1);
//定义一个子类
class Student extends Person {
constructor(name, age, salary) {
super(name, age); //通过super调用父类的构造方法
this.salary = salary;
}
showName() { //在子类自身定义方法
console.log("调用子类的方法");
console.log(this.name, this.age, this.salary);
}
}
let s1 = new Student('wade', 38, 1000000000);
console.log(s1);
s1.showName();
补充
JavaScript 不提供多重继承功能,即不允许一个对象同时继承多个对象。但是,可以通过变通方法,实现这个功能。
Object.assign ( target, ...sources )
将所有可枚举属性的值从一个或多个源对象分配到目标对象,返回目标对象
function M1() {
this.hello = 'hello';
}
function M2() {
this.world = 'world';
}
function S() {
M1.call(this);
M2.call(this);
}
// 继承 M1
S.prototype = Object.create(M1.prototype);
// 继承链上加入 M2
Object.assign(S.prototype, M2.prototype);
// 指定构造函数
S.prototype.constructor = S;
var s = new S();
s.hello // 'hello'
s.world // 'world'
上面代码中,子类 S
同时继承了父类 M1
和 M2
。这种模式又称为 Mixin(混入)。
注意:M2 是在继承链上,而非原型链。