相信你們確定見過下面這幾個單詞,可是有時候又是傻傻分不清楚,不知道這幾個單詞究竟是作啥的,又有啥區別.今天咱們就來好好的瞧一瞧,剖析剖析它們.它們分別是
prototyoe
,__proto__
,constructor
.javascript
在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
是個函數,經過構造調用的方式生成了p
和p2
兩個對象.而Person
的原型對象上又定義了一個say
方法,從運行結果看,p
和p2
兩個實例對象均可以調用say
方法輸出Hello World
,而且p.say === p2.say
的結果爲true
也證實了它們是共享訪問. 下面咱們來看關係圖: 函數
在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
屬性,指向其關聯的構造函數.
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
自身沒有say
和shout
方法,向原型查找,在Person原型對象中找到了say
,就再也不繼續向上查找,而shout
方法不在Person原型對象中,所以繼續向上查找.最終在Object原型對象中找到了shout
,發出了Hello Universe
的吶喊 此時咱們的關係圖變成了以下:
function Person(){}
console.log(Person.__proto__ === Function.prototype) // true
console.log(Object.__proto__ === Function.prototype) // true
複製代碼
Person
和Object
,它們一個是自定義函數,另一個是內置構造函數,都算是函數實例,所以也是由Function
構造生成. 最後,嘿嘿嘿,給你們出一道題目,看看你們知不知道爲何
console.log(Function instanceof Object)
console.log(Object instanceof Function)
複製代碼
總結: 有關原型和原型鏈的內容就暫時講到這裏了,相信你們對這幾個東西應該都有所瞭解了.確實,這塊內容中各類指來指去,關係圖中也是箭頭滿天飛,還須要你們好好消化一下.