面向對象編程很重要的一個方面,就是對象的繼承。A
對象經過繼承B
對象,就能直接擁有B
對象的全部屬性和方法。這對於代碼的複用是很是有用的。傳統上,JavaScript
語言的繼承不經過class
,須要使用原型機制或者用applay
和call
方法實現。ES6
引入了class
語法,則出現了基於class
的繼承。編程
爲何要使用繼承?繼承解決了什麼問題?這裏就不賣關子了,直接給出答案。繼承是爲了解決構造函數的缺陷,解決在同一個構造函數的多個實例之間,沒法共享屬性,從而形成對系統資源的浪費。讓咱們經過下面例子簡單來分析一下下。數組
例子1:bash
function Cat (name, color) {
this.name = name;
this.color = color;
}
var cat1 = new Cat('大毛', '白色');
cat1.name // '大毛'
cat1.color // '白色'
複製代碼
以上代碼中,Cat
函數是一個構造函數,函數內部定義了name
屬性和color
屬性,全部實例對象(cat1
)都會生成這兩個屬性。下面咱們再改造下例子1。app
例子2:函數
function Cat(name, color) {
this.name = name;
this.color = color;
this.meow = function () {
console.log('喵喵');
};
}
var cat1 = new Cat('大毛', '白色');
var cat2 = new Cat('二毛', '黑色');
cat1.meow === cat2.meow
// false
複製代碼
例子2中cat1
和cat2
是同一個構造函數的兩個實例,它們都具備meow
方法。因爲meow
方法是生成在每一個實例對象上面,因此兩個實例就生成了兩次。ok,我想你們到這都很清楚明白。可是,問題來了,每新建一個實例,這裏面就會新建一個函數方法,這既沒有必要,又浪費系統資源,由於全部meow
方法都是一樣的行爲,徹底能夠共享複用。學習
存在即合理。上面經過例子大概瞭解繼承出現是爲了幹什麼,如今我們得知道它是如何幹活的,即繼承要如何實現呢?開始以前看看都要哪些方法能夠實現繼承。ui
call
方法繼承applay
方法繼承ES6
實現繼承將構造函數的原型設置爲另外一個構造函數的實例對象,這樣就能夠繼承另外一個原型對象的全部屬性和方法,能夠繼續往上,最終造成原型鏈。this
子類經過prototype
將全部在父類中經過prototype
追加的屬性和方法都追加到子類,從而實現繼承。爲了讓子類繼承父類的屬性(也包括方法),首先須要定義一個構造函數,而後,將父類的新實例賦值給構造函數原型。具體看下面代碼。spa
function parent(){
this.name="garuda";
}
function child(){
this.sex="man"
}
child.prototype=new parent();//核心:子類繼承父類,經過原型造成鏈條
var test=new child();
console.log(test.name);
console.log(test.sex);
複製代碼
在js中,被繼承的函數稱爲超類型(父類、基類),繼承的函數稱爲子類型(子類、派生類)。prototype
使用原型繼承存在兩個問題:一是面量重寫原型會中斷關係,使用引用類型的原型,二是子類型還沒法給超類型傳遞參數。
爲了解決原型中包含引用類型值的問題,開始使用借用構造函數,也叫僞造對象或經典繼承
function parent(){
this.name="garuda";
}
function child(){
parent.call(this);//核心:借父類型構造函數加強子類型(傳參)
}
var test =new parent();
console.log(test.name);
複製代碼
存在的問題就是,全部的類型都只能使用構造函數模式(由於超類型的原型中定義的方法對於子類型不可見),所以方法都在構造函數中定義,函數複用就無從談起了。
call
方法是Function
類中的方法call
方法的第一個參數的值賦值給類(即方法)中出現的this
,call
方法的第二個參數開始依次賦值給類(即方法)所接受的參數(參數列表)。
function test(str){
alert(this.name + " " + str);
}
var object = new Object();
object.name = "zhangsan";
test.call(object,"langsin");
//此時,第一個參數值object傳遞給了test類(即方法)中出現的this,
// 而第二個參數"langsin"則賦值給了test類(即方法)的str
function Parent(username){
this.username = username;
this.hello = function(){
alert(this.username);
}
}
function Child(username,password){
Parent.call(this,username);
this.password = password;
this.world = function(){
alert(this.password);
}
}
var parent = new Parent("zhangsan");
var child = new Child("lisi","123456");
parent.hello();
child.hello();
child.world();
複製代碼
apply方法接受2個參數,第一個參數與call
方法的第一個參數同樣,即賦值給類(即方法)中出現的this
,第二個參數爲數組類型,這個數組中的每一個元素依次賦值給類(即方法)所接受的參數(數組參數)。
function Parent(username){
this.username = username;
this.hello = function(){
alert(this.username);
}
}
function Child(username,password){
Parent.apply(this,new Array(username));
this.password = password;
this.world = function(){
alert(this.password);
}
}
var parent = new Parent("zhangsan");
var child = new Child("lisi","123456");
parent.hello();
child.hello();
child.world();
複製代碼
也叫僞經典繼承,將原型鏈和借用構造函數的技術組合到一塊。使用原型鏈實現對原型屬性和方法的繼承,而經過構造函數來實現對實例屬性的繼承。
function parent(){
this.name="garuda";
}
function borther(){
return this.name;
}
function child(){
parent.call(this)
}
child.prototype=new parent();
var test=new parent();
console.log(test.borther())
複製代碼
其實是借用了構造函數,以覆蓋的方式,解決了在原型鏈繼承中原型的引用類型屬性共享在全部實例中的問題。
ES6
的class
是語法糖,其實質就是函數,而上述用class
實現繼承的過程,仍是基於原型鏈(和ES5
的是否是徹底一致)
// ES6 寫法
class Human{
constructor(name){
this.name = name
}
run(){
console.log("我叫"+this.name+",我在跑")
return undefined
}
}
class Man extends Human{ // extends 實現上述繼承過程
constructor(name){
super(name) // 調用構造函數:'超類'
this.gender = '男'
}
fight(){
console.log('糊你熊臉')
}
}
複製代碼
JS
中的繼承關係是很重要的技術知識,在實際開發中常常會用到,不瞭解的童鞋須要加緊學習理解哦!