JavaScript面向對象(二)——成員屬性、靜態屬性、原型屬性與JS原型鏈

前  言

上一篇博客(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面向對象中非常重要的一個環節——原型與原型鏈。你都學會了嗎? 如果有問題歡迎大家評論留言哦~~我們下次課再見吧!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章