簡(jiǎn)單介紹js中構(gòu)造函數(shù) 原型 原型鏈(一)

構(gòu)造函數(shù)

在ECMAScript中的構(gòu)造函數(shù)可用來(lái)創(chuàng)建特定類型的對(duì)象。像Object Array這樣的原生的構(gòu)造函數(shù) 在運(yùn)行時(shí)會(huì)自動(dòng)出現(xiàn)在運(yùn)行環(huán)境中。 當(dāng)然 我們也可以創(chuàng)建自定義的構(gòu)造函數(shù), 從而定義自定義對(duì)象類型的屬性和方法。
例如:

function Person(name, age, job) {
      this.name = name;
      this.age = age;
      this.job = job;
      this.sayName = function() {
        alert(this.name)
    };
}
var person1 = new Person("張三", 18,"工人");
var person2 = new Person("李四", 25, "包工頭")

在這個(gè)例子中, 我們可以看到與正常函數(shù)的一些不同之處:

1.沒(méi)有顯式地創(chuàng)建對(duì)象;
2.直接將屬性和方法賦給了this對(duì)象;
3. 沒(méi)有return 語(yǔ)句。

此外 我們還注意到函數(shù)名Person的首字母是大寫的,按照慣例 ,構(gòu)造函數(shù)的首字母是大寫的 而非構(gòu)造函數(shù)的首字母是不大寫的。
要?jiǎng)?chuàng)建Person函數(shù)的新實(shí)例, 必須使用new 操作符。 以這種方式調(diào)用構(gòu)造函數(shù)會(huì)經(jīng)歷以下四個(gè)步驟:

1.創(chuàng)建一個(gè)新對(duì)象;
2.將構(gòu)造函數(shù)的作用域賦給新對(duì)象(因此this就指向了這個(gè)新對(duì)象;
3. 執(zhí)行構(gòu)造函數(shù)中的代碼(為這個(gè)新對(duì)象添加新屬性);
4.返回新對(duì)象。

在前面例子的最后,person1和person2分別保存著Person的一個(gè)不同的實(shí)例。 這兩個(gè)對(duì)象都有一個(gè)constructor(構(gòu)造函數(shù)屬性),都指向了Person,如下所示

console.log(person1.constructor === Person);//返回true
console.log(person2.constructor === Person);//返回true

對(duì)象的constructor屬性最初是用來(lái)標(biāo)識(shí)對(duì)象類型的。 但是提到檢測(cè)對(duì)象類型,還是使用 instanceof操作符更加可靠一些。我們?cè)谶@個(gè)例子中創(chuàng)建的所有對(duì)象既是Object的實(shí)例 也是Person的實(shí)例,這一點(diǎn)通過(guò)instanceof操作符可以得到驗(yàn)證。

console.log(person1 instanceof Object)//返回true
console.log(person1 instanceof Person)//返回true
console.log(person2 instanceof Object)//返回true
console.log(person1 instanceof Person)//返回true
創(chuàng)建自定義的構(gòu)造函數(shù)意味著將來(lái)可以將它的實(shí)例標(biāo)識(shí)作為一種特定的類型,而這正是構(gòu)造函數(shù)模式勝過(guò)工廠模式的地方。

在這個(gè)例子中, 之所以person1和person2都是Object的實(shí)例,是因?yàn)樗袑?duì)象均繼承自O(shè)bject。
構(gòu)造函數(shù)和其他函數(shù)的唯一區(qū)別, 就是在于調(diào)用他們的方式不同。但是構(gòu)造函數(shù)也是函數(shù), 不存在定義構(gòu)造函數(shù)的特殊語(yǔ)法。 任何函數(shù),只要通過(guò)new操作符來(lái)調(diào)用,那它就可以作為構(gòu)造函數(shù), 而且任何函數(shù),不通過(guò)new 操作符來(lái)調(diào)用, 它就是普通函數(shù)。例如 前面的例子就可以通過(guò)以下任何一種方式來(lái)調(diào)用:

//當(dāng)作普通函數(shù)來(lái)調(diào)用:
  Person("柳白猿", 25,"箭士");//添加到了window上面;
  window.sayName(); // 返回柳白猿;

//當(dāng)作構(gòu)造函數(shù)來(lái)調(diào)用:
  var person = new Person("孫悟空", 500, "行者");
  person.sayName();// 孫悟空;

//在另一個(gè)對(duì)象的作用域中調(diào)用
var o = new Object();
Person.call(o,"八戒", 1000, "使者");
o.sayName ()//八戒

構(gòu)造函數(shù)的問(wèn)題

構(gòu)造函數(shù)雖然好用,但也并非沒(méi)有缺點(diǎn)。使用構(gòu)造函數(shù)的主要問(wèn)題,就是每個(gè)方法都要在每個(gè)實(shí)例上重新創(chuàng)建一遍。在前面的例子中 person1和person2都有一個(gè)名為名為sayName的方法,但那個(gè)方法不是同一個(gè)Function的實(shí)例。函數(shù)也是對(duì)象, 因此每定義一個(gè)函數(shù),就是實(shí)例化了一個(gè)對(duì)象。所以不同實(shí)例上的同名函數(shù)是不相等的, 以下代碼可以證明這一點(diǎn)。

console.log(person1.sayName ===person2.sayName)//返回false

然而 創(chuàng)建兩個(gè)完成同樣任務(wù)的Function的實(shí)例的確沒(méi)必要。況且有this對(duì)象在, 根本不用在執(zhí)行代碼前就把函數(shù)綁定到特定對(duì)象的上面。因此,大可以像下面這樣,通過(guò)函數(shù)定義轉(zhuǎn)移到構(gòu)造函數(shù)外邊來(lái)解決這個(gè)問(wèn)題。

function Person(name, age,job) {
  this.name  = name;
  this,age = age;
  this.job = job;
  this.sayName = sayName;
}
function sayName() {
  console.log(this.name);
}
var person1 = new Person('心猿意馬', 1500, '不明');
var person2 = new Person('覆水難收', 2000, '所以');

在這個(gè)例子中, 我們把sayName()函數(shù)的定義轉(zhuǎn)移到了構(gòu)造函數(shù)外部。 而在構(gòu)造函數(shù)內(nèi)部,我們把sayName的屬性設(shè)置成等于全局的sayName函數(shù)。這樣一來(lái),由于sayName包含的是一個(gè)指向函數(shù)的指針, 因此person1和person2對(duì)象就共享了在全局作用域中定義的同一個(gè)sayName()函數(shù)。這樣做確實(shí)解決了兩個(gè)函數(shù)做同一件事的問(wèn)題,可是新的問(wèn)題又來(lái)了,在全局作用域中,定義的函數(shù)是實(shí)際上只能被某個(gè)函數(shù)調(diào)用,這讓全局作用域有些名不副實(shí)。而更讓人無(wú)法接受的是,如果對(duì)象需要定義很多的方法,那么就要定義很多個(gè)全局函數(shù),于是我們這個(gè)自定義的引用類型就毫無(wú)意義可言了。 好在, 我們可以通過(guò)使用原型模式很好地解決這個(gè)問(wèn)題。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容