首先要搞明白幾個概念:java
- 函數(function)
- 函數對象(function object)
- 本地對象(native object)
- 內置對象(build-in object)
- 宿主對象(host object)
function foo(){ } var foo = function(){ }
前者爲函數聲明,後者爲函數表達式。typeof foo
的結果都是function。函數
函數就是對象,表明函數的對象就是函數對象ui
官方定義, 在Javascript中,每個函數實際上都是一個函數對象.JavaScript代碼中定義函數,或者調用Function建立函數時,最終都會以相似這樣的形式調用Function函數:var newFun = new Function(funArgs, funBody)this
其實也就是說,咱們定義的函數,語法上,都稱爲函數對象,看咱們如何去使用。若是咱們單純的把它當成一個函數使用,那麼它就是函數,若是咱們經過他來實例化出對象來使用,那麼它就能夠當成一個函數對象來使用,在面向對象的範疇裏面,函數對象相似於類的概念。spa
var foo = new function(){ } typeof foo // object 或者 function Foo (){ } var foo = new Foo(); typeof foo // object
上面,咱們所創建的對象prototype
ECMA-262 把本地對象(native object)定義爲「獨立於宿主環境的 ECMAScript 實現提供的對象」。簡單來講,本地對象就是 ECMA-262 定義的類(引用類型)。它們包括:
Object,Function,Array,String,Boolean,Number
Date,RegExp,Error,EvalError,RangeError,ReferenceError,SyntaxError,TypeError,URIError.
咱們不能被他們起的名字是本地對象,就把他們理解成對象(雖然是事實上,它就是一個對象,由於JS中萬物皆爲對象),經過code
typeof(Object) typeof(Array) typeof(Date) typeof(RegExp) typeof(Math)
返回的結果都是function對象
也就是說其實這些本地對象(類)是經過function創建起來的,繼承
function Object(){ } function Array(){ } ...
能夠看出Object本來就是一個函數,經過new Object()以後實例化後,建立對象。相似於JAVA中的類。ip
ECMA-262 把內置對象(built-in object)定義爲「由 ECMAScript 實現提供的、獨立於宿主環境的全部對象,在 ECMAScript 程序開始執行時出現」。這意味着開發者沒必要明確實例化內置對象,它已被實例化了。ECMA-262 只定義了兩個內置對象,即 Global 和 Math (它們也是本地對象,根據定義,每一個內置對象都是本地對象)。
理清楚了這幾個概念,有助於理解咱們下面要講述的原型和原型鏈。
prototype屬性是每個函數都具備的屬性,可是不是一個對象都具備的屬性。好比
function Foo(){ } var foo = new Foo();
其中Foo中有prototype屬性,而foo沒有。可是foo中的隱含的__proto__屬性指向Foo.prototype。
foo.__proto__ === Foo.prototype
爲何會存在prototype屬性?
Javascript裏面全部的數據類型都是對象,爲了使JavaScript實現面向對象的思想,就必需要可以實現‘繼承’使全部的對象鏈接起來。而如何實現繼承呢?JavaScript採用了相似C++,java的方式,經過new的方式來實現實例。
舉個例子,child1,child2都是Mother的孩子,且是雙胞胎。(雖然不是很好,可是仍是很能說明問題的)
function Mother(name){ this.name = name; this.father = 'baba'; } var child1 = new Mother('huahua'); var child2 = new Mother('huahua');
若是有一天,發現孩子的父親實際上是Baba,那麼就要修改每個孩子的father屬性。
child1.father ='Baba'; console.log(child2.father) // baba
也就是說修改了其中一個孩子的father屬性不會影響到下一個,屬性的值沒法共享。
正是這個緣由才提出來prototype屬性,把須要共享的屬性放到構造函數也就是父類的實例中去。
__proto__
__proto__屬性是每個對象以及函數都隱含的一個屬性。對於每個含有__proto__屬性,他所指向的是建立他的構造函數的prototype。原型鏈就是經過這個屬性構件的。
想像一下,若是一個函數對象(也稱爲構造函數)a的prototype是另外一個函數對象b構建出的實例,a的實例就能夠經過__proto__與b的原型鏈起來。而b的原型其實就是Object的實例,因此a的實例對象,就能夠經過原型鏈和object的prototype連接起來。
function a(){ } function b(){ } var b1 = new b(); a.prototype = b1; var a1 = new a(); console.log(a1.__proto__===b1);//true console.log(a1.__proto__.__proto__===b.prototype) //true console.log(a1.__proto__.__proto__.__proto__===Object.prototype) //true
若是要理清原型和原型鏈的關係,首先要明確一下幾個概念:
1.JS中的全部東西都是對象,函數也是對象, 並且是一種特殊的對象
2.JS中全部的東西都由Object衍生而來, 即全部東西原型鏈的終點指向Object.prototype
3.JS對象都有一個隱藏的__proto__屬性,他指向建立它的構造函數的原型,可是有一個例外,Object.prototype.__proto__指向的是null。
4.JS中構造函數和實例(對象)之間的微妙關係
構造函數經過定義prototype來約定其實例的規格, 再經過 new 來構造出實例,他們的做用就是生產對象.
function Foo(){ } var foo = new Foo(); foo實際上是經過Foo.prototype來生成實例的。
構造函數自己又是方法(Function)的實例, 所以也能夠查到它的__proto__(原型鏈)
function Foo(){ } 等價於 var Foo= new Function();
而Function其實是
function Function(){ Native Code } 也就是等價於 var Function= new Function();
因此說Function是經過本身建立出來的。正常狀況下對象的__proto__是指向建立它的構造函數的prototype的.因此Function的__proto__指向的Function.prototype
Object 實際上也是經過Function建立出來的
typeof(Object)//function 因此, function Object(){ Native Code } 等價於 var Object = new Function();
那麼Object的__proto__指向的是Function.prototype,也便是
Object.__proto__ === Function.prototype //true
下面再來看Function.prototype的__proto__指向哪裏
由於JS中全部的東西都是對象,那麼,Function.prototype 也是對象,既然是對象,那麼Function.prototype確定是經過Object建立出來的,因此,
Function.prototype.__proto__ === Object.prototype //true
綜上所述,Function和Object的原型以及原型鏈的關係能夠概括爲下圖。
對於單個的對象實例,若是經過Object建立,
var obj = new Object();
那麼它的原型和原型鏈的關係以下圖
若是經過函數對象來建立,
function Foo(){ } var foo = new Foo();
那麼它的原型和原型鏈的關係以下圖
那JavaScript的總體的原型和原型鏈中的關係就很清晰了,以下圖所示