相信很多人都很困惑于javascript的原型系統(tǒng)蹬癌,佑稠,在我剛開始接觸javascript的原型系統(tǒng)的時候,我也非常困惑于它的結構屋确,因為我以前都沒有接觸過這種基于原型的語言。javascript是一個基于原型的語言续扔,雖然是以函數(shù)為第一等公民的語言攻臀,但也可以實現(xiàn)面向?qū)ο螅蔷褪腔谒脑拖到y(tǒng)纱昧。
javascript原型之函數(shù)
現(xiàn)在我們來寫一個函數(shù)
function A(){
}
這是一個內(nèi)容為空的函數(shù)刨啸,但它真的內(nèi)容為空嗎?讓我們來看下面一段代碼
function A(){
}
console.log(A.toString());;//輸出為function A() {}
我們知道识脆,javascript里面的一切都是對象设联,函數(shù)也是,既然函數(shù)是對象那么就可以調(diào)用函數(shù)對象的方法灼捂,所以我試著調(diào)用了toString 方法离例,它輸出了一個字符串,證明toString方法是存在的悉稠。那么toString 方法到底是存在在那里呢宫蛆,不存在于函數(shù)體里,那么存在一個地方必然有toString的函數(shù)體且對象function A以某種方式獲得了toString方法的調(diào)用權的猛。耀盗。想虎。
我百度了一下基于原型的語言的特征,基于原型的語言叛拷,必然有一個或著多個最初的對象舌厨,然后以后的對象都是由這些最初對象克隆過來的,也就是說胡诗,基于原型的語言中對象的生成是根據(jù)存在的對象來復制的邓线。
好,那我們開始下一步的實驗
function A(){
}
console.log(A.__proto__);//Function
console.log(A.prototype);//{}
我輸出了Javascript對象所擁有的兩個屬性煌恢,這是javascript語言規(guī)定的兩個屬性_proto_屬性指向?qū)ο髽嬙旌瘮?shù)的原型(不是很理解)骇陈,prototype屬性指向?qū)ο蟮脑汀慕Y果看A函數(shù)構造函數(shù)的原型是Function,A函數(shù)自己的原型是{ }(同樣不是很理解)
于是我又做了下面這個實驗
Function.prototype.getName = function(){
return "FunctionTest";
}
function A(){
}
console.log(A.__proto__ === Function.prototype);//true
console.log(A.getName());//FunctionTest
function B(){
}
console.log(B.__proto__ === Function.prototype);//true
console.log(B.getName());//FunctionTest
console.log(A.__proto__ === A.constructor.prototype);//true
//即函數(shù)作為對象它的構造函數(shù)為Function
我從另外的地方得知javascript里面有內(nèi)建的Function和Object對象瑰抵,于是我想著Function對象是否和function A 有些關聯(lián)呢你雌,當我看到第一條console.log語句返回true的時候,我知道我是正確的二汛,于是我擴展了Function.prototype 給其中添加了getName方法婿崭,然后我在用函數(shù)A調(diào)用了這個方法返回FunctionTest,我又新建了函數(shù)B,也調(diào)用了這個方法肴颊,返回FunctionTest氓栈。
至此我知道了函數(shù)_proto屬性的指向,指向其構造函數(shù)的原型婿着,當對象A調(diào)用getName函數(shù)的時候授瘦,由于A對象沒有getName函數(shù),javascript會尋找對象A的_proto屬性所對應對象竟宋,有則調(diào)用提完,沒有則繼續(xù)向上找。
函數(shù)的prototype屬性我一開始始終沒有找到與之對應的對象
console.log(A.prototype === Function.__proto__);//false
console.log(A.prototype === Function.prototype);//false
console.log(A.prototype === Object.__proto__);//false
console.log(A.prototype === Object.prototype);//false
后來我換了一種思考方式終于找到了
console.log(A.prototype.__proto__ === Object.prototype);//true
console.log(A.prototype.prototype);//undefined
而之后我又實驗了
console.log(Function.prototype.__proto__ === Object.prototype);
所以最終所有的一切對象的內(nèi)置函數(shù)比如toString都是在Object.prototype里的
然后我又有一個猜測
所有一切函數(shù)對象的內(nèi)置函數(shù)比如call,apply都是Function.prototype里的丘侠,可以很容易的就驗證徒欣,普通對象是不能調(diào)用call,apply的。
javascript原型之對象
我實驗了如下的代碼
function A(){
this.getText = function(){
return "Text";
}
}
A.prototype.getName = function(){
return "my god";
}
var i = new A();
console.log(i.getText());//Text
console.log(i.getName());//my god
然后我改了一下程序
function A(){
this.getName = function(){
return "Text";
}
}
A.prototype.getName = function(){
return "my god";
}
var i = new A();
console.log(i.getName());//Text
console.log(i.__proto__ === A.prototype);//true
console.log(i.prototype);//undefined
由上面的實驗可知蜗字,由函數(shù)A作為構造函數(shù)打肝,所克隆出來的普通對象i的_proto_屬性指向函數(shù)A的原型(也就是prototype屬性),且普通對象i的prototype屬性是沒有定義的挪捕。
當i調(diào)用getName函數(shù)時闯睹,由于i是由函數(shù)A克隆出來(大家還沒忘記原型語言的特征吧,就是新的對象是由另一個已存在的對象克隆的)的担神,里面只用getText函數(shù)沒有getName函數(shù),于是javascript就會尋找i的_proto_屬性始花,而i的proto屬性所指向的其實就是A.prototype妄讯,所以javascript就在A.prototype里面找getName函數(shù)孩锡,找到了就調(diào)用。所以第二段代碼中優(yōu)先調(diào)用返回Text的那個getName函數(shù)亥贸。
javascript里面還有一種對象就是對象字面量躬窜,對象字面量的是否擁有Function.prototype或者Object.prototype的函數(shù)呢
請看實驗
var a = {
getName:function(){
return "a";
}
}
console.log(a.__proto__);//{}
console.log(a.prototype);//undefined
console.log(a.__proto__ === Object.prototype);//true
由于對象字面量不是由函數(shù)對象克隆的,所以沒有Function.prototype里面的方法炕置,因為a._proto_指向的是Object.prototype荣挨,這就說明javascript只能在a本身和Object.prototype里面找調(diào)用的函數(shù)。
總結
由上面以一些內(nèi)容我們可以得出:
1.javascript函數(shù)調(diào)用順序是查找對象的原型(即朴摊,對象的_proto_屬性)默垄,一層一層的往上找,直到遇到該函數(shù)或者undefined才停止甚纲。
2.函數(shù)作為特殊的對象口锭,它的原型是Function.prototype,能夠調(diào)用Function.prototype里面的方法介杆,而Function.prototype的原型又是Object.prototype鹃操,故而函數(shù)對象既能調(diào)用Function.prototype里面的方法,還能調(diào)用Object.prototype里面的方法春哨,這就說函數(shù)對象即是“函數(shù)”又是對象荆隘。
3.普通對象的原型是其構造函數(shù)的原型(即,A.prototype)赴背,而A.prototype的原型是Object.prototype椰拒,所以,普通對象只能調(diào)用它本身和Object.prototype內(nèi)的函數(shù)
4.對象字面量的原型是Object.prototype癞尚。