本文的目的是讓你們理解什麼狀況下把函數的方法寫在JavaScript的構造函數上,何時把方法寫在函數的 prototype
屬性上;以及這樣作的好處.javascript
爲了閱讀方便,咱們約定一下:把方法寫在構造函數內的狀況咱們簡稱爲 函數內方法 ,把方法寫在 prototype
屬性上的狀況咱們簡稱爲 prototype上的方法java
函數內的方法:使用函數內的方法咱們能夠 訪問到函數內部的私有變量 ,若是咱們經過構造函數 new
出來的對象須要咱們操做構造函數內部的私有變量的話,web
咱們這個時候就要考慮使用函數內的方法.app
prototype上的方法:當咱們須要 經過一個函數建立大量的對象 ,而且這些對象還都有許多的方法的時候;這時咱們就要考慮在函數的 prototype
上添加這些方法.函數
這種狀況下咱們代碼的 內存佔用 就比較小.this
在實際的應用中,這兩種方法每每是結合使用的;因此咱們要首先了解咱們須要的是什麼,而後再去選擇如何使用.spa
咱們仍是根據下面的代碼來講明一下這些要點吧,下面是 代碼部分 :prototype
// 構造函數A function A(name) { this.name = name || 'a'; this.sayHello = function() { console.log('Hello, my name is: ' + this.name); } } // 構造函數B function B(name) { this.name = name || 'b'; } B.prototype.sayHello = function() { console.log('Hello, my name is: ' + this.name); }; var a1 = new A('a1'); var a2 = new A('a2'); a1.sayHello(); a2.sayHello(); var b1 = new B('b1'); var b2 = new B('b2'); b1.sayHello(); b2.sayHello();
咱們首先寫了兩個構造函數,第一個是 A
,這個構造函數裏面包含了一個方法 sayHello
;第二個是構造函數 B
,咱們把那個方法 sayHello
寫在了構造函數 B
的 prototype
屬性上面.code
須要指出的是,經過這兩個構造函數 new
出來的對象具備同樣的屬性和方法,可是它們的區別咱們能夠經過下面的一個圖來講明:對象
咱們經過使用構造函數 A
建立了兩個對象,分別是 a1
, a2
;經過構造函數 B
建立了兩個對象 b1
, b2
;咱們能夠發現 b1
, b2
這兩個對象的那個 sayHello
方法都是指向了它們的構造函數的 prototype
屬性的 sayHello
方法.而 a1
, a2
都是在本身內部定義了這個方法.
定義在構造函數內部的方法,會在它的每個實例上都克隆這個方法;定義在構造函數的 prototype
屬性上的方法會讓它的全部示例都共享這個方法,可是不會在每一個實例的內部從新定義這個方法 .若是咱們的應用須要建立不少新的對象,而且這些對象還有許多的方法,爲了節省內存,咱們建議把這些方法都定義在構造函數的 prototype
屬性上 固然,在某些狀況下,咱們須要將某些方法定義在構造函數中,這種狀況通常是由於咱們須要訪問構造函數內部的私有變量 .
下面咱們舉一個二者結合的例子,代碼以下:
function Person(name, family) { this.name = name; this.family = family; var records = [{type: "in", amount: 0}]; this.addTransaction = function(trans) { if(trans.hasOwnProperty("type") && trans.hasOwnProperty("amount")) { records.push(trans); } } this.balance = function() { var total = 0; records.forEach(function(record) { if(record.type === "in") { total += record.amount; } else { total -= record.amount; } }); return total; }; }; Person.prototype.getFull = function() { return this.name + " " + this.family; }; Person.prototype.getProfile = function() { return this.getFull() + ", total balance: " + this.balance(); };
在上面的代碼中,咱們定義了一個 Person
構造函數;這個函數有一個內部的私有變量 records
,這個變量咱們是不但願經過函數內部之外的方法去操做這個變量,因此咱們把操做這個變量的方法都寫在了函數的內部.而把一些能夠公開的方法寫在了 Person
的 prototype
屬性上,好比方法 getFull
和 getProfile
.
把方法寫在構造函數的內部,增長了經過構造函數初始化一個對象的成本,把方法寫在 prototype
屬性上就有效的減小了這種成本.你也許會以爲,調用對象上的方法要比調用它的原型鏈上的方法快得多,其實並非這樣的,若是你的那個對象上面不是有不少的原型的話,它們的速度實際上是差很少的。
另外,須要注意的一些地方:
首先若是是在函數的 prototype
屬性上定義方法的話,要牢記一點,若是你改變某個方法,那麼由這個構造函數產生的全部對象的那個方法都會被改變.
還有一點就是變量提高的問題,咱們能夠稍微的看一下下面的代碼:
func1(); // 這裏會報錯,由於在函數執行的時候,func1尚未被賦值. error: func1 is not a function var func1 = function() { console.log('func1'); }; func2(); // 這個會被正確執行,由於函數的聲明會被提高. function func2() { console.log('func2'); }
關於對象序列化的問題.定義在函數的 prototype
上的屬性不會被序列化,能夠看下面的代碼:
function A(name) { this.name = name; } A.prototype.sayWhat = 'say what...'; var a = new A('dreamapple'); console.log(JSON.stringify(a));
咱們能夠看到輸出結果是 {"name":"dreamapple"}
參考的文章或者問答:
技術交流QQ羣:15129679