【JS】從Function說原型
前言
最近小組要做學習與交流,本來準備做ES6的使用,組員說先講下JS的原型。琢磨了下,一臉懵,平時在用這個,但是讓我去講,還真不知道怎麼講,趁機再梳理一次。
Function
平時會使用大量的Function,但是真的懂Function嗎?
console.log(typeof Object);
console.log(typeof Function);
上面的輸出是什麼呢?機智的小夥伴已經知道是,對,是function。是不是感覺很奇怪?
JS物件
首先,要明確一點,JS中所有的函式都是Function的例項。要注意的一點是,Function自身能夠生成自身,即Function的原型就是Function。
JS物件包含三種:
- 本地物件。獨立於宿主環境(瀏覽器)的物件。包括Object、Array、Date、RegExp、Function、Error、Number、String、Boolean。
- 內建物件:包括Math、Global(window,在js中就是全域性變數),使用的時候不需要new。
- 宿主物件:包括自定義物件、DOM、BOM。
一張圖
在JS中Object、Function既是物件、也是函式。
prototype
prototype表明例項自身的屬性。如上圖中function Foo,Foo.prototype中記錄中Foo自身的原型屬性,其中constructor表明Foo的構造器。
_proto_
_proto_ 表明例項的原型是誰,如上圖示:foo的_proto_指向Foo.prototype。
prototype與_proto_的區別
- prototype表明例項自身的屬性
- _proto_表明例項的原型是誰
- 構造器具有prototype和_proto_,單純例項只具有_proto_
原型鏈
原型鏈就是原型的集合,一級一級向上查詢,直到查詢到null為止。
屬性查詢
- 在訪問物件的某個成員的時候會先在物件中找是否存在
- 如果當前物件中沒有就在建構函式的原型物件中找
- 如果原型物件中沒有找到就到原型物件的原型上找
- 知道Object的原型物件的原型是null為止
比如我們想要查詢例項foo原型上的屬性,首先先找它的原型屬性foo._proto_,即Foo.prototype。接著繼續向上查詢Foo.prototype._proto_,即Object.prototype。繼續向上Object.prototype._proto_,找到了null。查詢結束。
原型繼承
需要注意的是原型繼承與類繼承是不同的概念。下面看一個demo:
Function.prototype.hh = 123;
function Foo(){}
let foo = new Foo();
console.log(Foo.hh)
console.log(foo.hh)
輸出是:
123
undefined
你思考的結果是否是和實際輸出一樣呢?
按照我們面向物件的思維,Foo是Function的例項,foo是Foo的例項,foo應當是應該繼承Function的屬性和Foo的屬性的,但是很顯然,並不是。這是類繼承和原型繼承很大的不同所在。
原因還是要從上面的那張圖說起,此時foo的原型鏈應該是Foo.prototype + Object.prototype + null,和Function.prototype並沒有什麼關係。這一點是需要特別注意的。
如果能搞懂這一點,那麼原型繼承也就懂,demo就不寫了,網上一堆。
原型鏈上的元素
原型鏈上的元素都是物件,原型鏈必須是有限長度,且原型鏈以null為終結,那麼其它位置上都是些什麼呢。
倒數第一位
null,無可否認。
倒數第二位
從實現上來看,倒數第二位與倒數第一位都是唯一的。因為原型鏈上所有的元素都是物件,所以倒數第二個元素應該是所有物件的基礎物件。即Object.prototype。
倒數第三位
那麼倒數第三個元素是不是固定的呢?不是。從倒數第二個元素是Object.prototype來看,通過{}字面量和new Object()建立的物件都在倒數第三這個位置。另外還有兩個特例,一個是除內建函式之外的內建物件,如Math、JSON;一個是除Object之外的內建函式的prototype屬性指向的物件,如Function.prototype。即Math、JSON、(Function、Array、String、Boolean、Number、Date、RegExp、Error).prototype。
倒數第四位
所有除Object之外的內建函式作為構造器呼叫時生成的例項物件都在倒數第四位。即Object、Function、Array、String、Boolean、Number、Date、RegExp、Error。