目录
- 1. 原型链继承
- 2. 构造函数继承
- 3. 组合继承(原型链+构造函数)
- 4. 原型式继承(使用较少)
- 5. 寄生式继承
- 6. 寄生组合式继承
- 7. ES6中的继承——extends
对象的继承有很多种方式,下面来一一列举:
1. 原型链继承
(1)构造函数、原型和实例的关系:
- 每个构造函数都有一个原型对象
- 原型对象都包含一个指向一个指向构造函数的指针
- 实例都包含一个指向原型对象的内部指针
(2)实现关键点:子类对象的实例的原型等于父类对象的实例
(3)特点: 可以继承父类的私有属性以及其原型上的方法,但是继承时是引用了同一个内存地址,因此只要修改其中一个,也会影响到另一个对象
// 父类构造函数
function Parent(){
this.name = "Tom";
}
Parent.prototype.sayName = function(){
console.log(this.name);
}
// 子类构造函数
function Children (){};
// 实现继承的关键
Children.prototype = new Parent();
var A = new Parent();
var B = new Children();
A.sayName(); // Tom
B.sayName(); // Tom
2. 构造函数继承
(1)实现关键点:在子类的函数体内执行父类,通过call()
和apply()
来改变this
指向
(2)特点:可继承父类里的私有属性和方法,但是不能继承父类原型上的方法和属性,当修改一个对象的属性和方法时不会影响到另一个对象。
// 父类构造函数
function Parent(){
this.name = "Tom";
}
Parent.prototype.sayName = function(){
console.log(this.name);
}
// 子类构造函数
function Children (){
Parent.call(this);
};
var B = new Children();
console.log(B.name); // Tom
B.sayName(); // 报错
3. 组合继承(原型链+构造函数)
(1)实现关键点:通过原型实现对原型属性和方法的继承,通过构造函数实现对实例属性的继承
(2)特点:这种方法继承了以上两种方法的优点,但是在使用的时候,会调用两次父类构造函数,第一次是在创建子类的原型时,第二次是在子类的构造函数内部,子类会包含父类的全部属性和方法。
// 父类构造函数
function Parent(){
this.name = "Tom";
}
Parent.prototype.sayName = function(){
console.log(this.name);
}
// 子类构造函数
function Children (){
Parent.call(this);
};
Children.prototype = new Parent();
var B = new Children();
console.log(B.name); // Tom
B.sayName(); // Tom
4. 原型式继承(使用较少)
实现基本点:借助Object.create()
的原理,用一个函数包装一个对象,然后返回该函数的调用,这个函数就变成了一个可以任意添加属性和方法的实例或对象
function createObj(o) {
function Fn(){}
Fn.prototype = o;
return new Fn();
}
// 父类
var parent = {
name : 'Tom',
sayName : function(){
console.log(this.name);
}
}
// 继承
var A = createObj(parent);
A.sayName(); // Tom
A.name = 'bobo';
console.log(A.name); // bobo
5. 寄生式继承
实现关键点:创建一个仅用于封装继承过程的函数,该函数内部通过某种方式来增强对象,最后返回构造函数
function object(o) {
function F(){}
F.prototype = o;
return new F();
}
function createObj(o) {
// 通过调用函数创建一个新对象
var obj = object(o);
// 以某种方式来增强这个对象
obj.sayName = function() {
console.log(this.name);
}
return obj;
}
// 父类
var parent = {
name : 'Tom',
sayName : function(){
console.log(this.name);
}
}
var children = createObj(parent);
children.sayName(); // Tom
children.name = 'bobo';
console.log(children.name); // bobo
6. 寄生组合式继承
实现关键点:组合使用以上方法的优点。通过借用构造函数传递参数和寄生模式实现继承属性,通过原型链的混成形式来继承方法,在函数中用apply或者call引入另一个构造函数,可以传参。
function inheritPrototype(children, parent){
var prototype = Object.create(parent.prototype); // Object.create创建对象
prototype.constructor = children; // 增强对象
children.prototype = prototype; // 指定对象
}
// 父类
function Parent(){
this.name = 'Tom';
}
Parent.prototype.sayName = function(){
console.log(this.name);
};
// 借用构造函数传递增强子类实例属性
function Children(){
Parent.call(this);
}
// 将父类原型指向子类
inheritPrototype(Children, Parent);
var A = new Children();
A.sayName(); // Tom
A.name = 'bobo';
console.log(A.name); // bobo
7. ES6中的继承——extends
该方法是ES6中引入的方法,使用关键字extends
进行继承,其本质还是构造函数+原型链的组合式继承。
(1)extends
关键字:B类通过extends
关键字实现了对A类属性和方法的继承,A类中的所有方法都添加到了B类上,所以extends
的继承本质是原型链。
(2)super
关键字:super
关键字既可以当函数使用,也可以当对象使用。
- 当作为函数时,
super
代表父类的构造函数,并在子类中执行Parent.apply(this)
,从而将父类的属性和方法添加到子类的this
上。 - 当作为对象时,在子类中指向父类的原型对象,即
super = Parent.prototype
。、
(3)super
关键字在使用时要注意以下三点:
- 子类必须在
constructor
方法中调用super
方法 - 在子类的
constructor
方法中,只有调用super
之后,才可以使用this
关键字 -
super()
只能用在子类的constructor
方法之中
class A {
constructor(name, age) {
this.name = name;
this.age = age;
}
getName() {
return this.name;
}
}
class B extends A {
constructor(name, age) {
super(name, age);
this.job = "IT";
}
getJob() {
return this.job;
}
}
var b = new B("Tom", 18);
console.log(b.name); // Tom
console.log(b.age); // 18
console.log(b.getName()); // Tom
console.log(b.getJob()); // IT