首頁 > 軟體

JavaScript深入淺出__proto__和prototype

2022-05-24 22:00:56

首先我們先記住幾個知識點:

  • 每個函數都有一個prototype屬性
  • 每個物件都有一個__proto__屬性(null除外)
  • 函數也是物件

建構函式和範例

首先我們通過下面的例子瞭解些基本的概念

function Person() {
}
var person1 = new Person()
  • 使用 new 建立物件的函數就是建構函式、建立出的物件就是建構函式的範例物件
  • 本例中:Person 就是一個建構函式,我們使用 new 建立了一個範例物件 person1
  • 建構函式大寫只是約定俗成的習慣,實際上任何可以使用 new 運運算元的函數都可以是建構函式

prototype

function Person() {
}
Person.prototype.name = 'Person'
var person1 = new Person()
console.log(person1.name) // Person
console.log(Person.prototype)

  • Person 的 prototype 屬性指向的是 Person.prototype 的原型物件
  • 在這個例子中可以看出,person1 本身並沒有 name 屬性,存取的其實是 Person 中prototype 的 name
  • 從這裡可以得出必然有一種關係把person1Person關聯起來,使得person1可以存取到Person中prototype的屬性值

帶著上面的疑問我們繼續下面的例子

__proto__prototype的關係

function Person() {
}
Person.prototype.name = 'Person'
var person1 = new Person()
console.log(person1)
console.log(person1.__proto__.name === Person.prototype.name) // true
console.log(person1.__proto__ === Person.prototype) // true

一開始我們就提到,每個物件都有一個__proto__屬性(null除外),通過這個例子我們發現person1的__proto__屬性下有個name屬性,正好是Person.prototype.name的值,由此我們可以看出範例person1是通過__proto__存取的建構函式Person的prototype屬性

根據上面的結論,我們很容易得出以下關係圖

由此我們很容易得出,多個範例物件之間通過__proto__進行關聯,可以通過__proto__共用Person.prototype上的屬性

constructor

既然建構函式和範例都可以指向原型,那麼原型是否有屬性指向建構函式或者範例呢? 通過上面的例子,我們發現除了__proto__,還有一個constructor屬性

function Person() {
}
Person.prototype.name = 'Person'
var person1 = new Person()
console.log(Person) // ƒ Person() {}
console.log(Person.prototype.constructor) // ƒ Person() {}
console.log(person1.__proto__.constructor) // ƒ Person() {}
// 由此我們發現`constructor`屬性指向的是Person建構函式本身,不難得出以下結論
console.log(Person === Person.prototype.constructor) // true

由此我們可以得出以下關係圖:

原型物件的原型

  • 原型也是一個物件,一開始就提到,每個物件都有一個__proto__屬性(null除外),因此原型物件也是有__proto__
  • 實際上原型物件就是通過 Object 建構函式生成的
var prototypeObj = new Object()
console.log(prototypeObj.__proto__ === Object.prototype) // true

到此我們可以得出一個新的關係圖

到這裡大家可能就有疑惑了,這樣不就無限迴圈了嗎,Object.protoType__proto__又是什麼呢?

console.log(Object.protoType.__proto__) // null
console.log(Object.protoType.__proto__ === null) // true

所以 Object.prototype 為null,屬性查詢到這裡也就結束了

原型鏈

由上面的例子我們得知,在查詢物件的屬性時,優先查詢範例物件的屬性,查詢不到時就會通過__proto__ 查詢 prototype原型物件的屬性,還查不到會通過prototype的__proto__繼續查詢,直到Object.protoType.__proto__為止

圖中由__proto__組成的紅色鏈路就是我們所說的原型鏈

擴充套件知識

關於 Object 和 Function

既然函數也是物件,物件都有 __proto__ ,那麼 Object 和 Function 的 __proto__ 屬性又是什麼呢?

  • Object物件是由Function建構函式建立的
  • Function的原型物件Function.prototype是由Object建構函式建立的
  • 【按照原型的定義,可以理解為】Function物件是由Function建構函式本身建立

根據原型鏈的相關知識,範例物件的 __proto__ 指向建構函式的原型物件 prototype

// Object物件由Function建構函式建立
Object.__proto__ === Function.prototype // true
// Function的原型物件`Function.prototype`是由Object建立
Function.prototype.__proto__ === Object.prototype // true
// Function由Function本身建立(按照原型的定義可以簡單這麼去理解,這裡不做深究)
Function.__proto__ === Function.prototype // true
Object.getPrototypeOf(Function) === Function.prototype // true

由此我們可以得出最終的關係圖:

關於 getPrototypeOfisPrototypeOfinstanceof

function Person() {
}
var person1 = new Person()
// Object.getPrototypeOf(obj) 返回obj範例物件的原型(obj.__proto__)
console.log(Object.getPrototypeOf(person1) === Person.prototype) // true
// Object.isPrototypeOf(obj),如果obj.__proto__和Object.prototype在一條原型鏈上(或者理解為Object為obj的建構函式或父級建構函式),則返回true
console.log(Object.prototype.isPrototypeOf(person1)) // true
console.log(Object.prototype.isPrototypeOf(Person.prototype)) // true
console.log(Person.prototype.isPrototypeOf(person1)) // true
// (obj instanceof Object) 同isPrototypeOf類似,obj.__proto__和Object.prototype在一條原型鏈上,則返回true
console.log(person1 instanceof Person) // true
console.log(person1 instanceof Object) // true

總結

  • 每個函數都有一個prototype屬性,值是一個原型物件
  • 每個物件都有一個__proto__屬性(null除外),指向建構函式的原型prototype
  • 建構函式的原型物件的constructor指向建構函式本身
  • 原型物件是由Object建構函式範例化產生的,所以原型物件的__proto__指向Object的原型物件Object.prototype
  • Object建構函式的原型物件為null
function Person() {}
var person = new Person()
console.log(person.__proto__ === Person.prototype) // true
console.log(Person === Person.prototype.constructor) // true
console.log(Object.prototype.__proto__ === null) // true
// 推導person.constructor等於person.__proto__.constructor等於Person.prototype.constructor
console.log(person.constructor === Person.prototype.constructor) // true

到此這篇關於JavaScript深入淺出__proto__和prototype的文章就介紹到這了,更多相關JS proto 和 prototype內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com