ES5和ES6继承

1.ES6继承

class Book{

	constructor(title,pages,isbn){
		this.title = title;
		this.pages = pages;
		this.isbn = isbn;
	}
	printIsbn(){
		console.log(this.isbn)
	}

}

class ITBook extends Book{

	constructor(title,pages,isbn,technology){
		super(title,pages,isbn);
		this.technology = technology;
	}

	printTechnology(){
		console.log(this.technology);
	}

}

let jsBook = new ITBook('JS','200','123','JavaSCript')
jsBook.title
"JS"
jsBook.printIsbn()
123

2.ES5继承

Javascript利用原型链的特性来实现继承,就是通过将子类构造函数的原型作为父类构造函数的实例,这样就连通了子类-子类原型-父类,原型链的特点就是逐层查找,从子类开始一直往上直到所有对象的原型Object.prototype,找到属性方法之后就会停止查找,所以下层的属性方法会覆盖上层。

1.原型链继承

一个基本的基于原型链的继承过程大概是这样的:

//先来个父类,带些属性
function Super(){
    this.flag = true;
}
//为了提高复用性,方法绑定在父类原型属性上
Super.prototype.getFlag = function(){
    return this.flag;
}
//来个子类
function Sub(){
    this.subFlag = false;
}
//实现继承
Sub.prototype = new Super;

//给子类添加子类特有的方法,注意顺序要在继承之后
Sub.prototype.getSubFlag = function(){
    return this.subFlag;
}
//构造实例
var es5 = new Sub;

原型链实现的继承主要有几个问题:
1、本来我们为了构造函数属性的封装私有性,方法的复用性,提倡将属性声明在构造函数内,而将方法绑定在原型对象上,但是现在子类的原型是父类的一个实例,自然父类的属性就变成子类原型的属性了;
这就会带来一个问题,我们知道构造函数的原型属性在所有构造的实例中是共享的,所以原型中属性的改变会反应到所有的实例上,这就违背了我们想要属性私有化的初衷;
2、创建子类的实例时,不能向父类的构造函数传递参数

//构造函数的原型属性在所有构造的实例中是共享的,所以原型中属性的改变会反应到所有的实例上
function Super(){
    this.flag = true;
}
function Sub(){
   this.subFlag = false;
}
Sub.prototype = new Super;
var obj = new Sub();
obj.flag = flase;  //修改之后,由于是原型上的属性,之后创建的所有实例都会受到影响
var obj_2 = new Sub();
console.log(obj.flag)  //false;

为了解决以上两个问题,有一个叫借用构造函数的方法。

2.借用构造函数

只需要在子类构造函数内部使用apply或者call来调用父类的函数即可在实现属性继承的同时,又能传递参数,又能让实例不互相影响

function Super(){
    this.flag = true;
}
function Sub(){
    Super.call(this)  //如果父类可以需要接收参数,这里也可以直接传递
}
var obj = new Sub();
obj.flag = flase;
var obj_2 = new Sub();
console.log(obj.flag)  //依然是true,不会相互影响

通过call或者apply方法,我们实际上是在将来新创建的SubType实例的环境下调用了SuperType构造函数。这样一来,就会在新SubType对象上执行SuperType函数中定义的所有对象初始化代码,因此,每一个SubType的实例都会有自己的colors对象的副本。

缺点:
1.方法都在构造函数中定义,函数无法复用
2.在超类型中定义的方法,子类型不可见,结果所有类型都只能使用构造函数模式

3.组合继承

基本思想:将原型链和借用构造函数技术组合到一起。使用原型链实现对原型属性和方法的继承,用借用构造函数模式实现对实例属性的继承。这样既通过在原型上定义方法实现了函数复用,又能保证每个实例都有自己的属性。

function Supertype(name){
    this.name = name;
    this.colors = ["red","green","blue"];
}
​
Supertype.prototype.sayName = function(){
    console.log(this.name);
};
​
function Subtype(name,age){
    \\继承属性
    Supertype.call(this,name);
    this.age  = age;
}
​
//继承方法
Subtype.prototype = new Supertype();
Subtype.prototype.constructor = Subtype;
Subtype.prototype.sayAge = function(){
    console.log(this.age);
};
​
var instance1 = new Subtype('Annika',21);
instance1.colors.push("black");
//["red", "green", "blue", "black"]
console.log(instance1.colors); 
instance1.sayName(); //Annika
instance1.sayAge();  //21
​
var instance2 = new Subtype('Anna',22);
//["red", "green", "blue"]
console.log(instance2.colors);
instance2.sayName();   //Anna
instance2.sayAge();    //22

再看一个例子:

function Super(){
    this.flag = true;
}
Super.prototype.getFlag = function(){
    return this.flag;     //继承方法
}
function Sub(){
    this.subFlag = flase
    Super.call(this)    //继承属性
}
Sub.prototype = new Super;
//这里还有个小问题,Sub.prototype = new Super; 会导致Sub.prototype的constructor指向Super;
//然而constructor的定义是要指向原型属性对应的构造函数的,Sub.prototype是Sub构造函数的原型,所以应该添加一句纠正:Sub.prototype.constructor = Sub;
Sub.prototype.constructor = Sub;
var obj = new Sub();
Super.prototype.getSubFlag = function(){
    return this.flag;
}

缺点:无论在什么情况下,都会调用两次超类型构造函数,一次是在创建子类型原型的时候,一次是在子类型构造函数的内部

还有原型式继承、寄生式继承、寄生组合式继承。

可以参考:
https://www.jianshu.com/p/342966fdf816
https://www.cnblogs.com/xiadongqing/p/6088252.html
https://www.cnblogs.com/annika/p/9073572.html

https://blog.csdn.net/daydream13580130043/article/details/83830420
https://blog.csdn.net/wlk2064819994/article/details/81389161
https://www.cnblogs.com/chengzi/p/5623280.html
https://blog.csdn.net/u013055396/article/details/79529736

你可能感兴趣的:(ES6,Javascript)