我記得初學js時,最難懂的概念就是js的原型,並且這個概念在筆試面試中經常提到,
所以今天咱們把這個概念拿出來,好好聊一聊。面試
在仔細講解以前,咱們先來看一道題,這道題來自JavaScript高級程序設計中原型鏈那一節:算法
function SuperType(){ this.property = true; } SuperType.prototype.getSuperValue = function(){ return this.property; }; function SubType(){ this.subproperty = false; } SubType.prototype = new SuperType(); SubType.prototype.getSubValue = function (){ return this.subproperty; }; var instance = new SubType(); alert(instance.getSuperValue());
請你們猜一猜最後alert出的結果是什麼?數據結構
你們先思考一下再看下面的內容。閉包
若是上面的題沒解出來,也不要灰心喪氣,由於有可能有的人解出來的結果也不必定對。
要想弄明白原型等一系列概念,其實只須要記住這幾個知識點。函數
_proto_
:任何一個對象Object都有_proto_,它是每個對象的私有屬性,是天生自帶的。prototype
:不是任何對象都有prototype,只有構造函數有prototype,是後天賦予的。學習
其實只須要記住一句話:調用一個對象的屬性或方法,一但這個對象中沒有,就去這個對象的_proto_中查找。這個對象的_proto_指向本身構造函數的prototype屬性
而後咱們來看一張圖:this
再來看一個例子:spa
var Person = function () { this.sleep = 'Zzzzz...' } Person.prototype.sayHello = function () { console.log('hello world'); } var zhangsan = new Person(); zhangsan.sayHello();
其實zhangsan
這個對象下面只有一個sleep
屬性,是沒有sayHello
方法的。
可是經過原型鏈查找會查zhangsan._proto_
也就是查找它的構造函數的Person.prototype
,Person.prototype
下有sayHello
這個方法,因此會在控制檯輸出hello world
因此啦,萬變不離其宗!prototype
除了最新的ES6外,js實際上是沒有繼承和類的概念的,所以想要達到js的繼承就要經過模擬的方式。
這裏將主要介紹三種繼承的方式:純原型鏈繼承,借用構造函數繼承,組合繼承設計
多說無益,來個簡單暴力直接的方式,直接上代碼:
function Father() { this.likeFood= ['牛排','餃子','啤酒','可樂'] } Father.prototype.saylikeFood = function () { console.log(this.likeFood); }; function Son() { } Son.prototype = new Father(); var zhangsan = new Son(); zhangsan.likeFood.push('西瓜'); zhangsan.saylikeFood(); // ["牛排", "餃子", "啤酒", "可樂", "西瓜"]
Ok,這就是純原型鏈的繼承方式,其實說白了就是把繼承的對象的prototype
等於繼承自構造函數的實例。
可是這樣的方式有問題。接着上面的代碼的後面咱們再寫:
var lisi = new Son(); lisi.saylikeFood(); // ["牛排", "餃子", "啤酒", "可樂", "西瓜"]
看出問題了吧,zhangsan直接修改了其構造函數的likeFood,
致使咱們再實例的對象也收到了修改的影響,
所以這種繼承方式有缺陷
仍是多說無益,咱們直接來看例子,上代碼:
function Father(name) { this.name = name; this.sayName = function () { console.log(this.name); } } function Son(name, age) { Father.call(this, name); this.age = age; } var zhangsan = new Son('zhangsan', 17);
這種繼承方式並無利用到原型以及原型鏈的概念,它主要利用call的特性,call的第一個參數傳入this,後面的參數傳入函數所需的參數。
這種方式歸根結底其實就是在實例一個對象的時候,向這個對象的上面添加所需的屬性和方法。
不信的話能夠輸出zhangsan
看一下
可是其實這種方式也有問題,什麼問題呢?
按照這種方式,每次new
一個對象,就是實例化一個對象,都會向這個對象身上添加一堆屬性和方法。
添加屬性是沒問題的,可是每次在對象身上添加的方法,這個函數就要重寫一次。
函數不能進行復用,這就是最大的問題!
此次要多說兩句,組合繼承其實分別是擁有以上兩種方法的優勢,同時也規避了以上兩種方法的缺點。
這種方法的應用是最普遍的,是最廣泛的,舉個栗子來看一下:
function Father(name) { this.name = name; } Farther.prototype.sayName = function () { console.log(this.name); } function Son(name, age) { Farther.call(this, name); this.age = age; } Son.prototype = new Father(); Son.constructor = Son; Son.prototype.sayage = function () { console.log(this.age); } var zhangsan = new Son();
來看一下,這種方式不錯吧!
其實除了組合繼承外,還有兩種繼承方式:原型式繼承和寄生式繼承,
這兩種方式感興趣的同窗能夠自行了解一下
抱歉,這篇這麼遲纔出來,其實應該早早完事的。
我最近買了一本《學習JavaScript數據結構與算法》再看,我準備新開闢一個專欄來寫一下個人學習筆記。
下一次我決定和你們講講閉包的事。
本期的內容是原創的,但其實主要是看了《JavaScript高級程序設計》的啓發!
建議你也去看書,讀一下這本書的第六章的繼承那一部分,相信你也會深有啓發。
加油!