五、構造函數
至今,我們已經學習了非常多的調用函數的方式:
- 圓括号直接調用
- 對象打點調用
- 定時器調用
- 事件處理函數調用
- 數組枚舉調用
它們展現的不同點就是函數上下文不用,它們裡面的this不一樣!
現在,我們要學習新的函數調用方法!就是用 new 運算符來調用函數!
5.1 new 運算符
我們現在試着用new運算符調用一個函數:

此時函數可以執行,彈出 你好。也就是說new 是一個全新的調用函數的方式,我們關心的是這個函數的上下文是誰?
用new運算符調用一個函數的時候,會經曆四步走:
-
函數内部悄悄的建立一個局部變量,是一個空對象{}。
-
函數将自己的上下文設定為這個空對象{},即所有語句中的this就表示這個空對象。
-
函數将執行所有語句。
-
所有語句執行後,函數将return這個對象,函數将把自己的上下文傳回。
是以,遇見new操作符,馬上想起四步走!
目前為止,我們發現,喲!這個new運算符能夠調用函數,還能傳回一個對象!
我們發現用new 操作符,可以傳回具有相同屬性群的對象:
我們說,可以認為 People 是一個“類(class)”,xiaoming , xiaohong , xiaoqing 都是這個類的“執行個體(instance)”。
JS中沒有類的概念,我們這裡隻是和 Java、C++、C# 做一個類比,JS中隻有構造函數,當一個函數被new操作符調用的時候,這個函數就是一個構造函數,它總能傳回一類的具有相同屬性群的對象,感覺在“構造東西”。是以這個函數很神奇,像一個模子,在制作類似的對象。
為了提醒其他程式猿,這是一個必須用new調用的函數,換句話說提醒别人這是一個構造函數,這類函數的名字必須首字母大寫。
我們看不用new 操作符調用構造函數會發生什麼:
由于我們直接調用了構造函數,此時就是标準的函數直接加圓括号調用,函數裡面的 this 是 window 對象,是以,此時 name 、age 、sex 都被設定為了 window 對象的屬性,我們知道 window 對象的屬性是全局變量,此時能夠彈出12。
下面就是一個标準的構造函數:
給學過其他語言的同學提個醒,JS中沒有Class關鍵字(ECMAScript2016 增加了Class關鍵字),我們就是使用new操作符的時候,會按‘四步走’建立一些些具有相同屬性群的執行個體,此時就感覺有了‘類’,JS隻提“構造函數”,而不提“類”。
複習,學習了什麼:
- 一個函數可以用 new 來調用,此時這個函數将按四步走執行,此時将傳回一個對象,這個函數我們稱為構造函數。
- 一般的,用 new 來多次調用同一個函數,總能得到具有相同屬性群的對象,我們可以稱這些對象是“同一個類的執行個體”,或者稱為“同一類的對象”。
- new 就是一個運作函數的運算符,不要太有 “類” 的概念!會害了你!
JS不是一個面向對象(OO)的語言,它隻是基于對象(BO)。
小題目:
這個函數是不是一個構造函數? 答案:不知道。因為隻要用new 操作符調用一個函數,此時這個函數就是一個構造函數,至于函數名字沒有大寫,隻是我們的習慣,不是語言要求。
再來,這個函數是不是構造函數:
答案也是不确定!因為用new調用它就是一個構造函數,不用new調用就不是,比如我們直接調用:
函數根本沒有傳回值!此時obj就是null 。
是以,構造函數真的沒有什麼!就是一個全新的函數調用方式!
構造函數和普通函數沒有什麼差別,隻不過這家夥能夠被new來調用,事實上所有函數都能用new調用!
5.2 原型鍊
每一個構造函數都有一個屬性叫做prototype,指向一個對象,當這個構造函數被new 的時候,它的每一個執行個體的__proto__屬性,也指向這個對象
每個函數天生都有prototype屬性,指向一個空對象,也就是說,我們不需要定義這個prototype屬性,任何一個函數隻要寫出來,它就擁有了prototype屬性,這個屬性對它自己而言沒有任何意義!它的唯一意義是兒子們的指明燈!構造函數的 prototype屬性 指向誰,它new出來的兒子的__proto__屬性就指向誰!
我們稱 People.prototype 是People構造函數的 “原型”,People.prototype 是小明的 “原型對象”。
__proto__是神器,有原型鍊查找功能,當xiaoming身上沒有某個屬性的時候,系統會沿着__proto__去尋找它的運作對象身上有沒有這個屬性。
一定要記住這個三角戀:構造函數的 prototype 指向誰,new 出來的玩意兒的__proto__就指向誰!!!這個__proto__有原型鍊查找功能!
小題目:
小明身上雖然沒有random屬性,但是小明的__proto__身上有這個屬性,是以小明能夠打點調用random屬性。
一定記住,任何函數都有prototype屬性,prototype是一個空對象,而不是函數的東西,一定沒有prototype屬性,prototype屬性的值是一個對象,這個函數new出來的東西__proto__指向這個對象。
5.3 在原型上定義方法
我們剛才知道如果把函數寫在構造函數裡面,就相當于定義在了對象上,此時函數會産生多個不用的副本,影響程式效率,此時可以把函數定義在構造函數的原型上,這樣所有new出的東西, __proto__就會指向這個對象,進而能夠使用這個方法。
5.4 構造函數和執行個體的關系
構造函數:抽象,定義的是一類對象應該具有什麼屬性,什麼方法, 描述規則,描述藍圖。
執行個體:具體,根據規則、藍圖建立的一個個具體的執行個體,它能擁有屬性,并且能調用方法。
等價于:
再看下面的小例子:
構造函數中,我們用prototype來定義方法,定義的是構造函數的執行個體的方法,不是構造函數的方法。