1. 程式人生 > 其它 >JavaScript之原型與原型鏈

JavaScript之原型與原型鏈

前言

JavaScript常被描述為一種「基於原型的語言」——每個物件都擁有一個「原型物件」,物件以其原型為模板、從原型繼承屬性和放法。原型物件也可能擁有原型,並從中繼承屬性和方法,一層一層以此類推。這種關係常被稱為「原型鏈」,它解釋了為何一個物件會擁有定義在其他物件中的屬性和方法。

準確的說,這些屬性和方法定義在Object建構函式prototype屬性上,而非物件例項本身。

四句話道破原型與原型鏈:

  • 每個函式(類)天生自帶一個屬性prototype,屬性值是一個物件,裡面儲存了當前類供例項使用的屬性和方法 「(顯示原型)」
  • 在瀏覽器預設給原型開闢的堆記憶體中有一個constructor
    屬性:儲存的是當前類本身(⚠️注意:自己開闢的堆記憶體中預設沒有constructor屬性,需要自己手動新增)「(建構函式)」
  • 每個物件都有一個__proto__屬性,這個屬性指向當前例項所屬類的原型(不確定所屬類,都指向Object.prototype「(隱式原型)」
  • 當你試圖獲取一個物件的某個屬性時,如果這個物件本身沒有這個屬性,那麼它會去它的隱式原型__proto__(也就是它的建構函式的顯示原型prototype)中查詢。「(原型鏈)」

建構函式,原型與例項的關係:

每個建構函式(constructor)都有一個原型物件(prototype)原型物件(prototype)都包含一個指向建構函式(constructor)

的指標,而例項(instance)都包含一個指向原型物件(__proto__)的內部指標

prototype(顯式原型)

每個函式都有一個prototype屬性

//建構函式(類)
functionPerson(name){
this.name=name
}
//new了一個例項(物件)
varperson=newPerson('南玖')
console.log(person)//Person{name:'南玖'}
console.log(Person.prototype)//建構函式(類)的原型----->物件
Person.prototype.age=18//建構函式原型
console.log(person.age)//18

上面我們把這個函式Person的原型打印出來了,它指向的是一個物件,並且這個物件正是呼叫該建構函式而建立的例項的原型

上面這張圖表示的是建構函式與例項原型之間的關係,所以我們知道了建構函式的prototype屬性指向的是一個物件。

那例項與例項原型之間的關係又是怎樣的呢?這裡就要提到__proto__屬性了

__proto__(隱式原型)

從上面四句話中我們可以知道這是每一個Javascript物件(除null)都具有的一個屬性,這個屬性會指向該物件的原型(也就是例項原型)

因為在JavaScript中沒有類的概念,為了實現類似繼承的方式,通過__proto__將物件和原型聯絡起來組成原型鏈,的以讓物件訪問到不屬於自己的屬性。

那麼我們就能夠證明例項與例項原型之間的關係

console.log(person.__proto__)//例項(物件)的原型--->物件

console.log(person.__proto__===Person.prototype)//例項的原型與建構函式的原型相等

從上圖我們可以看出例項物件與建構函式都可以指向原型,那麼原型能不能指向建構函式或者是例項呢?

constructor(建構函式)

原型是沒有屬性指向例項的,因為一個建構函式可以建立多個例項物件;

從前面的四句話中我們知道「在瀏覽器預設給原型開闢的堆記憶體中有一個constructor屬性」,所以原型也是可以指向建構函式的,這個屬性就是「constructor」

於是我們可以證明一下觀點:

console.log(Person.prototype.constructor)//例項的顯式原型的建構函式ƒPerson(name){this.name=name}
console.log(person.__proto__.constructor)//例項的隱式原型的建構函式ƒPerson(name){this.name=name}
console.log(person.__proto__.constructor===Person.prototype.constructor)//true例項原型的建構函式與類的建構函式相等
console.log(Person===Person.prototype.constructor)//true

例項物件的__proto__是如何產生的?

我們知道當我們使用new 操作符時,生成的例項物件就擁有了__proto__屬性

functionFoo(){}
//這個函式時Function的例項物件
//function是一個語法糖
//內部其實呼叫了newFunction()

所以可以說,在new的過程中,新物件被添加了__proto__屬性並且連結到了建構函式的原型上。

new的原理

說簡單點可以分為以下四步:

  • 新建一個空物件
  • 連結原型
  • 繫結this,執行建構函式
  • 返回新物件
functionmyNew(){
//1.新建一個空物件
letobj={}
//2.獲得建構函式
letcon=arguments.__proto__.constructor
//3.連結原型
obj.__proto__=con.prototype
//4.繫結this,執行建構函式
letres=con.apply(obj,arguments)
//5.返回新物件
returntypeofres==='object'?res:obj
}

原型鏈

說完了原型,我們再來看看什麼是原型鏈?先來看一張圖:

這張圖中,由__proto__串起來的鏈式關係,我們就稱它為原型鏈

原型鏈的作用

原型鏈決定了JavaScript中繼承的實現方式,當我們訪問一個屬性時,它的查詢機制如下:

  • 訪問物件例項屬性,有的話直接返回,沒有則通過__proto__去它的原型物件上查詢
  • 原型物件上能找到的話則返回,找不到繼續通過原型物件的__proto__查詢
  • 一直往下找,直到找到Object.prototype,如果能找到則返回,找不到就返回undefined,不會再往下找了,因為Object.prototype.__proto__是null,說明了Object是所有物件的原型鏈頂層了。

從圖中我們可以發現,所有物件都可以通過原型鏈最終找到 Object.prototype ,雖然 Object.prototype 也是一個物件,但是這個物件卻不是 Object 創造的,而是引擎自己建立了 Object.prototype 。所以可以這樣說,所有例項都是物件,但是物件不一定都是例項。

建構函式的__proto__是什麼呢?

由上面的原型鏈的解釋,我們應該能夠理解建構函式的__proto__的,在JavaScript中所有東西都是物件,那麼建構函式肯定也是物件,是物件就有__proto__

functionPerson(){}
console.log(Person.__proto__)
console.log(Function.prototype)
console.log(Person.__proto__===Function.prototype)//true

「這也說明了所有函式都是Function的例項」

那這麼理解的話,Function.__proto__豈不是等於Function.prototype。。。。我們不妨來列印一下看看

Function.__proto__===Function.prototype//true

打印出來確實是這樣的。難道 Function.prototype 也是通過 new Function() 產生的嗎?

答案是否定的,這個函式也是引擎自己建立的。首先引擎建立了 Object.prototype ,然後建立了 Function.prototype ,並且通過 __proto__ 將兩者聯絡了起來。這裡也很好的解釋了上面的一個問題,為什麼 let fun = Function.prototype.bind() 沒有 prototype 屬性。因為 Function.prototype 是引擎創建出來的物件,引擎認為不需要給這個物件新增 prototype 屬性。

總結

  • Object 是所有物件的爸爸,所有物件都可以通過 __proto__ 找到它
  • Function 是所有函式的爸爸,所有函式都可以通過 __proto__ 找到它
  • Function.prototypeObject.prototype 是兩個特殊的物件,他們由引擎來建立
  • 除了以上兩個特殊物件,其他物件都是通過構造器 new 出來的
  • 函式的 prototype 是一個物件,也就是原型
  • 物件的 __proto__ 指向原型, __proto__ 將物件和原型連線起來組成了原型鏈
作者:前端南玖 出處:https://www.cnblogs.com/songyao666/

-------------------------------------------

個性簽名:智者創造機會,強者把握機會,弱者坐等機會。做一個靈魂有趣的人!

如果覺得這篇文章對你有小小的幫助的話,可以關注下方公眾號,在該公眾號同樣會推送技術文章給大家,謝謝~

歡迎加入前端技術交流群:928029210