談談JavaScript中的function constructor和prototype的創建

《談談JavaScript中的function constructor和new關鍵字》這篇文章中咱們說明了如何經過函數構造式(function constructor)搭配關鍵字new來創建對象,但其實這樣只講了一半,在這篇咱們會補齊另外一半,說明function constructor如何用來設定該對象的原型(prototype)。javascript

在JavaScript中的函數也是一種對象,其中包含一些屬性像是該函數的名稱(Name)和該函數的內容(Code),但其實function這裏面還有一個屬性,這個屬性就是prototype,這個屬性會以空對象的型式呈現。java

除非你是把function當作function constructor來使用,不然這個屬性就沒有特別的用途;但若是你是把它當作function constructor,經過new這個關鍵字來執行這個function的話,它就有特別的意義了。git

要進入這個function的prototype屬性只要直接經過 .prototype 就能夠了。github

然而,有一點很容易使人困惑的地方,咱們會覺得若是我使用 .prototype 時,就能夠直接進入該函數的原型,但實際上並非這樣的!json

函數當中prototype這個屬性並非這個函數的prototype,它指的是全部經過這個function constructor所創建出來的對象的prototype,聽起來有點混亂吧...不要緊,讓咱們來看一些代碼來幫助咱們理解這一律念。函數

說明函數中的prototype 屬性

1.function 中的prototype 屬性一開始是空對象性能

咱們先執行上篇文章最後所寫的代碼:ui

function Person ( firstName , lastName ) { 
  this . firstName = firstName ; 
  this . lastName = lastName ; 
}

var person1 =  new  Person ( 'Jay' ,  'chou' ) ; 
console . log ( person1 ) ; 
var person2 =  new  Person ( 'Jane' ,  'chou' ) ; 
console . log ( person2 ) ;
複製代碼

到Google Chrome的console視窗中,咱們輸入 Person.prototype獲得的結果會獲得一個空對象,以下圖:this

2.經過function constructor 所創建的對象會繼承該function 中prototype 的內容spa

接着,讓咱們在Person.prototype裏面增長一個getFullName的函數:

function Person ( firstName , lastName ) { 
  this . firstName = firstName ; 
  this . lastName = lastName ; 
}

Person . prototype . getFullName  =  function ( ) { 
  return  this . firstName +  ' '  +  this . lastName ; 
}

var person1 =  new  Person ( 'Jay' ,  'chou' ) ; 
console . log ( person1 ) ; 
var person2 =  new  Person ( 'Jane' ,  'chou' ) ; 
console . log ( person2 ) ;
複製代碼

在上面代碼的第6 - 8行中,咱們爲Person.prototype添加了一個函數,因此當咱們在Google Chrome的console視窗中調用Person.prototype時,會多了這個函數在內:

剛剛,咱們有提到很重要的一句話,「函數當中prototype這個屬性並非這個函數的prototype,它指的是全部經過這個function constructor所創建出來的對象的prototype」。

這句話的意思實際上是說Person.prototype並非Person.__proto__,可是全部經過Person這個function constructor所創建的對象,在該實例對象的__proto__中,會包含有Person.prototype的內容。

也就是說,當咱們使用new這個運算符來執行function constructor時,它會先創建一個空對象,同時將該構造函數中prototype這個屬性的內容(Person.prototype),設置到該實例對象的prototype中,即 person1.__proto__ === Person.prototype的結果爲true

所以,當咱們在Google Chrome的console中輸入person1.__proto__時,咱們就能夠看到剛剛在Person.prototype所創建的函數getFullName已經繼承在裏面了:

實際運用

因爲Person.prototype中的方法已經被繼承到由Person這個function constructor所創建的實例對象person1中,因此這時侯,咱們就能夠順利的使用 person1.getFullName 這個方法:

function Person ( firstName , lastName ) { 
  this . firstName = firstName ; 
  this . lastName = lastName ; 
}

Person . prototype . getFullName  =  function ( ) { 
  return  this . firstName +  ' '  +  this . lastName ; 
}

var person1 =  new  Person ( 'Jay' ,  'chou' ) ; 
console . log ( person1 ) ; 
console . log ( person1.getFullName() ) ;
複製代碼

能夠正確的執行getFullName這個函數並獲得以下的結果:

經過function constructor與Prototype 的實用處

經過這樣的方法,咱們可讓全部根據這個函數構造器(function constructor)所創建的對象都包含有某些咱們想要使用的方法。若是咱們有1000個對象是根據這個函數構造器所創建的,那麼咱們只須要使用 .prototype這樣的方法,就可讓這1000個物件均可以使用到咱們想要執行的某個method,這樣減小了代碼的複用。

有的人可能會好奇問,爲何咱們不把getFullName這個方法直接寫在函數構造式當中呢?

function Person ( firstName , lastName ) { 
  this . firstName = firstName ; 
  this . lastName = lastName ; 
  this . getFullName  =  function ( ) { 
    return  this . firstName +  ' '  +  this . lastName ; 
  }
}

/* Person . prototype . getFullName = function ( ) { return this . firstName + ' ' + this . lastName ; } */
複製代碼

注意!咱們不應把方法放在function constructor 中。

把方法放在函數構造式中這麼作雖然仍然能夠正確執行並獲得結果,可是這麼作會有個問題,若是咱們是把這個方法直接寫在函數構造式中,那麼每個對象都會包含有這個方法,若是咱們有1000 個對象根據這個函數構造式所創建,那麼這1000 個對象都會包含這個方法在內,如此將會佔據至關多的內存;但若是是創建在prototype 中,咱們只會有一個這樣的方法。

因此,爲了性能上的考量,一般會把方法(method)放在構造函數的prototype 中,由於它們能夠是通用的;把屬性(property)放在構造函數當中,由於每個對象可能都會有不一樣的屬性內容,如此將能有效減小內存的問題。

最後,若是感受當前缺乏你要用的方法,能夠本身經過這一方法去建立。

例如在json2.js源碼中,爲DateStringNumberBoolean方法添加一個toJSON的屬性。

if (typeof Date.prototype.toJSON !== 'function') {
  Date.prototype.toJSON = function (key) {
    return isFinite(this.valueOf()) ?
        this.getUTCFullYear() + '-' +
      f(this.getUTCMonth() + 1) + '-' +
      f(this.getUTCDate()) + 'T' +
      f(this.getUTCHours()) + ':' +
      f(this.getUTCMinutes()) + ':' +
      f(this.getUTCSeconds()) + 'Z' : null;
  };

  String.prototype.toJSON = 
  Number.prototype.toJSON = 
  Boolean.prototype.toJSON = function (key) {
    return this.valueOf();
  };
}
複製代碼

若是你要添加內置方法的原型屬性,最好作一步判斷,若是該屬性不存在,則添加。若是原本就存在,就不必再添加了。

若是以爲文章對你有些許幫助,歡迎在個人GitHub博客點贊和關注,感激涕零!

相關文章
相關標籤/搜索