(摘自:https://segmentfault.com/q/1010000005926088)
造函數(shù)其實和普通函數(shù)本質(zhì)上并無區(qū)別,唯一的區(qū)別有兩個:
函數(shù)首字母大寫轨淌,這個區(qū)別只是約定俗成的迂烁,便于區(qū)分。你實在要小寫定義構(gòu)造函數(shù)也完全沒問題递鹉,所以這個區(qū)別可以忽略婚被。
構(gòu)造函數(shù)的調(diào)用需要用new操作符,而普通函數(shù)的調(diào)用又分很多種梳虽,但是都不會用到new操作符址芯。所以,構(gòu)造函數(shù)和普通函數(shù)的區(qū)別就在這個new操作符里窜觉,現(xiàn)在讓我們來好好研究一下這個new操作符谷炸。
用new操作符創(chuàng)建對象時發(fā)生的事情:
第一步: 創(chuàng)建一個Object對象實例。
第二步: 將構(gòu)造函數(shù)的執(zhí)行對象賦給新生成的這個實例禀挫。
第三步: 執(zhí)行構(gòu)造函數(shù)中的代碼
第四步: 返回新生成的對象實例
注意:原本的構(gòu)造函數(shù)是window對象的方法旬陡,如果不用new操作符而直接調(diào)用,那么構(gòu)造函數(shù)的執(zhí)行對象就 是window语婴,即this指向了window描孟。現(xiàn)在用new操作符后,this就指向了新生成的對象砰左。理解這一步至關(guān)重要匿醒。
執(zhí)行構(gòu)造函數(shù)中的代碼,看代碼:
function Person(){
this.name = "Tiny Colder";
var age = 22;
window.age = 22;
}
var p = new Person();
alert(p.name)//Tiny Colder;
alert(p.age)//undefined;
alert(window.age)//22;
當(dāng)用new操作符創(chuàng)建對象時缠导,先創(chuàng)建了一個對象實例廉羔,然后執(zhí)行代碼。所以還在糾結(jié)僻造,什么時候構(gòu)造函數(shù)定義的屬性會繼承給實例對象的憋他,都可以這么來看:
var p = new Object();
p.name = "Tiny Colder";
這是普通的創(chuàng)建對象,然后給對象添加屬性的方法髓削。如果每創(chuàng)建一個對象竹挡,都需要這么幾行代碼,無疑是糟糕的立膛。這個需求就正好跟這一點對應(yīng):new操作符揪罕,自動執(zhí)行構(gòu)造函數(shù)里的代碼。如此我們便可以省掉添加屬性時重復(fù)冗余的代碼。那么這些屬性時如何添加到新生成的對象里的呢耸序?
第二個步驟里已經(jīng)說了:將構(gòu)造函數(shù)的執(zhí)行對象賦給新生成的這個實例忍些。再結(jié)合上一段里說的,自動執(zhí)行構(gòu)造函數(shù)里的this.name = "Tiny Colder";時坎怪,就相當(dāng)于是執(zhí)行p.name = "Tiny Colder";而構(gòu)造函數(shù)里的
var age = 22;語句罢坝,會執(zhí)行但是對新生成的對象并無影響。window.age = 22;語句搅窿,會執(zhí)行嘁酿,且會給window對象添加一個屬性。alert為證男应。
或許到這里闹司,你已經(jīng)理解了new操作符的前三步了,重要的三步沐飘。但是這個函數(shù)是如何返回對象的呢游桩?我們并沒有看到有任何跟return相關(guān)的語句。這就是new操作符的最后一步:返回新生成的對象耐朴。
如果被調(diào)用的函數(shù)沒有顯式的 return 表達(dá)式(僅限于返回對象)借卧,則隱式的會返回 this 對象 - 也就是新創(chuàng)建的對象。
現(xiàn)在來看一下這個代碼:
function Person(){
this.name = "Tiny Colder";
return {};
}
var p = new Person();
alert(p.name)//undefined;
一個對象就這么被創(chuàng)建出來了筛峭。
實際上铐刘,
var p = new Person();
和
var p = new Object();
Person.apply(p);
是一樣的效果。