面向对象--继承

在ES6 class类声明出来以前,ES5的继承十分得蛋疼,这里就写一下ES5继承的具体操做,最后和ES6作一下对比,你会发现ES6出来真是太好了!!!函数

在讲知识点以前,首先明确几个概念ui

  • ES5中构造函数声明方式:function A(){}(大写的函数名)
  • 实例化:new A();
  • 实例化生成的对象称为实例:var a = new A()
  • 原型:A.prototype
  • 实例能够经过__proto__访问原型,即:a.__proto__ === A.prototype
  • 原型上的constructor属性指向构造函数自己(能够修改),即:A.prototype.constructor === A

ES5

首先给出一个构造函数this

function Person(opt){
    this.name = opt.name;
    this.age = opt.age;
}

Person.prototype.sayName = function(){
    alert(this.name);
}
复制代码
  1. 私有属性的继承
function Student(opt){
    Person.call(this,opt);//当生成实例时,此步骤能够将Person的私有属性挂载至实例上
    
    this.sex = opt.sex;//扩展私有属性
}
复制代码
  1. 原型链的继承

继承中最麻烦的就是原型链的继承,为了你们便于理解,这里给出几种方案,并比较他们的优劣。spa

方案一:prototype

Student.prototype = Person.prototype
复制代码

此种方案虽然简便,可是有个十分严重的缺点,由于原型自己是一个对象,经过直接赋值的形式,则你在Student的原型上作的全部扩展都会影响到Person.code

思路: 既然不能直接将原型赋值给子类,那么势必要经过中间介质来访问父类的原型,利用原型链,底层实例能够向上层原型链访问的特色,咱们能够利用Person生成一个实例,并把它赋值给Student.prototype,此时Student.prototype就能够经过__proto__访问到Person的原型,也就可使用它的方法cdn

方案二:对象

Student.prototype = new Person({});
复制代码

可能看了上面的代码,大家可能有点不理解,不急下面给出解释.blog

首先从代码自己来看,咱们把Person实例出的一个对象赋值给了Student的原型,那么咱们就来看看,如今Student的原型是什么样的继承

let opt = {
    name: "erha",
    age: 18,
    sex: "男"
};

let student = new Student(opt);
console.log(new Person({}));
console.log(student);
console.log(student.__proto__ === Student.prototype);//true
复制代码

如今Student的原型就如红框中所示. 此时虽然Student的原型上没有 sayName的方法,可是student实例能够经过原型链找到Person.prototype原型上的 sayName方法

此时不只能够给Student的原型添加方法不会对Person的原型形成影响,并且仍然能够调用Person原型上的方法

Student.prototype.sayAge = function(){
    alert(this.age);
}
student.sayName();//弹窗'erha'
student.sayAge();//弹窗18
let person = new Person(opt);
person.sayName();//弹窗'erha'
person.sayAge();//报错
复制代码

此时原型链的状况

能够看到对Student原型的操做并无对Person原型形成影响

可是咱们看到这种方法,会在Student的原型上产生无用的公有属性

所以给出改进的方案三:

/*既然方案二会产生无用的公有属性,那么咱们就定义一个没有私有属性的构造函数*/
function A(){};
A.prototype = Person.prototype;//而后和Person的原型关联
Student.prototype = new A();//和方案二原理相似
Student.prototype.sayAge = function(){
    alert(this.age);
}
let studentA = new Student(opt);
console.log(studentA);
复制代码

能够看到方案三不只继承了方案二的优势,并且完善了方案二原型上会有无效的公有属性的缺点.

最后,由于经过new A({})生成的实例并无constructor属性,因此咱们还须要修正一下Student原型上的construtor.

Student.prototype.constructor = Student;
复制代码

最后给出完成的方案三代码

function Person(opt){
    this.name = opt.name;
    this.age = opt.age;
}

Person.prototype.sayName = function(){
    alert(this.name);
}

function Student(opt){
    Person.call(this,opt);//当生成实例时,此步骤能够将Person的私有属性挂载至实例上

    this.sex = opt.sex;//扩展私有属性
}

let opt = {
    name: "erha",
    age: 18,
    sex: "男"
};
/***************/
function A(){};
A.prototype = Person.prototype;//而后Person的原型关联
Student.prototype = new A();
/***************/
Student.prototype.sayAge = function(){
    alert(this.age);
}
Student.prototype.constructor = Student;
let studentA = new Student(opt);
console.log(studentA);
复制代码

虽然方案三,完美地解决了继承和扩展的问题,可是仍是有它的缺点:步骤繁琐

方案四:

Object.create()方法建立一个新对象,使用现有的对象来提供新建立的对象的__proto__

function Person(opt){
    this.name = opt.name;
    this.age = opt.age;
}

Person.prototype.sayName = function(){
    alert(this.name);
}

function Student(opt){
    Person.call(this,opt);//当生成实例时,此步骤能够将Person的私有属性挂载至实例上

    this.sex = opt.sex;//扩展私有属性
}

let opt = {
    name: "erha",
    age: 18,
    sex: "男"
};
/***************/
Student.prototype = Object.create(Person.prototype);//建立一个新对象,并将Person原型提供给新建对象的__proto__,最后赋值给Student的原型
/***************/
Student.prototype.sayAge = function(){
    alert(this.age);
}
let studentA = new Student(opt);

console.log(studentA);
复制代码

最后结果和方案三如出一辙,可是使用原生JS自带的方法省去不少步骤,可是它们的原理是同样的.

最后来看看ES6是如何实现继承的.

ES6

class Person{
    constructor(opt){
        this.name = opt.name;
        this.age = opt.age; 
    }
    
    sayName(){
        alert(this.name);
    }
}

class Student extends Person{
    constructor(opt){
        super(opt);//继承私有属性
        this.sex = opt.sex;
    }
    
    //扩展方法
    sayAge(){
        alert(this.age);
    }
}

let opt = {
    name: "erha",
    age: 18,
    sex: "男"
};

let studentA = new Student(opt);
console.log(studentA);

复制代码

能够看到使用extends,直接解决了ES5中最麻烦的原型链继承,且调用super()也直接继承了父类的私有属性,而且私有属性的扩展和公有属性的扩展都写在了class内部,让人看上去更加得直观,而且class写法更贴合其余真正面向对象语言的写法

相关文章
相关标签/搜索