看了10篇文章,我寫下了這篇有關原型和原型鏈的筆記

相信你們確定見過下面這幾個單詞,可是有時候又是傻傻分不清楚,不知道這幾個單詞究竟是作啥的,又有啥區別.今天咱們就來好好的瞧一瞧,剖析剖析它們.它們分別是 prototyoe, __proto__, constructor.javascript

prototype

在JavaScript中,每個對象(除了null)在建立的時候都會與之關聯另外一個對象,這另外一個對象就是咱們所說的原型,每個對象都會從原型"繼承"屬性.這裏的繼承並非真繼承.由於繼承意味着複製操做,然而JavaScript默認並不會複製對象的屬性.這裏只是在兩個對象之間創建一個關聯.這樣,一個對象就能經過委託訪問另外一個對象的方法或屬性. 全部函數都有prototype屬性,稱之爲原型屬性(也叫顯式原型),而普通對象是沒有prototype的. 雖然函數也是對象,可是咱們這裏的普通對象顯然是不包含函數的.java

console.log(({}).prototype)  // undefined
console.log((function(){}).prototype)  // {constructor: ƒ}
複製代碼

其實,這個prototype屬性指向一個對象,這個對象就是調用該構造函數而建立的實例的原型,其中包含了一些屬性方法,而這些屬性方法可讓全部的實例共享訪問.bash

function Person(name){
  this.name = name 
}
Person.prototype.say = function(){
  console.log('Hello World')
}
let p = new Person('zhangsan')
let p2 = new Person('lisi')
console.log(p.say === p2.say)   // true
複製代碼

Person是個函數,經過構造調用的方式生成了pp2兩個對象.而Person的原型對象上又定義了一個say方法,從運行結果看,pp2兩個實例對象均可以調用say方法輸出Hello World,而且p.say === p2.say的結果爲true也證實了它們是共享訪問. 下面咱們來看關係圖: 函數

在這裏插入圖片描述

proto

在JavaScript中,全部對象(除了null)都會有一個內部屬性__proto__(也叫隱式原型),該屬性指向被構造調用函數的原型對象ui

function Person(){} 
let p = new Person()
console.log(p.__proto__ === Person.prototype)
複製代碼

由此,咱們又能夠在關係圖上面再添加點東西:從實例指向原型對象 this

在這裏插入圖片描述

上面也說到了,全部對象(除了null)都有__proto__,那麼Person.__proto__又指向什麼呢?咱們在上面的代碼後面添加以下spa

console.log(Person.__proto__  === Function.prototype)  // true
複製代碼

這是由於函數也是一種對象,能夠認爲Person函數是調用了內置構造函數Function後生成的實例,因此上面的結果爲true 咱們再來看下面代碼prototype

let num = new Number(1)
console.log(num.__proto__ === Number.prototype)  // true
let str = new String('Hello')
console.log(str.__proto__ === String.prototype)  // true
let bool = new Boolean(true)
console.log(bool.__proto__ === Boolean.prototype)  // true
let f = new Function()
console.log(f.__proto__ === Function.prototype)  // true
console.log(Function.__proto__ === Function.prototype)  // true 這個我目前還不太清楚是爲何 QAQ
複製代碼

能夠得出一個結論:3d

let o = new F()
o.__proto__ === F.prototype
複製代碼

或者:code

o.__proto__ === o.constructor.prototype  // 這個緣由在後面的constructor中會提到
複製代碼

注意: 使用__proto__是有爭議的,所以不鼓勵使用它,如今更推薦使用Object.getPrototypeOf/Reflect.getPrototypeOf.參考地址:MDN

constructor

默認狀況下,每一個原型對象都會自動得到一個constructor屬性,指向其關聯的構造函數.

function Person(){} 
console.log(Person.prototype.constructor === Person) 
let p = new Person()
// 使用 getPrototypeOf 方法還能獲取對象的原型
console.log(Object.getPrototypeOf(p) === Person.prototype)  // true
複製代碼

咱們再來看下面代碼

function Person(){}
let p = new Person()
console.log(p.constructor === Person)  // true
複製代碼

實例p並無constructor屬性,但它從原型Person.prototype中去讀取,所以上面的輸出結果爲true.此時再回過頭去看o.__proto__ === o.constructor.prototype是否是就明白爲何了.

由此,咱們又能夠在關係圖上面再添加點東西:從原型對象指回被構造調用的函數

在這裏插入圖片描述

原型鏈

當訪問一個對象的方法或屬性時,若找不到則會查找與該對象關聯的原型中的方法(屬性),若仍是找不到,則繼續向上去原型的原型中查找,直到找到爲止,就這樣一直到最頂層.能夠理解__proto__一層一層的指向就是原型鏈了.

function Person(){} 
Person.prototype.say = function(){
  console.log('Hello World')
}
let p = new Person()
console.log(p.__proto__ === Person.prototype)  // true
console.log(Person.prototype.__proto__ === Object.prototype)  // true 由於全部的對象都是直接或間接繼承自Object
console.log(Object.prototype.__proto__)  // null 由於Object做爲最頂層對象,是原型鏈的最後一環.因此這裏的null表示了Object.prototype沒有原型
複製代碼

咱們在上面的代碼後面繼續添加以下代碼:

Object.prototype.say = function(){
  console.log('Hi Universe')
}
Object.prototype.shout = function(){
  console.log('Hello Universe')
}
p.say()  // Hello World
p.shout()  // Hello Universe
複製代碼

實例對象p自身沒有sayshout方法,向原型查找,在Person原型對象中找到了say,就再也不繼續向上查找,而shout方法不在Person原型對象中,所以繼續向上查找.最終在Object原型對象中找到了shout,發出了Hello Universe的吶喊 此時咱們的關係圖變成了以下:

在這裏插入圖片描述

補充

function Person(){} 
console.log(Person.__proto__ === Function.prototype)  // true
console.log(Object.__proto__ === Function.prototype)  // true
複製代碼

PersonObject,它們一個是自定義函數,另一個是內置構造函數,都算是函數實例,所以也是由Function構造生成. 最後,嘿嘿嘿,給你們出一道題目,看看你們知不知道爲何

console.log(Function instanceof Object)
console.log(Object instanceof Function)
複製代碼

總結: 有關原型和原型鏈的內容就暫時講到這裏了,相信你們對這幾個東西應該都有所瞭解了.確實,這塊內容中各類指來指去,關係圖中也是箭頭滿天飛,還須要你們好好消化一下.

相關文章
相關標籤/搜索