面向對象,創建對象,構造函數,原型,原型鏈

面向對象

對象是一系列無序屬性的集合。

j權威指南:

對象是js的基本數據類型,它是一種複合值,它將很多值(原始類型或者其他值)聚合在一起,可通過名字訪問這些值。

對象的內部屬性有:

  • 數據屬性
    可配置(表示能否通過delete刪除屬性從而重新定義屬性,能否修改屬性的特性,或者能否把屬性修改爲訪問器屬性) ,可枚舉(默認爲true),可寫(默認爲true),值(包含這個屬性的數據值,默認undefined)

  • 訪問器屬性
    可配置(表示能否通過delete刪除屬性從而重新定義屬性,能否修改屬性的特性,或者能否把屬性修改爲數據屬性),可枚舉(默認爲true),[[get]]讀取屬性時調用的函數,[[set]]寫入屬性調用的函數,get,set默認都爲undefined

面向對象:

我理解的就是指關注對象,以對象作爲出發點來進行思考進行編程的一種方式。
另一種就是面向過程。
網上看到這個例子:一個木匠要做一個盒子,面向對象就是關注盒子要做成什麼樣,然後再去想需要什麼材料。
面向過程就是不管做什麼盒子,材料隨取隨用。

面向對象的三個特徵

封裝,繼承,多態。
封裝呢:我理解就是指把對象封裝成抽象的一類,比如我們都是一個個人,但是我們可以統稱人類。光說個人類好像也沒有指向具體的某個實體,這就是說抽象….把對象的某些屬性對可信的就讓它們防問,不可信的就不能防問。
繼承:就像是人類,這是一個對象,我們每個人都是人類,有相同的人類構造。
多態:就比方是雖然我們都是人類,但是每個人又都是不一樣的。

創建對象

在js中創建對象有很多的方法:
字面量:

var person1 = {
    name: "one",
    job: "artist",
    hand:2,
    foot:2
}

但是這樣,如果我要寫一個和person1的屬性一樣的也有手和腳的對象person2,那又要重新像上面一樣重新寫一次
就很麻煩。
於是有了工廠模式

工廠模式

就是在函數裏面新建個對象,然後通過傳的參數,來返回這個對象:

function person(name,job){
    var person = new Object();
        person.name = name,
        person.job = job,
        person.hand = 2,
        person.foot = 2
    return person;
}
 var person1 = person("zhuyi","student");
    alert(person1.name);//zhuyi

但是這樣一來雖然我建立person2並不需要再寫一堆了,這個問題解決了,但是…怎麼判斷person1是什麼類呢,它是人還是物呢?怎麼通過person1來找到它的類別呢?

構造函數模式

構造函數和一般的函數其實….好像也沒什麼區別,也就是在調用它的時候前面加了個new,它內部也有點變化啦:

  • 一般函數名字的首字母都會大寫
  • 它不會像上面的那個person函數一樣顯式創建一個對象
  • 直接將屬性和方法賦給了對象
  • 一般沒有return語句

再來說一下new操作符,比如上面var person = new Object(); 這裏的new實際上是
1)先創建了一個空對象
2)把Object這個構造函數的作用域賦給上面創建的那個新空對象,然後this就指向了這個新空的對象
3)執行Object這個構造函數中的代碼,爲這個空對象添加了一系列屬性
4)返回給person,這裏的person就是一個基本類型–對象

把上面的那個person改成構造函數的方式:

 function person(name,job) {
//        var person = new Object(); 沒有明顯創建對象
        this.name = name, //this先指向了空對象,再指向賦值後的對象person1
                this.job = job,
                this.hand = 2,
                this.foot = 2
//        return person; 沒有明顯返回 因爲這兩步在new和賦值來完成了
    }
    var person1 = new person("zhuyi","student");
    alert(person1.name); //zhuyi
    alert(person1.job); //student

在此之前,this的其他的用法,這篇筆記記錄了:
http://blog.csdn.net/u014106213/article/details/77966422
總之this在這裏就是說這個方法屬於誰。

通過這個方法呢,也達到了同樣的創建person1的對象的效果,但是這樣有一個不一樣的,也是解決上面說到沒辦法去知道person1是那個抽象來的類別的問題:
person1現在是person的一個實例,現在這個對象person1通過構造函數來創建的話,它通過一個屬性,叫構造函數屬性,constructor,這個屬性就是指向了它的類別,person,但是這個屬性並不是它自己本身有的,是它繼承而來(後面會提及):

alert(person1.constructor);

這樣alert一下,會返回person那個函數,如果用來檢查某個對象是什麼類型,也可以用以下這個:

alert(person1 instanceof person); //true

