JavaScript是一種基于對象的語言法挨,可以說萬物皆對象纳账。
??特別說明:基于對象也好,面向?qū)ο笠埠脤嶋H上都是以對象的概念來編寫程序刹泄,從本質(zhì)上并無區(qū)別外里。
??JavaScript沒有類這個概念,它繼承機制是基于原型特石,產(chǎn)生原型鏈的模式盅蝗,而不是其他語言的類。因此要理解JavaScript的繼承機制姆蘸,需要更深入了解原型對象的概念墩莫。
對象
??JavaScript中,對象是有區(qū)別的逞敷,分為普通對象和函數(shù)對象狂秦,Object、Function是JS自帶的函數(shù)對象推捐,function定義方式本質(zhì)上還是new Function方式故痊。(函數(shù)對象都是通過Function來實例化的)
<script type="text/javascript">
function f1(){};
var f2 = function(){};
var f3 = new Function('str', 'console.log(str)');
var o3 = new f1();
var o1 = {};
var o2 = new Object();
console.log(typeof Object);//function
console.log(typeof Function);//function
console.log(typeof o1);//object
console.log(typeof o2);//object
console.log(typeof o3);//object
console.log(typeof f1);//function
console.log(typeof f2);//function
console.log(typeof f3);//function
</script>
在上面的例子中 o1 o2 o3 為普通對象,f1 f2 f3 為函數(shù)對象玖姑。怎么區(qū)分愕秫,其實很簡單,凡是通過 new Function() 創(chuàng)建的對象都是函數(shù)對象焰络,其他的都是普通對象戴甩。f1,f2闪彼,歸根結(jié)底都是通過 new Function()的方式進行創(chuàng)建的甜孤。Function Object 也都是通過 new Function()創(chuàng)建的协饲。
對象繼承
??Brendan Eich參考C++和Java,做了簡化設(shè)計缴川,將new命令引入到JavaScript中茉稠,new后面跟對象的構(gòu)造函數(shù),用來創(chuàng)建對象把夸,缺點:無法共享方法和屬性而线。
比如,在DOG對象的構(gòu)造函數(shù)中恋日,設(shè)置一個實例對象的共有屬性species膀篮。(函數(shù)也是一個對象)
function DOG(name) {
this.name = name;
this.species = '犬科';
};
用上面**函數(shù)對象**生成兩個實例對象
var dogA = new DOG('大毛');
var dogB = new DOG('二毛');
這兩個對象species屬性是獨立的,修改其中一個岂膳,不會影響到另一個誓竿。
dogA.species = '貓科';
alert(dogB.species);//犬科
每個實例對象都有自己的屬性和方法的副本,這不僅無法做到資源共享谈截,也極大的資源浪費筷屡。
Brendan Eich決定為構(gòu)造函數(shù)設(shè)置一個prototype屬性。這個屬性包含一個對象簸喂,所有實例對象需要共享的屬性和方法速蕊,都放在這個對象里面;那些不需要共享的屬性和方法娘赴,就在構(gòu)造函數(shù)里面规哲。實例對象一旦創(chuàng)建,將自動引用prototype對象的屬性和方法(也就是__proto__
)诽表。也就是說唉锌,實例對象的屬性和方法,分成兩種竿奏,一種是本地的袄简,另一種是引用的。
species屬性放在prototype對象里泛啸,是兩個實例對象共享的绿语。只要修改了prototype對象,就會同時影響兩個實例對象候址。
DOG.prototype.species = '貓科';
alert(dogA.species);//貓科 (dogA的__proto__
指向DOG.prototype)
alert(dogB.species);//貓科
由于所有實例對象共享一個prototype對象吕粹,那么從外界看起來,prototype對象就好像是實例對象的原型岗仑,而實例對象則好像"繼承"了prototype對象一樣匹耕。
原型prototype
??在JavaScript中,每當(dāng)定義一個對象(函數(shù))時候荠雕,對象都會包含一些預(yù)定的屬性稳其。其中函數(shù)對象的一個屬性就是原型對象prototype(原型對象還有constructor屬性(指向構(gòu)造函數(shù)對象))驶赏。普通對象沒有prototype,但是有__proto__
屬性既鞠。(比如實例化煤傍、字面量的對象)(指向其原型鏈)
原型對象其實就是普通對象(Function.prototype除外,它是函數(shù)對象,但它很特殊嘱蛋,他沒有prototype屬性(前面說道函數(shù)對象都有prototype屬性))蚯姆。
function f1(){};
console.log(f1.prototype);//f1{} (f1{}表示普通對象)
console.log(typeof f1.prototype);//object
console.log(typeof Function.prototype);//function,這個特殊
console.log(typeof Object.prototype);//object
console.log(typeof Function.prototype.prototype);//undefined
打印關(guān)于f1函數(shù)對象的相關(guān)信息
原型鏈
??JS在創(chuàng)建對象(不論普通對象還是函數(shù)對象)的時候,都有一個叫做__proto__([[ prototype ]] )
的內(nèi)置屬性浑槽,用于指向創(chuàng)建它 的 函數(shù)對象的原型對象prototype。
??(ps:函數(shù)對象來自Function對象返帕,字面量對象來自O(shè)bject對象桐玻,實例對象來自創(chuàng)建它的函數(shù)對象-->等等類似)
var person = function(name) {
this.name = name;
};
person.prototype.getName = function() {
return this.name;
}
var gz = new person('gongzi');
gz.getName();//gongzi
以上面的例子為例:
console.log(gz.__proto__ === person.prototype) //true
同樣,person.prototype對象也有__proto__屬性荆萤,它指向創(chuàng)建它的函數(shù)對象(Object)的prototype
console.log(person.prototype.__proto__ === Object.prototype) //true
繼續(xù)镊靴,Object.prototype對象也有__proto__屬性,但它比較特殊链韭,為null
console.log(Object.prototype.__proto__) //null
我們把這個有__proto__串起來的直到Object.prototype.__proto__為null的鏈叫做原型鏈偏竟。
function Fruits(name) {
this.name = name;
}
Fruits.prototype.colour = 'red';
Object.prototype.taste = '甜';
var fruits = new Fruits('蘋果');
console.log(fruits);//打印實例對象(普通對象)
var Game = {
name:'王者榮耀'
};
Object.prototype.type = '競技';
console.log(Game);//打印字面量對象(普通對象)
console.log(Fruits.__proto__);//打印 創(chuàng)建它 的 函數(shù)對象 的原型對象,結(jié)果function () {}
console.log(Fruits.constructor);//可以打印出構(gòu)造函數(shù)對象Function//Fruits.__proto__.constructor一樣
Function對象作為一個函數(shù)敞峭,就會有prototype屬性踊谋,該屬性將對應(yīng)”function () {}”對象。
alert(Fruits.__proto__ === Function.prototype);//true
前面有提到旋讹,原型對象屬于普通對象殖蚕。Function.prototype是個例外,它是原型對象沉迹,卻又是函數(shù)對象睦疫,作為一個函數(shù)對象,它又沒有prototype屬性鞭呕。
constructor屬性
??constructor 屬性返回對創(chuàng)建此對象的函數(shù)的引用
??prototype對象有一個construction屬性蛤育,默認(rèn)指向prototype對象所在的構(gòu)造函數(shù)。
??由于construction屬性定義在prototype對象上面葫松,意味著可以被所有實例對象繼承瓦糕。
??constructor屬性的作用,是分辨原型對象到底屬于哪個構(gòu)造函數(shù)腋么。
總結(jié)
1.原型和原型鏈?zhǔn)莏s實現(xiàn)繼承的一種模式刻坊。
2.原型鏈的形成真正的是靠proto而非prototype。
訪問原型對象的方法
獲取實例對象的原型對象党晋,有三種方法:
1.obj.construction.prototype或者C.prototype用于引用new C()創(chuàng)建的對象的原型對象
2.Object.getPrototypeOf(obj):是獲取obj對象的原型對象的標(biāo)準(zhǔn)方法谭胚。
3.obj.proto:是獲取obj對象的原型對象的非標(biāo)準(zhǔn)方法徐块,每個對象都有的屬性,可以在不支持Object.getPrototypeOf方法時作為權(quán)宜之計灾而,proto是非安全的胡控。
??上面三種方法第一種和第三種都不是很可靠。最新的ES6標(biāo)準(zhǔn)規(guī)定旁趟,proto屬性只有瀏覽器才需要部署昼激,其他環(huán)境可以不部署,obj.construction.prototype在手動改變原型對象時锡搜,可能會失效橙困。
擴展
1.Object.__proto__ === Function.prototype//true
2.Function.__proto__ === Function.prototype//true
3. Function.prototype.__proto__ === Object.prototype//true
1.Object是函數(shù)對象,是通過new Funciton()創(chuàng)建耕餐,所以O(shè)bject.__proto__指向Function.prototype凡傅。
2.Function也是對象函數(shù),也是通過new Function()創(chuàng)建肠缔,所以Function.__proto__指向Function.prototype夏跷。
3.Function.prototype是個函數(shù)對象,理論上其__proto__應(yīng)該指向Functuin.prototype明未,就是它自己槽华,自己指向自己,沒有意義趟妥。函數(shù)對象也是對象猫态,給它設(shè)定根指向Object.prototype,Object.prototype.__proto__ === null披摄,保證原型鏈能夠正常結(jié)束懂鸵。
Object.create()
??創(chuàng)建一個具有指定原型且可選擇性地包含指定屬性的對象。
Object.create(prototype, descriptors)
??這種創(chuàng)建對象的方式與上面所說略有差異行疏,如果要查找原型鏈匆光,按照原型對象的類型分析就可以。
??是使用第一個參數(shù)作為它們的原型酿联。
prototype
??必需终息。要用作原型的對象≌耆茫可以為 null周崭,不繼承任何對象。
descriptors
??可選喳张。包含一個或多個屬性描述符的 JavaScript 對象续镇。
??“數(shù)據(jù)屬性”是可獲取且可設(shè)置值的屬性。 數(shù)據(jù)屬性描述符包含 value 特性销部,以及 writable摸航、enumerable 和 configurable 特性制跟。
使用方法可以參考網(wǎng)絡(luò)上的這兩篇,或自己百度酱虎,沒時間細(xì)說了雨膨。
javascript一種新的對象創(chuàng)建方式-Object.create()
前端開發(fā)者進階之ECMAScript新特性【一】--Object.create
參考文獻:
Javascript繼承機制的設(shè)計思想
JavaScript原型及原型鏈詳解
JS原型、原型鏈深入理解
實在不懂就就就.............這樣記住吧
??JS中所有函數(shù)的原型都是Function.prototype读串,所有對象原型鏈的終點指向Object.prototype