js 中實現繼承有兩種經常使用方式:數組
原型鏈繼承(對象間的繼承) 類式繼承(構造函數間的繼承)
JavaScript不是真正的面向對象的語言,想實現繼承能夠用JS的原型prototype
機制或者call
和apply
方法app
在面向對象的語言中,可使用類來建立一個自定義對象,固然ES6
中也引入了class
來建立類。在這以前,咱們須要使用js
的原型來建立自定義對象。函數
類繼承
是在子類型構造函數的內部調用父類型的構造函數this
function Super (){ this.colors = ["blue","red"]; } function Sub () { Super.call(this); }
原型式繼承
是藉助已有的對象建立新的對象,將子類的原型指向父類。prototype
function Super (id) { this.id = id; } Super.prototype.toString = function () { return 'Super' + this.id; } function Sub (id) { this.id = id; } Sub.prototype = new Super(); // 這句即原型式繼承的核心代碼
爲了讓子類繼承父類的屬性和方法,首先須要定義一個構造函數,而後將父類的實例賦值給構造函數的原型。code
function Parent () { this.name = 'Parent'; } function Child () { this.age = 10; } Chid.prototype = new Parent(); // Chid繼承Parent,造成原型鏈 var test = new Child(); console.log(test.name) // Parent console.log(test.age) // 10 獲得被繼承的屬性 // 繼續原型鏈繼承 function Brother () { this.weight = 60; } Brother.prototype = new Child(); var peter = new Brother(); console.log(peter.name) //繼承了Child和Parent,輸出Parent console.log(peter.age) // 輸出10
全部的構造函數都繼承自Object
,它是自動完成繼承的並不須要咱們手動繼承,那麼接着看它們的從屬關係對象
能夠經過兩種方式肯定原型和實例的關係,經過操做符instanceof
和isPrototypeof()
方法繼承
console.log(peter instanceof Object); //true console.log(test instanceof Brother) //false,test是brother的父類 console.log(peter instanceof Child) //true console.log(peter instanceof Parent) //true
只要是原型鏈中出現過的原型,均可以說是改原型鏈派生的實例的原型。所以,isProtptypeof()
方法也會返回true
ip
在JS中,被繼承的函數成爲父類(或者 基類、超類),繼承的函數成爲子類(派生類)。使用原型繼承主要有兩個問題,一是字面量重寫原型會中斷關係,使用引用類型的原型,而且子類型沒法給父類型傳遞參數。原型鏈
僞類解決引用共享和超類型沒法傳參的問題,咱們能夠採用'類式繼承'的方式
function Parent (age) { this.colors = ["blue","red","green"]; this.age = age; } function Child (age) { Parent.call(this,age); } var peter = new Child(20); console.log(peter.age) //20 console.log(peter.colors) //blue,red,green peter.colors.push("white"); console.log(peter.colors) //blue,red,green,white
借用構造函數雖然解決了上面兩張問題,但沒有原型,因此咱們須要原型鏈+借用構造函數
的模式,這種模式成爲組合繼承
function Parent (age) { this.colors = ["blue","red","green"]; this.age = age; } Parent.prototype.run = function () { return this.colors + ' is ' +this.age; } function Child (age) { Parent.call(this,age); //對象冒充,給父類型傳參 } Child.prototype = new Parent(); //原型鏈繼承 var peter = new Child(20); console.log(peter.run()); //blue,red,green is 20
組合繼承是一種比較經常使用的方法,思路是使用原型鏈實現對原型屬性和方法的繼承,借用構造函數來實現對實例屬性的繼承。這樣,既實現了原型上定義方法的函數複用,又保證每一個實例都有本身的屬性。
call()與apply()
的用法:調用一個對象的一個方法,用另外一個對象替換當前對象。
call(thisObj,Object); // call接收一個對象 apply(thisObj,[argArray]) //apply接收一個數組
這種繼承藉助原型並基於已有的對象建立新的對象,同時還不用建立自定義類型的方式成爲原型式繼承
function obj(o) { function F() {} F.prototype = o; return new F(); } var box = { name : 'peter', arr : ['blue','red','green'] }; var b1 = obj(box); console.log(b1.name) // peter b1.name = 'jack'; console.log(b1.name) //jack console.log(b1.arr) //blue,red,green b1.arr.push('white') //blue,red,green,white var b2 = obj(box); console.log(b2.name) // peter console.log(b1.arr) //blue,red,green
原型式繼承首先在obj()
函數內部建立一個臨時性的構造函數,而後將傳入的對象做爲這個構造函數的原型,最後返回這個臨時類型的新實例。
這種繼承方式是把原型式+工廠模式
結合起來,目的是爲了封裝建立的過程。
function create(o){ var f = obj(o); f.run = function () { return this.arr;//一樣,會共享引用 }; return f; }
組合式繼承是JS
最經常使用的繼承模式,但組合繼承的父類型會在使用過程當中被調用兩次,一次是建立子類型的時候,另外一次是在子類型構造函數的內部
function Parent(name){ this.name = name; this.arr = ['哥哥','妹妹','父母']; } Parent.prototype.run = function () { return this.name; }; function Child(name,age){ Parent.call(this,age);//第二次調用 this.age = age; } Child.prototype = new Parent();//第一次調用
以上代碼是組合繼承,那麼寄生組合繼承
解決了兩次調用的問題
function obj() { function F() {} F.prototype = o; return new F(); } function create(parent,test) { var f = obj(parent.prototype); //建立對象 f.constructor = test; //加強對象 } function Parent(name){ this.name = name; this.arr = ['brother','sister','parents']; } Parent.prototype.run = function () { return this.name; }; function Child(name,age){ Parent.call(this,name); this.age =age; } inheritPrototype(Parent,Child); //經過這裏實現繼承 var test = new Child('peter',20); test.arr.push('new'); console.log(test.arr); //brother,sister,parents,new console.log(test.run()); //只共享了方法 var test2 = new Child('jack',22); console.log(test2.arr); //引用問題解決
call和apply能夠用來改變函數中this
的指向:
// 定義一個全局函數 function f () { console.log(this.name); } // 定義一個全局變量 var name = 'peter'; var obj = { name: 'jack'; }; f.apply(window); //apple, 此時this 等於window 至關於window.f() f.apply(obj); //jack, 此時this === obj 至關於obj.f()