instanceof,翻譯成中文就是說,這個a是不是b的一個實例,是就返回true,不是就是false

原型模式

接下來,再看一下這個person函數,這裏面像是名字,工作這些會不一樣的,但是hand和foot一直都一樣,那每次來新建個人就要重新把他們這些公共屬性賦值給新人,就造成了資源的浪費。於是又有了原型模式:

我理解的原型就是一個存放公共屬性的一個對象。它的英文名叫prototype,這個屬性是函數纔有的
它保存了一些公共的屬性。同時,它有一個屬性叫constructor,這個屬性指向這個函數本身。

用原型模式來寫上面的內容:

function person(name,job) {
        this.name = name,
        this.job = job,
        person.prototype.hand = 2,
        person.prototype.foot= 2
    }
    var person1 = new person("zhuyi","student");
    alert(person.hand);    // 共享prototype的2

這個時候,再新建一個對象person2的話,hand和foot這兩個屬性都是共享
他們都能訪問到。
這時候的實例**person1其實它的內部包含了一個指針(內部屬性),這個屬性將指向構造函數的原型對象。
**es5叫這個爲[[prototype]],又__proto__.它是每個對象都有的。
實際上上面提及的實例的constructor,也是繼承自構造函數的prototype

這裏寫圖片描述

那麼現在似乎可以發現:分爲兩種,一種是函數,而每個函數都可以作爲構造函數的,一種是普通實例對象:
1)prototype是函數纔有的,中文叫原型,也就是說實例的共同特徵的“源頭”
2)constructor是一個存在構造函數的prototype中的對象,也就是說實例(被構造函數new出來的對象)自身其實沒有這個對象,而原型(構造函數)都存在這樣一個屬性,構造函數的constructor會指向自己,這個屬性名字中文是構造器,也就是指向構造函數的一個屬性
3)而__proto__ 是對象都有的屬性,也就是說不管是構造函數還是實例,都有着這樣一個屬性,它指向構造函數的prototype,構造函數這個屬性存放在prototype中

上面說person1的constructor指向person,其實是person這個函數的prototype中有constructor這樣一個對象,這個對象指向了person,而person1的__proto__ 指向了person的prototype,訪問person1的時候,順着這個指針,就到了person的prototype中的constructor,這就找到了person。

那如果這時候我在某個實例上也同樣給它自己建立一個屬性叫做hand,而且給它設置爲3呢?
如果我alert(person1.hand),這時候它會彈出幾呢?

function person(name,job) {
        this.name = name,
        this.job = job,
        this.hand = 3,
        person.prototype.hand = 2,
        person.prototype.foot= 2
    }
    var person1 = new person("zhuyi","student");
    alert(person1.hand);//3

每當代碼讀取某個對象的某個屬性時,都會執行一次搜索,目標是具有給定名字的屬性,搜索首先從對象實例
本身開始,如果在實例中找到了具有給定名字的屬性,則返回該屬性的值,如果沒有找到,則繼續搜索指針指向的
原型對象。當爲對象實例中添加一個屬性時,這個屬性就會屏蔽原型對象中保存的同名屬性,使用delete刪除掉
這個實例屬性後,我們還是能重新訪問原型中的這個屬性。

因此上面的那段代碼中,首先搜索person1中的實例屬性hand,搜索到了,就會返回,不會再搜索原型中的
hand了,而如果刪除掉這個person1中的hand,原型對象的hand還是能再次訪問到

原型鏈

首先,js中有undefined,null,object,string,number,boolean,這幾種基本類型。而object又有Date,function,array,regExp等作爲它的實例的類型。
看上面的圖會發現,新創建出來的對象person1是通過屬性中有一個[[prototype]]屬性,也就是__proto__.來繼承person這個函數中的共享對象的。
person1是屬於基本類型object,而person的類型雖然也是對象,但是更細分下去,它屬於function。
那麼person也同樣有__proto_ 這個屬性,它又指向的是哪裏呢?

當我們聲明一個函數的時候,函數的__proto__ 指向的是一個構造函數function的prototype,每個函數都是function的實例。

這個function就相當於是整個函數的“祖先”,每個函數通過直接聲明出來的都會通過__proto__ 來指向它。
而function也同樣是object的實例,function也是對象,它的__proto__ 也指向的是構造函數object()的prototype,而Object的__proto__ 又是指向了null。(null這個是相當於規定,null在js中,邏輯上是一個空對象指針,記住它是原型鏈頂端就可以,從無到有)
這樣就構成了一條完整的原型鏈。
在上面的基礎上:

var apple = new Object();

創建一個apple普通對象,以下是圖:

這裏寫圖片描述

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