最近在读《高程》,总结一下继承的成长过程。JS的继承主要是依靠原型链的概念,因此学习继承之前需要掌握好原型链以及构造函数的概念。
1、原型链继承
function Parent(){//父
this.name="juan";
this.arr=[1,2,3,4];
}
Parent.prototype.sayName=function(){//父方法
console.log(this.name);
}
function Child(){//子
this.sex='1'
}
Child.prototype=new Parent();//相当于是Child的原型是Parent的实例
//因此可以使用Parent的属性和方法
let child=new Child();
console.log(child);
通过console继承实例,可以很清楚的看到继承的过程。
1、不能传递参数
2、父类型中的属性是所有子类型共享的
let child=new Child();
let child2=new Child();
child.arr.push(666);//child给arr添加了一个数据
console.log(child2.arr);//[1, 2, 3, 4, 666],child2读取的是同一个
为了解决这两个问题,引出了另一种继承方法。
2、构造函数继承
function Parent(name){
this.name=name;
this.arr=[1,2,3,4];
this.sayName=function(){
console.log(this.name);
}
}
function Child(name){
this.sex='1';
Parent.call(this,name);//每个实例都重新创建了父类型的构造
}
let child=new Child("juan");
let child2=new Child("guo");
child.arr.push(666)
console.log(child2.arr)//[1, 2, 3, 4]
child.sayName();//juan
child2.sayName();//guo
1、子类型无法访问父类型原型链上的属性和方法 (因为不是通过原型继承)
child instanceof Parent; //false
2、方法在构造函数里创建,不具有复用性
因此通过结合以上两种继承方式,私有属性通过构造函数call来继承,共享的通过原型链来继承。
3、组合继承
function Parent(name) {//这里是各自私有的属性
this.name = name;
this.arr = [1, 2, 3, 4];
}
Parent.prototype.sayName = function () {//共享方法
console.log(this.name);
}
function Child(name) {
this.sex = '1';
Parent.call(this, name);//继承父类型自身的
}
Child.prototype=new Parent();//原型继承父类型原型上的
Child.prototype.constructor=Child;//补全自身的constructor
let child = new Child("juan");
let child2 = new Child("guo");
child.arr.push(666)
console.log(child2.arr)//[1, 2, 3, 4]
child.sayName();//juan
child2.sayName();//guo
如下图所示,可以清楚的看到继承的过程
就是每次实现,都要执行两次父类型。并且父类型中属性也存在了两组
1、通过call执行一次
2、通过new执行一次
此时,一种原型式继承的方法横空出世。
4、原型式继承
通过一个函数,内部创建一个临时构造函数,通过传入一个对象,当作是该函数的原型,返回这个构造函数的实例。
实质上是对obj进行了一次浅拷贝。
function object(obj){
function F(){};
F.prototype=obj;
return new F();
}
这样做的好处是,无需重复地创建构造函数。甚至无需创建构造函数,如果需要两个对象拥有类似的方法,使用这个函数就可以达到目的。
Object.create();
//是如上函数在JS中的规范化
5、寄生式继承
通过一个函数,对对象进行增强,但仍旧不能实现函数复用。
function createAnother(original){//传入某个对象
let clone=object(original);//进行原型继承
clone.sayHi=function(){//添加方法,但方法不能复用,每次都是新的
console.log("hi");
}
return clone;
}
6、寄生组合式继承
function Parent(name) {
this.name = name;
this.arr = [1, 2, 3, 4];
}
function object(obj){ //即为Object.create()
function F(){};
F.prototype=obj;
return new F();
}
Parent.prototype.sayName = function () {
console.log(this.name);
}
function Child(name){
Parent.call(this,name);
this.sex = '1';
}
Child.prototype=object(Parent.prototype);
//可以直接使用xxxx=Object.create(xxx)代替
Child.prototype.constructor=Child;
Child.prototype.saySex=function(){
console.log(this.sex);
}
let child = new Child("juan");
let child2 = new Child("guo");
child.arr.push(666)
console.log(child2)
child.sayName();//juan
child2.sayName();//guo
如下图所示,可以清楚的看到与组合式继承相比的变化,父类型中的属性没有被创建两次,构造函数也只通过call执行了一次。
7、ES6的extends继承
class本质上是构造函数的语法糖
ES6中的继承也有说是语法糖但具体是哪种继承的语法糖,暂时还不是很清楚
通过super来继承父类的this,并加工成自己的属性方法,console之后没看出来有哪种跟它是一样的……它把继承来的属性和方法都放在自己身上了……
等认真研究完ES6再来深究一下
class Parent{
constructor(name){
this.name = name;
this.arr = [1, 2, 3, 4];
}
sayName = function () {
console.log(this.name);
}
}
class Child extends Parent{
constructor(name,sex){
super(name);//子类继承父类this,并加工成自己的
this.sex = sex;
}
saySex=function(){
console.log(this.sex);
}
}
let child = new Child("juan");
let child2 = new Child("guo");
child.arr.push(666)
console.log(child2)
child.sayName();
child2.sayName();