以前分享了一篇文章JS原型鏈與繼承別再被問倒了,發現繼承的問題研究到後面,必定會觸及到Object.prototype和Function.prototype這些概念,爲了解答疑惑,本篇就抽絲剝繭,從prototype與__proto__來推出函數與對象的深層關係。
原文:詳解prototype與__proto__html
由上, prototype 只有函數纔有, 其餘(非函數)對象不具備該屬性. 而 __proto__ 是對象的內部屬性, 任何對象都擁有該屬性. git
下面咱們來吃個栗子幫助消化下:github
function Person(name){
this.name = name;
}
var p1 = new Person('louis');
console.log(Person.prototype);//Person原型 {constructor: Person(name),__proto__: Object}
console.log(p1.prototype);//undefined
console.log(Person.__proto__);//空函數, function(){}
console.log(p1.__proto__ == Person.prototype);//true複製代碼
吃栗子時咱們發現, Person.prototype(原型) 默認擁有兩個屬性:瀏覽器
而p1做爲非函數對象, 天然就沒有 prototype 屬性; 此處佐證了概念1
app
下面來看看__proto__屬性:函數
Person.__proto__ 屬性 指向的是一個空函數( function(){} ), 待會兒咱們再來研究這個空函數.post
p1.__proto__ 屬性 指向的是 構造器(Person) 的原型, 即 Person.prototype. 此處佐證了概念2
測試
這裏咱們發現: 原型鏈查詢時, 正是經過這個屬性(__proto__) 連接到構造器的原型, 從而實現查詢的層層深刻.ui
對 概念1
不太理解的同窗, 說明大家不會吃栗子, 我們忽略他們. 對 概念2
不太理解的同窗, 咱們來多吃幾個栗子, 邊吃邊想:this
var obj = {name: 'jack'},
arr = [1,2,3],
reg = /hello/g,
date = new Date,
err = new Error('exception');
console.log(obj.__proto__ === Object.prototype); // true
console.log(arr.__proto__ === Array.prototype); // true
console.log(reg.__proto__ === RegExp.prototype); // true
console.log(date.__proto__ === Date.prototype); // true
console.log(err.__proto__ === Error.prototype); // true複製代碼
可見, 以上經過 對象字面量
和 new + JS引擎內置構造器() 建立的對象, 無一例外, 它們的__proto__ 屬性所有指向構造器的原型(prototype). 充分佐證了 概念2
.
剛纔留下了一個問題: Person.__proto__ 指向的是一個空函數, 下面咱們來看看這個空函數到底是什麼.
console.log(Person.__proto__ === Function.prototype);//true複製代碼
Person 是構造器也是函數(function), Person的__proto__ 屬性天然就指向 函數(function)的原型, 即 Function.prototype.
這說明了什麼呢?
咱們由 "特殊" 聯想到 "通用" , 由Person構造器聯想通常的構造器.
這說明 全部的構造器都繼承於Function.prototype (此處咱們只是由特殊總結出了普適規律, 並無給出證實, 請耐心看到後面) , 甚至包括根構造器Object及Function自身。全部構造器都繼承了Function.prototype的屬性及方法。如length、call、apply、bind(ES5)等. 以下:
console.log(Number.__proto__ === Function.prototype); // true
console.log(Boolean.__proto__ === Function.prototype); // true
console.log(String.__proto__ === Function.prototype); // true
console.log(Object.__proto__ === Function.prototype); // true
console.log(Function.__proto__ === Function.prototype); // true
console.log(Array.__proto__ === Function.prototype); // true
console.log(RegExp.__proto__ === Function.prototype); // true
console.log(Error.__proto__ === Function.prototype); // true
console.log(Date.__proto__ === Function.prototype); // true複製代碼
JavaScript中有內置(build-in)構造器/對象共計13個(ES5中新加了JSON),這裏列舉了可訪問的9個構造器。剩下如Global不能直接訪問,Arguments僅在函數調用時由JS引擎建立,Math,JSON是以對象形式存在的,無需new。因爲任何對象都擁有 __proto__ 屬性指向構造器的原型. 即它們的 __proto__ 指向Object對象的原型(Object.prototype)。以下所示:
console.log(Math.__proto__ === Object.prototype); // true
console.log(JSON.__proto__ === Object.prototype); // true複製代碼
如上所述, 既然全部的構造器都來自於Function.prototype, 那麼Function.prototype 究竟是什麼呢?
咱們借用 typeof 運算符來看看它的類型.
console.log(typeof Function.prototype) // "function"複製代碼
實際上, Function.prototype也是惟一一個typeof XXX.prototype爲 「function」的prototype。其它的構造器的prototype都是一個對象。以下:
console.log(typeof Number.prototype) // object
console.log(typeof Boolean.prototype) // object
console.log(typeof String.prototype) // object
console.log(typeof Object.prototype) // object
console.log(typeof Array.prototype) // object
console.log(typeof RegExp.prototype) // object
console.log(typeof Error.prototype) // object
console.log(typeof Date.prototype) // object複製代碼
既然Function.prototype 的類型是函數, 那麼它會擁有 __proto__ 屬性嗎, Function.prototype.__proto__ 會指向哪裏呢? 會指向對象的原型嗎? 請看下方:
console.log(Function.prototype.__proto__ === Object.prototype) // true複製代碼
透過上方代碼, 且咱們瞭解到: Function.prototype 的類型是函數, 也就意味着一個函數擁有 __proto__ 屬性, 而且該屬性指向了對象(Object)構造器的原型. 這意味着啥?
根據咱們在 概念2
中瞭解到的: __proto__ 是對象的內部屬性, 它指向構造器的原型.
這意味着 Function.prototype 函數 擁有了一個對象的內部屬性, 而且該屬性還剛好指向對象構造器的原型. 它是一個對象嗎? 是的, 它必定是對象. 它必須是.
實際上, JavaScript的世界觀裏, 函數也是對象, 函數是一等公民.
這說明全部的構造器既是函數
也是一個普通JS對象
,能夠給構造器添加/刪除屬性等。同時它也繼承了Object.prototype上的全部方法:toString、valueOf、hasOwnProperty等。
函數的 __proto__ 屬性指向 Function.prototype, 如: Person.__proto__ —> Function.prototype
Function.prototype 函數的 __proto__ 屬性指向 Object.prototype, 如: Function.prototype.__proto__ —> Object.prototype.
那麼Object.prototype.__proto__ 指向什麼呢?
console.log(Object.prototype.__proto__ === null);//true複製代碼
因爲已經到頂, JS世界的源頭一片荒蕪, 居然什麼都沒有! 使人嗟嘆不已.
都說一圖勝千言, 我也不能免俗. 下面附一張 stackoverflow 上的圖:
這張圖也指出:
//雖然上面作過測試, 咱們仍是再次測試下
console.log(Object.__proto__ == Function.prototype);//true
console.log(Function.__proto__ == Function.prototype);//true複製代碼
因爲對象構造器 (Object) 也是構造器, 又構造器都是函數, 又函數是一等公民, 函數也是對象.
故, 對象構造器 (Object) 擁有3種身份:
構造器
函數
對象
推而廣之, 全部構造器都擁有上述3種身份.
因爲構造器是 對象
(身份3), 理所固然擁有 __proto__ 屬性, 且該屬性必定指向其構造器的原型, 也就是指向 函數
(身份2) 構造器(Function)的原型, 即 Function.prototype. 因而咱們證實了上面那句 全部的構造器都繼承於Function.prototype (身份1).
注: 上面代碼中用到的 __proto__ 目前在IE6/7/8/9中並不支持。IE9中可使用Object.getPrototypeOf(ES5)獲取對象的內部原型。
附上網友的疑問(問題提得特別好,問出了函數與對象最尖銳的歸宿問題):
問題背景:先有 Object.prototype(原型鏈頂端),Function.prototype 繼承 Object.prototype 而產生,最後,Function 和 Object 和其它構造函數繼承 Function.prototype 而產生。
如下是具體問題:
先有 Object.prototype,再有 Object,那麼先有 Object.prototype 裏面的這個 Object 表明的是什麼呢?
Function.__proto__=== Function.prototype
;
Function 構造函數的 prototype 屬性和__proto__屬性都指向同一個原型,是否能夠說 Function 對象是由 Function 構造函數建立的一個實例?
Object instanceof Function
// trueFunction instanceof Object
// true
Object 自己是構造函數,繼承了 Function.prototype; Function 也是對象,繼承了Object.prototype。感受成了雞和蛋的問題了。
好比說:function Person(){}
Person.prototype 是一個對象,爲何 Function.prototype 倒是一個函數呢,固然函數也是一個對象,爲何要這麼設計呢?
這裏是疑惑:
感受有些地方很難理解,總感受有種悖論同樣,還有人扯到羅素悖論,各有說辭,不知道樓主怎麼看。。。
通讀全文若是你能回答這些問題,說明看懂了本文。不然請容許我建議你再讀一篇,別打我。
---華麗麗的分割線---
如下是回答:
答1: 先有的必定是Object,它是BOM對象,打印出來爲:function Object() { [native code] }
,而後纔是Object.prototype。這不矛盾,請看分析。
首先:Object.create(null)你應該知道,它能夠建立一個沒有原型的對象。以下:Object.prototype.__proto__ == Object.create(null).__proto__
結果爲true,也就是說,Object.prototype是使用這種方式生成的,而後才綁在Object的prototype屬性上。爲何這裏沒有用===
,由於__proto__指向的是對象,對象是無法比較相等的。
要知道,對象能夠先生成,而後再賦予新的屬性。
答2: Function.__proto__=== Function.prototype
這沒毛病。
咱們都知道,Function是一個函數,只要是函數,它的__proto__就指向 Funtion.prototype. 這是繼承的設定。那麼咱們怎麼理解這種構造器自己就繼承自己的prototype屬性的現象呢?
Function生成時,是沒有__proto__屬性的,它是一個BOM對象,打印出來爲:function Function() { [native code] }
,Function.prototype一樣是BOM對象,打印出來爲:function () { [native code] }
,那麼能夠這麼理解:
Function的__proto__和prototype屬性都是後面才指向同一個BOM對象的。
答3: 這不是蛋和雞的問題,計算機中沒有蛋和雞。Object instanceof Function
// true 。Object是構造器函數,你必須認,有new Object()
爲證。
答2已經給告終論:只要是函數,它的__proto__就指向 Funtion.prototype。Object也是函數。所以它的__proto__屬性會指向Funtion.prototype。故而Object instanceof Function 爲 true。
答4: Function.prototype是一個函數,也是對象。這是天然的。
那麼爲何這麼設計呢?你注意到沒有 typeof Function.prototype === "function"
結果爲true。注意:只有Function的prototype是函數,其餘都是普通對象。它是一個橋樑,咱們一直說函數也是對象,只有這個知足了, 函數才能是對象,關於『js中函數是一等公民』的定理,Function.prototype同時是函數, 又是原型對象即是佐證。
說了說去,都回到了Function.prototype、Function和Object的問題上,本質上它們都是BOM對象,即它們都在瀏覽器global對象(這個js是訪問不到的)生成以前生成的,後面的指向關係,能夠認爲是js世界繼承規律的一種設定,有了這個設定,js遊戲才能玩下去。
本文就討論這麼多內容,你們有什麼問題或好的想法歡迎在下方參與留言和評論.
本文做者: louis
本文連接: louiszhai.github.io/2015/12/17/…
本次活動連接:juejin.im/post/58d8e9…
參考文章