1、借助构造函数实现继承
call和apply改变的是JS运行的上下文:
/*借助构造函数实现继承*/
function Parent(name) {
this.name = name;
this.getName = function () {
console.log(this.name);
}
}
function Child(name) {
Parent.call(this, name);
this.type = 'child1'
}
let child = new Child('yanle');
child.getName();
console.log(child.type);父类的this指向到了子类上面去,改变了实例化的this 指向,导致了父类执行的属性和方法,都会挂在到 子类实例上去;
缺点:父类原型链上的东西并没有被继承;
2、通过原型链实现继承
/*通过原型链实现继承*/
function Parent2(){
this.name='parent2'
}
function Child2(){
this.type='child2'
}
Child2.prototype=new Parent2();
console.log(new Child2());Child2.prototype是Child2构造函数的一个属性,这个时候prototype被赋值了parent2的一个实例,实例化了新的对象Child2()的时候,
会有一个__proto__属性,这个属性就等于起构造函数的原型对象,但是原型对象被赋值为了parent2的一个实例,
所以new Child2的原型链就会一直向上找parent2的原型
var s1=new Child2();
var s2=new Child2();
s1.proto===s2.proto;//返回true
缺点:通过子类构造函数实例化了两个对象,当一个实例对象改变其构造函数的属性的时候,
那么另外一个实例对象上的属性也会跟着改变(期望的是两个对象是隔离的赛);原因是构造函数的原型对象是公用的;
3、组合方式
/*组合方式*/
function Parent3(){
this.name='parent3';
this.arr=[1,2,3];
}
function Child3(){
Parent3.call(this);
this.type='child';
}
Child3.prototype=new Parent3();
var s3=new Child3();
var s4=new Child3();
s3.arr.push(4);
console.log(s3,s4);**优点:**这是最通用的使用方法,集合了上面构造函数继承,原型链继承两种的优点。
**缺点:**父类的构造函数执行了2次,这是没有必要的,
constructor指向了parent了
4、组合继承的优化
/*组合继承的优化1*/
function Parent4(){
this.name='parent3';
this.arr=[1,2,3];
}
function Child4(){
Parent4.call(this);
this.type='child5';
}
Child4.prototype=Parent4.prototype;
var s5=new Child4();
var s6=new Child4()**缺点:**s5 instaceof child4 //true, s5 instanceof Parent4//true
我们无法区分一个实例对象是由其构造函数实例化,还是又其构造函数的父类实例化的
s5.constructor 指向的是Parent4;//原因是子类原型对象的constructor 被赋值为了父类原型对象的 constructor,所以我们使用constructor的时候,肯定是指向父类的
Child3.constructor 也有这种情况
5、组合继承的优化2
function Parent5() {
this.name = 'parent5';
this.play = [1, 2, 3];
}
function Child5() {
Parent5.call(this);
this.type = 'child5'
}
Child5.prototype = Object.create(Parent5.prototype);
//这个时候虽然隔离了,但是constructor还是只想的Parent5的,因为constructor会一直向上找
Child5.prototype.constructor=Child5;
var s7=new Child5();
console.log(s7 instanceof Child5,s7 instanceof Parent5);
console.log(s7.constructor);通过Object.create来创建原型中间对象,那么这么来的话,chiild5的对象prototype获得的是parent5 父类的原型对象;
Object.create创建的对象,原型对象就是参数;
6、ES 中的继承
Class 可以通过extends关键字实现继承,让子类继承父类的属性和方法。extends 的写法比 ES5 的原型链继承,要清晰和方便很多。
class Point { /* ... */ }
class ColorPoint extends Point {
constructor(x, y, color) {
super(x, y); // 调用父类的constructor(x, y)
this.color = color;
}
toString() {
return this.color + ' ' + super.toString(); // 调用父类的toString()
}
}