在ES6 class類聲明出來以前,ES5的繼承十分得蛋疼,這裏就寫一下ES5繼承的具體操做,最後和ES6作一下對比,你會發現ES6出來真是太好了!!!函數
在講知識點以前,首先明確幾個概念ui
function A(){}
(大寫的函數名)new A()
;var a = new A()
A.prototype
a.__proto__ === A.prototype
A.prototype.constructor === A
首先給出一個構造函數this
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;//擴展私有屬性
}
複製代碼
繼承中最麻煩的就是原型鏈的繼承,爲了你們便於理解,這裏給出幾種方案,並比較他們的優劣。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
複製代碼
sayName
的方法,可是student實例能夠經過原型鏈找到Person.prototype原型上的
sayName
方法
Student.prototype.sayAge = function(){
alert(this.age);
}
student.sayName();//彈窗'erha'
student.sayAge();//彈窗18
let person = new Person(opt);
person.sayName();//彈窗'erha'
person.sayAge();//報錯
複製代碼
此時原型鏈的狀況
可是咱們看到這種方法,會在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是如何實現繼承的.
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
寫法更貼合其餘真正面向對象語言的寫法