JavaScript語言的繼承機制---因爲JavaScript沒有‘類(class)’和‘實例(instance)’的概念,他是靠‘原型鏈(prototype chain)’模式來實現繼承的html
要理解JavaScript的繼承機制,首先,要清楚這幾個概念:構造函數,實例對象,prototype,constructor、__proto__ 以及他們之間的關係。java
構造函數:用來初始化新建立的對象的函數是構造函數。(你能夠把構造函數當作是「類」)chrome
實例對象:經過構造函數的new操做建立的對象是實例對象。能夠用同一個構造函數,構造多個實例對象。瀏覽器
prototype:每一個函數(固然包括構造函數)都有一個prototype屬性,它是一個對象。咱們稱它爲‘原型對象’ prototype:{...}app
constructor:每一個對象都有一個constructor屬性,它是一個函數:constructor:fn(){...} 它的做用就是指向該原型對象對應的構造函數。因爲實例對象能夠繼承原型對象的屬性,因此實例對象也擁有constructor屬性,一樣指向原型對象對應的構造函數函數
__proto__:每一個實例對象都有一個proto屬性,指向建立該實例對象的構造函數的原型對象(能夠理解爲 實例對象---》構造函數的原型對象)this
熟練使用JavaScript的繼承必需要對這幾個概念爛熟於心。spa
接下來結合圖示來分析一下。prototype
要想清楚的瞭解JavaScript的繼承機制,這些概念是必須爛熟於心的。3d
JavaScript的繼承 ---指的是子類繼承父類的屬性和方法
JavaScript的繼承特色:1.子類擁有父類全部的屬性和方法(代碼複用);2.子類能夠擴展本身的屬性和方法(更加靈活);3.子類能夠重寫父類的方法
JavaScript的繼承方式
1.組合繼承
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>組合繼承法</title> <script> function Person(name,age,gender,geyan){ this.name = name; this.age = age; this.gender = gender; this.geyan = geyan; } Person.prototype.say = function(){ console.log(`我叫${this.name},${this.gender},我今年${this.age}歲,個人人生格言是:${this.geyan}`); } var p1 = new Person('lx',18,'男','我姓謝,王菲的哪一個謝'); var p2 = new Person('lw',45,'男','你有困難我幫忙,我住隔壁,我姓王'); p1.say(); p2.say(); function Student(){ // 借用構造函數法 // Person.call(this,name,age,gender,geyan) Person.apply(this,arguments); } //原型鏈繼承法 Student.prototype = new Person(); var s1 = new Student('小明', 12, '男', '姐姐,約嗎'); s1.say(); console.log(s1); </script> </head> <body> </body> </html>
chrome顯示以下:
組合繼承主要使用:1.借用構造函數法--使用call方法或apply方法借用父類的屬性,使子類能夠使用。 2.原型鏈繼承法---將父類的實例對象賦給子類的原型對象,這樣,子類的原型對象就繼承到了父類的原型對象裏的方法(子類原型對象---》父類原型對象)
組合繼承的弊端:1.屢次執行了父類構造函數 在咱們的例子中, 每建立一個子類的實例sn,都要調用一次父類構造函數 ,很麻煩。 2.在原型對象裏生成多餘的屬性 緣由在於原型鏈繼承這一步中,父類的實例對象的屬性也被子類的原型對象繼承了。
知道他的弊端以後,就能加以改進了:將原型鏈繼承法用新的方法替代,這個新的方法叫作:原生式繼承法。這個方法不會生成多餘的屬性
來看改進過的代碼:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>組合繼承法</title> <script> function Person(name,age,gender,geyan){ this.name = name; this.age = age; this.gender = gender; this.geyan = geyan; } Person.prototype.say = function(){ console.log(`我叫${this.name},${this.gender},我今年${this.age}歲,個人人生格言是:${this.geyan}`); } var p1 = new Person('lx',18,'男','我姓謝,王菲的哪一個謝'); var p2 = new Person('lw',45,'男','你有困難我幫忙,我住隔壁,我姓王'); p1.say(); p2.say(); function Student(){ // 借用構造函數法 // Person.call(this,name,age,gender,geyan) Person.apply(this,arguments); } //原生式繼承法 function object(o){ function F(){} F.prototype = o; return new F(); } Student.prototype = object(Person.prototype); var s1 = new Student('小明',12, '男', '姐姐,約麼?'); s1.say(); console.log(s1); </script> </head> <body> </body> </html>
在chrome中顯示:
原生式繼承法的精髓在於:直接繼承父類的原型,而不是向原型鏈繼承那樣繼承整個父類。但這樣又帶來了一個新的問題:每次繼承都要寫一長串的代碼。
怎麼偷懶? 好在ES5給咱們拓展了關於原型的繼承方法----子類.prototype = Object.create(父類.prototype);
再看一下改進的代碼:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>組合繼承法</title> <script> function Person(name,age,gender,geyan){ this.name = name; this.age = age; this.gender = gender; this.geyan = geyan; } Person.prototype.say = function(){ console.log(`我叫${this.name},${this.gender},我今年${this.age}歲,個人人生格言是:${this.geyan}`); } var p1 = new Person('lx',18,'男','我姓謝,王菲的哪一個謝'); var p2 = new Person('lw',45,'男','你有困難我幫忙,我住隔壁,我姓王'); p1.say(); p2.say(); function Student(){ // 借用構造函數法 // Person.call(this,name,age,gender,geyan) Person.apply(this,arguments); } //ES5的原型使繼承法 Student.prototype = Object.create(Person.prototype); var s1 = new Student('小明',12,'男','姐姐,約麼?'); s1.say(); console.log(s1); </script> </head> <body> </body> </html>
在chrome中顯示:
這種原型式繼承法+借用構造函數法的強力組合就是JavaScript繼承方式的第2種方法 叫作寄生繼承。這也是最終推薦的繼承方式。
你覺得結束了嗎??
並
沒
有
!
講完了ES5,怎能不講講ES6? ES6給咱們準備了一個「大禮包」!!
開頭說到,JavaScript沒有「類」的概念。如今有了(JavaScript一直在成長啊)!!ES6中用關鍵字 class 來定義一個類
先上代碼:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>ES6中的繼承方式</title> <script> // 類: 使用class定義了一個類 Person class Person{ // 構造函數 全部的屬性都放在constructor函數裏(模仿java) constructor(name,age){ this.name = name; this.age = age; } // 全部的方法寫在函數constructor外 注意:並不須要聲明方法 直接 say(){...}的形式 至關於 Person.prototype.say = function(){...} say(){ console.log('say,say'); } // 靜態方法 只會被子類繼承, 不會被子類的實例繼承 static sing(){ console.log(`我叫${this.name},我會唱歌`); } } // Person.prototype.say = function(){} var p1 = new Person('lx',18); p1.say(); // 靜態方法的調用 Person.sing(); //使用extends去繼承父類 class 子類 extends 父類{...} class Student extends Person{ constructor(name,age){ super(name,age); } } var s1 = new Student('小紅',12); Student.sing(); s1.say() console.log(p1,s1) </script> </head> <body> </body> </html>
在chrome中顯示:
是否是很強大!!! 考慮到部分瀏覽器還未兼容ES6的標準。因此仍是推薦使用ES5的寄生繼承。
JavaScript的繼承機制就暫告一段落了。有問題之後補充 ----2017-03-21-20:10