關於JS原型和原型鏈我以前剛學js就有寫過一篇學習筆記形式的博客,但前兩天翻出來一看——什麼鬼,這是我寫的嗎?本身都看不懂了,因而我從新整理思路,今天 「前端料包」 帶來第四篇。前端
原型和原型鏈是js中的難點也是重點,有句話說,沒理解透原型和原型鏈,就算尚未真正入門的前端。而且原型和原型鏈會是面試中必不可少的話題。看完這篇相信你能對原型和原型鏈會有個深入的理解。若是以爲本文對你有所幫助請點亮左邊的👍,不甚感激🙆面試
JS全部對象分爲函數對象和普通對象。凡是經過new Function()建立得來的對象都是函數對象。函數對象擁有prototype屬性和__proto__屬性。函數
prototype學習
JS設計之初爲了實現簡單繼承,引入了prototype屬性,也叫原型對象(顯式原型)。ui
//原型對象
function Animal(){};
console.log(typeof Animal.prototype) //Object
console.log(typeof Object.prototype) // Object
複製代碼
能夠看出,從本質上講,prototype是一個普通對象,是函數對象的構造函數建立的一個實例。至關於在Animal建立的時候,自動建立了一個它的實例,而且把這個實例賦值給了prototype。this
可是存在一個特例Function, Function.prototype是原型對象,本質倒是函數對象。做爲一個函數對象,又沒有prototype屬性。spa
console.log(typeof Function.prototype) // 特殊 Function
console.log(typeof Function.prototype.prototype) //undefined 函數對象卻沒有prototype屬性
複製代碼
__proto__屬性.net
全部的對象obj(null和undefined除外)都具備__proto__屬性(隱式原型),__proto__屬性在本質上爲一個指針,指向函數對象的prototype屬性。prototype
//建立構造函數
function Animal(name,age){
this.name = name;
this.age= age;
}
Animal.prototype = {
alertName(){
alert(this.name);
}
}
//建立實例
var dog = new Animal("大黃");
dog .print = function(){
alert(this.name);
}
dog.print(); //大黃
dog.alertName(); //大黃
複製代碼
print()方法是dog實例自己具備的方法,因此dog.print()
輸出「大黃」;alertName()
不屬於dog實例的方法,屬於構造函數的方法,dog.alertName()
也會輸出「大黃」,是由於dog實例繼承了構造函數的方法。設計
事實上,實例dog的隱式原型(__proto__
)指向它構造函數的顯式原型(prototype
),指向的意思是等價於,即
dog.__proto__ === Animal.prototype// true
複製代碼
咱們能夠經過一張圖來幫助理解記憶
顧名思義,構造器constructor就是用來構造函數對象的,constructor 屬性返回對建立此對象的函數對象的引用。通俗了講就是指向當前對象的爸爸
function Animal(){};
console.log(Animal.constructor===Function); //true
console.log(Animal.prototype.constructor===Animal); //true
複製代碼
函數Animal是由Function創造出來的,那麼它的constructor指向的Function,Animal.prototype
是由new Animal()的方式創造出來,那麼Animal.prototype.constructor
理應指向Animal。 一樣,咱們用圖來幫助記憶
留個思考題:
Animal.prototype._proto_.constructor
指向誰? ,歡迎在評論區留下你的答案
原型鏈是JS中實現繼承的主要方法。其基本思想就是讓一個引用類型繼承另外一個引用類型的屬性和方法。下面就是咱們最多見的組合繼承
function Animal(){
this.animalType = "animal";
}
Animal.prototype.getAnimalType = function(){
return this.animalType ;
}
function Dog(){
this.Dogtype = "dog";
}
Dog.prototype = new Animal();
Dog.prototype.getDogType = function(){
return this.Dogtype ;
}
var dahuang = new Dog();
alert(dahuang.getAnimalType ());// animal
複製代碼
dahuang.getAnimalType ()
打印結果爲animal,dahuang
自身沒有getAnimalType ()
方法,那麼就會去它的_proto_
(即它的構造函數的prototype
)中尋找,發現Dog中也沒有,因而順着_proto_
再往上找,在Animal.prototype.getAnimalType
找到,返回結果 。
若是Animal中仍是沒有,就接着往上找,一直到Object.prototype原型對象終止
總結得出原型鏈就是:由原型對象組成,每一個對象都有 __proto__
屬性,指向了建立該對象的構造函數的原型,__proto__
將對象鏈接起來組成了原型鏈。是一個用來實現繼承和共享屬性的有限的對象鏈。
b.prototype.x = 2
;可是這樣會形成全部繼承於該對象的實例的屬性發生改變。若是構造函數中有不少屬性和方法,那麼構造函數全部的實例化對象都是公用這些屬性和方法的,當有多個實例想用共用這些東西的時候,每一個實例都拷貝一份,就形成極大的資源浪費,那是否是能夠考慮存把這些須要共用的屬性和方法放到一個共同的東西上。這個共同的東西就是原型對象(prototype
)。
固然原型鏈實現繼承也會存在一些問題,最主要的問題來自包含引用類型的原型以及在繼承父類函數的時候調用了父類構造函數,致使子類的原型上多了不須要的父類屬性,存在內存上的浪費。其次就是在建立子類型的實例時,不能向超類型的構造函數中傳遞參數。解決辦法將在後續的文章中揭曉~
JavaScript的原型和原型鏈的確晦澀難懂,聽說搞了幾年前端的老司機都不必定繞的通,但經過實例和畫圖來幫助理解仍是能夠的。最後,小生乃前端小白一枚,寫文章的最初衷是爲了讓本身對該知識點有更深入的印象和理解,寫的東西也很小白,文中若有不對,歡迎指正~ 而後就是但願看完的朋友能夠點個喜歡,也能夠關注一波~ 我會持續輸出!