什麼是原型?原型是JS 基礎學習中我們沒有提及的概念,原型它是一個泛指,主要包含了 原型物件(prototype) , 物件原型(__proto__) , 原型鏈 等等,這些概念據統計也是面試中常問到的內容,這篇文章就帶大家了解並掌握原型的相關知識,讓大家不再迷惘。
我們學過很多物件導向的語言,例如java c++ 等等,但是JavaScript 是個例外,在ES6 之前,是沒有類別的概念的,那在之前我們要如何建立物件呢?原來在ES6 之前,我們是利用建構子來建立實例化物件的,建構子是一種特殊的函數,包含了物件的公共特徵,要配合new一起使用才有意義。
<script> function Animal(name,age){ //建構子字首大寫this.name=name; this.age=age; this.eat=function(){ console.log('我在吃東西'); } } var dog=new Animal('旺財',3) //要配合new 一起使用創建物件console.log(dog.name); console.log(dog.age); dog.eat() </script>
<script> function Animal(name,age){ this.name=name; this.age=age; } var dog=new Animal('旺財',3) console.log(dog.name); console.log(Animal.name); </script>
<script> function Animal(name,age){ this.name=name; this.age=age; } var dog=new Animal('旺財',3) Animal.color='黑色' console.log(Animal.color); console.log(dog.color); </script>
在開始將原型對像是什麼前,我們先說明一個案例,還是剛才的那個Animal 類,我們創建了多個實例化對象,輸出其實例化對象的兩個方法的比較,我們發現輸出了false,即二者的這個複雜資料類型的位址不同,什麼原因呢?
<script> function Animal(name,age){ this.name=name; this.age=age; this.eat=function(){ console.log('我在吃東西'); } } var dog=new Animal('旺財',3) var cat=new Animal('咪咪',3) var pig=new Animal('哼哼',3) var fish=new Animal('咕嚕',3) var sheep=new Animal('咩咩',3) console.log(dog.eat==cat.eat); </script>
在我們創建實例化對象的過程中,new 的過程首先會創建一個新對象,但是複雜數據類型會領開闢一塊空間存放(對象,方法),這就造成了構造函數內同樣的方法被開闢了無數塊內存,造成了內存的極度浪費
建構函式原型prototype 是建構函式內的一個屬性,其屬性是一個指針,指向一個對象,這個物件內存放的就是公共的方法,存在這個物件裡的方法,再透過建構函式建立實例化物件時就可以公共利用這一個方法了,不需要再對多個相同的複雜資料型別開闢多個重複的記憶體空間。就是為了解決上述存在的記憶體浪費的問題,其也可以直接稱為原型物件。
解決方案我們使用原型物件存放公共方法,並且讓實例化物件呼叫該方法,並且比較二者的位址是否相同
<script> function Animal(name,age){ this.name=name; this.age=age; } Animal.prototype.eat=function(){ console.log('我在吃東西'); } var dog=new Animal('旺財',3) var cat=new Animal('咪咪',3) dog.eat() cat.eat() console.log(dog.eat==cat.eat); </script>
我們發現不但成功地呼叫了這個方法,而且二者呼叫方法的位址是相同的,這就證明了,其公共的複雜資料型別只開闢了一塊記憶體空間,減少了之前公共方法寫在構造函數內部資源浪費的問題。
物件原型__proto__的作用是讓你搞清楚一個問題:為什麼給建構函數的prototype屬性添加的方法,實例化物件卻可以使用?這是因為每個物件都有一個__proto__屬性(注意前後都是兩個底線),這個屬性也是一個指針,指向的是其對應構造函數的原型對象prototype,這就解釋了為什麼實例化的對象可以去呼叫原型物件裡的方法。
我們要注意物件原型__protp__的作用只是為了給查找原型物件內的內容提供一個方向,我們不需要使用它,只需要記住它指向對應的建構函數的原型物件prototype 即可
物件原型__proto__ 身上和建構函式的原型物件prototype 身上都有一個constructor 屬性,之所以叫constructor叫建構函數,是因為這個屬性指向的是對應的建構子本身,其主要用來記錄實例化的物件引用於哪一個建構子
<script> function Animal(name,age){ this.name=name; this.age=age; } Animal.prototype.eat=function(){ console.log('我在吃東西'); } var dog=new Animal('旺財',4) console.log(dog.__proto__.constructor); console.log(Animal.prototype.constructor); </script>
我們發現列印出來結果確實為構造函數本身