首先要搞明白幾個(gè)概念:
函數(shù)(function)
函數(shù)對(duì)象(function object)
本地對(duì)象(native object)
內(nèi)置對(duì)象(build-in object)
宿主對(duì)象(host object)
函數(shù)
function foo(){
}
var foo = function(){
}
前者為函數(shù)聲明,后者為函數(shù)表達(dá)式。typeof foo
的結(jié)果都是function。
函數(shù)對(duì)象
函數(shù)就是對(duì)象,代表函數(shù)的對(duì)象就是函數(shù)對(duì)象
官方定義, 在Javascript中,每一個(gè)函數(shù)實(shí)際上都是一個(gè)函數(shù)對(duì)象.JavaScript代碼中定義函數(shù),或者調(diào)用Function創(chuàng)建函數(shù)時(shí),最終都會(huì)以類似這樣的形式調(diào)用Function函數(shù):var newFun = new Function(funArgs, funBody)
其實(shí)也就是說,我們定義的函數(shù),語(yǔ)法上,都稱為函數(shù)對(duì)象,看我們?nèi)绾稳ナ褂谩H绻覀儐渭兊陌阉?dāng)成一個(gè)函數(shù)使用,那么它就是函數(shù),如果我們通過他來實(shí)例化出對(duì)象來使用,那么它就可以當(dāng)成一個(gè)函數(shù)對(duì)象來使用,在面向?qū)ο蟮姆懂犂锩?,函?shù)對(duì)象類似于類的概念。
var foo = new function(){
}
typeof foo // object
或者
function Foo (){
}
var foo = new Foo();
typeof foo // object
上面,我們所建立的對(duì)象
本地對(duì)象
ECMA-262 把本地對(duì)象(native object)定義為“獨(dú)立于宿主環(huán)境的 ECMAScript 實(shí)現(xiàn)提供的對(duì)象”。簡(jiǎn)單來說,本地對(duì)象就是 ECMA-262 定義的類(引用類型)。它們包括:
Object,Function,Array,String,Boolean,Number
Date,RegExp,Error,EvalError,RangeError,ReferenceError,SyntaxError,TypeError,URIError.
我們不能被他們起的名字是本地對(duì)象,就把他們理解成對(duì)象(雖然是事實(shí)上,它就是一個(gè)對(duì)象,因?yàn)镴S中萬(wàn)物皆為對(duì)象),通過
typeof(Object)
typeof(Array)
typeof(Date)
typeof(RegExp)
typeof(Math)
返回的結(jié)果都是function
也就是說其實(shí)這些本地對(duì)象(類)是通過function建立起來的,
function Object(){
}
function Array(){
}
...
可以看出Object原本就是一個(gè)函數(shù),通過new Object()之后實(shí)例化后,創(chuàng)建對(duì)象。類似于JAVA中的類。
內(nèi)置對(duì)象
ECMA-262 把內(nèi)置對(duì)象(built-in object)定義為“由 ECMAScript 實(shí)現(xiàn)提供的、獨(dú)立于宿主環(huán)境的所有對(duì)象,在 ECMAScript 程序開始執(zhí)行時(shí)出現(xiàn)”。這意味著開發(fā)者不必明確實(shí)例化內(nèi)置對(duì)象,它已被實(shí)例化了。ECMA-262 只定義了兩個(gè)內(nèi)置對(duì)象,即 Global 和 Math (它們也是本地對(duì)象,根據(jù)定義,每個(gè)內(nèi)置對(duì)象都是本地對(duì)象)。
理清楚了這幾個(gè)概念,有助于理解我們下面要講述的原型和原型鏈。
prototype
prototype屬性是每一個(gè)函數(shù)都具有的屬性,但是不是一個(gè)對(duì)象都具有的屬性。比如
function Foo(){
}
var foo = new Foo();
其中Foo中有prototype屬性,而foo沒有。但是foo中的隱含的__proto__屬性指向Foo.prototype。
1foo.__proto__ === Foo.prototype
為什么會(huì)存在prototype屬性?
Javascript里面所有的數(shù)據(jù)類型都是對(duì)象,為了使JavaScript實(shí)現(xiàn)面向?qū)ο蟮乃枷?,就必須要能夠?qū)崿F(xiàn)‘繼承’使所有的對(duì)象連接起來。而如何實(shí)現(xiàn)繼承呢?JavaScript采用了類似C++,java的方式,通過new的方式來實(shí)現(xiàn)實(shí)例。
舉個(gè)例子,child1,child2都是Mother的孩子,且是雙胞胎。(雖然不是很好,但是還是很能說明問題的)
function Mother(name){
????this.name = name;
????this.father = 'baba';
}
var child1 = new Mother('huahua');
var child2 = new Mother('huihui');
如果有一天,發(fā)現(xiàn)孩子的父親其實(shí)是Baba,那么就要對(duì)孩子每一個(gè)孩子的father屬性。
child1.father ='Baba';
console.log(child2.father) // baba
也就是說修改了其中一個(gè)孩子的father屬性不會(huì)影響到下一個(gè),屬性的值無法共享。
正是這個(gè)原因才提出來prototype屬性,把需要共享的屬性放到構(gòu)造函數(shù)也就是父類的實(shí)例中去。
__proto__
__proto__屬性是每一個(gè)對(duì)象以及函數(shù)都隱含的一個(gè)屬性。對(duì)于每一個(gè)含有__proto__屬性,他所指向的是創(chuàng)建他的構(gòu)造函數(shù)的prototype。原型鏈就是通過這個(gè)屬性構(gòu)件的。
想像一下,如果一個(gè)函數(shù)對(duì)象(也成為構(gòu)造函數(shù))a的prototype是另一個(gè)函數(shù)對(duì)象b構(gòu)件出的實(shí)例,a的實(shí)例就可以通過__proto__與b的原型鏈起來。而b的原型其實(shí)就是Object的實(shí)例,所以a的實(shí)例對(duì)象,就可以通過原型鏈和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
如果要理清原型和原型鏈的關(guān)系,首先要明確一下幾個(gè)概念:
1.JS中的所有東西都是對(duì)象,函數(shù)也是對(duì)象, 而且是一種特殊的對(duì)象
2.JS中所有的東西都由Object衍生而來, 即所有東西原型鏈的終點(diǎn)指向Object.prototype
3.JS對(duì)象都有一個(gè)隱藏的__proto__屬性,他指向創(chuàng)建它的構(gòu)造函數(shù)的原型,但是有一個(gè)例外,Object.prototype.__proto__指向的是null。
4.JS中構(gòu)造函數(shù)和實(shí)例(對(duì)象)之間的微妙關(guān)系
構(gòu)造函數(shù)通過定義prototype來約定其實(shí)例的規(guī)格, 再通過 new 來構(gòu)造出實(shí)例,他們的作用就是生產(chǎn)對(duì)象.
function Foo(){
}
var foo = new Foo();
foo其實(shí)是通過Foo.prototype來生成實(shí)例的。
構(gòu)造函數(shù)本身又是方法(Function)的實(shí)例, 因此也可以查到它的__proto__(原型鏈)
function Foo(){
}
等價(jià)于
var Foo= new Function();
而Function實(shí)際上是
function Function(){
????Native Code
}
也就是等價(jià)于
var Function= new Function();
所以說Function是通過自己創(chuàng)建出來的。正常情況下對(duì)象的__proto__是指向創(chuàng)建它的構(gòu)造函數(shù)的prototype的.所以Function的__proto__指向的Function.prototype
Object 實(shí)際上也是通過Function創(chuàng)建出來的
typeof(Object)//function
所以,
function Object(){
????Native Code
}
等價(jià)于
var Object = new Function();
那么Object的__proto__指向的是Function.prototype,也即是
1Object.__proto__ === Function.prototype //true
下面再來看Function.prototype的__proto__指向哪里
因?yàn)镴S中所有的東西都是對(duì)象,那么,F(xiàn)unction.prototype 也是對(duì)象,既然是對(duì)象,那么Function.prototype肯定是通過Object創(chuàng)建出來的,所以,
1 Function.prototype.__proto__ === Object.prototype //true
綜上所述,F(xiàn)unction和Object的原型以及原型鏈的關(guān)系可以歸納為下圖。

對(duì)于單個(gè)的對(duì)象實(shí)例,如果通過Object創(chuàng)建,
1var obj = new Object();
那么它的原型和原型鏈的關(guān)系如下圖。

如果通過函數(shù)對(duì)象來創(chuàng)建,
function Foo(){
}
var foo = new Foo();
那么它的原型和原型鏈的關(guān)系如下圖
那JavaScript的整體的原型和原型鏈中的關(guān)系就很清晰了,如下圖所示

