在《談談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
,聽起來有點混亂吧...不要緊,讓咱們來看一些代碼來幫助咱們理解這一律念。函數
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)所創建的對象都包含有某些咱們想要使用的方法。若是咱們有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源碼中,爲Date
、String
、Number
、Boolean
方法添加一個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博客點贊和關注,感激涕零!