前 言
上一篇博客(https://www.cnblogs.com/qdjianghao/p/10177360.html )中,我們介紹了JS中的面向對象,並詳細的解釋了this的指向問題。本篇博客,我們繼續來學習JS的面向對象。來深入理解一下JavaScript OOP中的成員屬性/方法、靜態屬性/方法、原型屬性/方法,並且一起來探討一下JS中的原型與原型鏈。
一 成員屬性與成員方法
在構造函數中,通過this.屬性聲明。或者實例化出對象後,通過“對象.屬性”追加的。都屬於成員屬性、或成員方法;也叫實例屬性與實例方法
成員屬性/方法,是屬於實例化出的這個對象。通過"對象.屬性"調用
代碼實例:
function Person(name){ this.name = name;//聲明成員屬性 this.say =function(){}//聲明成員方法 } var zhangsan =new Person("張三"); zhangsan.age= 14;//追加成員屬性 alert(zhangsan.name);//調用成員屬性 zhangsan.say();//調用成員方法
二 靜態屬性與靜態方法
通過"類名.屬性名","類名.方法名"聲明的變量,成爲靜態屬性、靜態方法;也叫類屬性、類方法
類屬性/類方法,是屬於類的(屬於構造函數的),這類屬性或方法必須通過"類名.屬性"調用,而不同通過對象名調用。
代碼實例:
functionPerson(name){ } Person.sex= "男";//聲明類屬性 alert(Person.age);//調用類屬性 var zhangsan = new Person("張三"); alert(zhangsan.sex);//無法調用。類屬性只能用類名調用。
三 私有屬性與私有方法
在構造函數中,通過var聲明的屬性,成爲私有屬性。
私有屬性的作用域,僅在當前函數有效。對外不公開,即通過對象/類都無法調用到。
代碼示例:
function Person(name){ var sex = "男"; alert(sex);//私有屬性只能在類內容使用。 } alert(Person.sex);//無法調用。私有屬性只能在類內容使用。 var zhangsan =new Person("張三"); alert(zhangsan.sex);//無法調用。私有屬性只能在類內容使用。
四 原型屬性與原型方法
寫在了構造函數的prototype上。當使用構造函數實例化對象事,該屬性方法會進入新對象的__proto__上。
代碼示例:
functionPerson(){ } Person.prototype.name4= "name4";//聲明原型屬性Person.prototype.say = function(){ }//聲明原型方法 var zhangsan =new Person(); alert(zhangsan.name);//可以使用對象調用原型屬性 zhangsan.say();//可以使用對象調用原型方法
通過上述代碼示例,我們可以發現:原型屬性和原型方法、成員屬性和成員方法,都可以使用對象名調用。那麼,成員屬性和原型屬性到底有什麼不同。我們先來看這樣的一幅圖:
看出點門道了嗎?成員屬性是直接聲明在對象自身上!!而原型屬性是聲明在對象的__proto__上!!!在我們使用“對象名.屬性名”訪問一個屬性時,會先訪問成員屬性,如果成員屬性中找不到,JS會沿着對象的原型鏈,繼續通過__proto__向上查找,也就找到了原型屬性。 這就是我們可以使用“對象名.屬性名”訪問成員屬性和原型屬性的原因。
但是,說了這麼多,什麼是__proto__?什麼是prototype?什麼又是原型鏈?接下來,我們就講解一下JS面向對象中最重要的一環——原型與原型鏈。
五 原型與原型鏈
1__proto__ 與 prototype
要講解原型與原型鏈,首先,我們要了解兩個基本概念——__proto__和prototype。
① prototype(函數的原型對象):函數纔有prototype,而且所有函數必有prototype。prototype是一個對象,指向了當前構造函數的引用地址呢。
② __proto__(對象的原型):對象纔有__proto__,而且所有對象必有__proto__屬性(這裏的對象除了我們理解的狹義對象,也包括了函數、數組等對象)。當用構造函數實例化(new)一個對象時,會將新對象的__proto__屬性,指上構造函數的prototype。
來看個案例:
functionPerson(){
}varzhangsan =newPerson();
console.log(zhangsan.__proto__== Person.prototype);//true//【解釋】上例中,我們使用函數Person,new出了一個對象zhangsan。 那麼對象zhangsan的__proto__就等於函數Person的prototype。
2原型鏈
上面我們介紹了對象的__proto__和函數的prototype。知道了對象的__proto__指向了函數的prototype。那麼,我們也說了,函數的prototype本身也是個對象,是對象肯定也有__proto__,那他的__proto__指向了誰? 順着這個問題,我們延着一個對象__proto__向上查找,這條線路就是我們所說的原型鏈。
所以,要研究原型鏈的走向,其實就是研究各種類型對象的__proto__到底指向誰。還記得上篇博客的“飛魚老師this五大準則”嗎? 這次,貼心的飛魚老師老師又爲大家準備了“飛魚老師原型鏈四大法則”,一起來看看吧!
【飛魚老師原型鏈四大準則】
① 通過構造函數,new出的對象,新對象的__proto__指向構造函數的prototype
② 所有函數的__proto__指上Function()的prototype
③ 非構造函數new出的對象( {} new Object()對象的prototype)的__proto__指向Object的prototype
④Object的prototype的__proto__指向null
字不如表,表不如圖。我們一起以一段代碼爲例,探討一下原型鏈。
代碼:
functionPerson(){
}
varzhangsan =newPerson();
就是這樣一個簡單的類new出對象,我們來探討一下原型鏈吧~上圖!!!
原型鏈圖(飛魚老師親情手繪,親,點個讚唄!):
六 成員屬性與原型方法
說了這麼多,我們應該能夠理解原型與原型鏈,也深刻的理解了原型屬性、原型方法。那麼問題來了,既然成員屬性/方法和原型屬性/方法,都能通過對象名訪問。我們到底用那種比較好呢?
習慣上,我們會將屬性寫爲成員屬性,而方法寫爲原型方法
原因如下:
① 實例化出對象後,所有屬性直接在對象上,所有方法都在__proto__上,非常直觀清晰。
② 方法寫到prototype上,要更加節省內存;
③ 使用for in循環時,會將對象以及對象原型鏈上的所有屬性和方法打印出來,而方法往往是不需要展示的。 將方法寫到__proto__上,可以使用hasOwnProperty將原型上的方法過濾掉、不顯示。
④ 官方都這麼寫。
七 prototype擴展內置函數
那原型還有什麼作用呢?還有一個最常用的,就是我們使用prototype擴展內置函數的方法。可以直接使用內置函數的對象,調用我們擴展的方法;
比如,爲Array類添加一個find(val)方法,當一個Array對象調用該方法的時候,如果能找到val值,則返回其下標,否則返回-1。
Array.prototype.find =function(val){ //在Array類的原型上添加擴展方法 } var arr =new Array(1,2,3,4,5); alert(arr.find(1));//直接使用Array類的對象,也就是數組,就可以調用這個方法
好了,今天的課程就先到這裏吧?我們學習了JS中的成員屬性、靜態屬性、原型屬性、私有屬性等各種學習。 也學習了JS面向對象中非常重要的一個環節——原型與原型鏈。你都學會了嗎? 如果有問題歡迎大家評論留言哦~~我們下次課再見吧!