挺有用的一篇文章,今天還有人在問我關於構造函數的方法和原型,構造函數的方法是定義在函數內容,做爲一個私有方法,不對外開放,而prototype則能夠經過對象定義,在外面訪問,更加深刻請看本文。app
本文的目的是讓你們理解什麼狀況下把函數的方法寫在JavaScript的構造函數上,何時把方法寫在函數的prototype屬性上;以及這樣作的好處.
爲了閱讀方便,咱們約定一下:把方法寫在構造函數內的狀況咱們簡稱爲函數內方法
,把方法寫在prototype屬性上的狀況咱們簡稱爲prototype上的方法
函數
首先咱們先了解一下這篇文章的重點:this
咱們仍是根據下面的代碼來講明一下這些要點吧,下面是代碼部分:spa
// 構造函數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
屬性上面.prototype
須要指出的是,經過這兩個構造函數new出來的對象具備同樣的屬性和方法,可是它們的區別咱們能夠經過下面的一個圖來講明:code
咱們經過使用構造函數A
建立了兩個對象,分別是a1
,a2
;經過構造函數B建立了兩個對象b1
,b2
;咱們能夠發現b1
,b2
這兩個對象的那個sayHello
方法 都是指向了它們的構造函數的prototype
屬性的sayHello
方法.而a1
,a2
都是在本身內部定義了這個方法. 對象
定義在構造函數內部的方法,會在它的每個實例上都克隆這個方法;定義在構造函數的prototype
屬性上的方法會讓它的全部示例都共享這個方法,可是不會在每一個實例的內部從新定義這個方法. 若是咱們的應用須要建立不少新的對象,而且這些對象還有許多的方法,爲了節省內存,咱們建議把這些方法都定義在構造函數的prototype
屬性上ip
固然,在某些狀況下,咱們須要將某些方法定義在構造函數中,這種狀況通常是由於咱們須要訪問構造函數內部的私有變量.內存
下面咱們舉一個二者結合的例子,代碼以下:原型鏈
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
屬性上就有效的減小了這種成本. 你也許會以爲,調用對象上的方法要比調用它的原型鏈上的方法快得多,其實並非這樣的,若是你的那個對象上面不是有不少的原型的話,它們的速度實際上是差很少的
另外,須要注意的一些地方:
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"}