構造函數內的方法與構造函數prototype屬性上方法的對比

挺有用的一篇文章,今天還有人在問我關於構造函數的方法和原型,構造函數的方法是定義在函數內容,做爲一個私有方法,不對外開放,而prototype則能夠經過對象定義,在外面訪問,更加深刻請看本文。app

本文的目的是讓你們理解什麼狀況下把函數的方法寫在JavaScript的構造函數上,何時把方法寫在函數的prototype屬性上;以及這樣作的好處.

爲了閱讀方便,咱們約定一下:把方法寫在構造函數內的狀況咱們簡稱爲函數內方法,把方法寫在prototype屬性上的狀況咱們簡稱爲prototype上的方法函數

首先咱們先了解一下這篇文章的重點:this

  • 函數內的方法:
    使用函數內的方法咱們能夠訪問到函數內部的私有變量,若是咱們經過構造函數new出來的對象須要咱們操做構造函數內部的私有變量的話,
    咱們這個時候就要考慮使用函數內的方法.
  • prototype上的方法:
    當咱們須要經過一個函數建立大量的對象,而且這些對象還都有許多的方法的時候;這時咱們就要考慮在函數的prototype上添加這些方法.
    這種狀況下咱們代碼的內存佔用就比較小.
  • 在實際的應用中,這兩種方法每每是結合使用的;因此咱們要首先了解咱們須要的是什麼,而後再去選擇如何使用.

咱們仍是根據下面的代碼來講明一下這些要點吧,下面是代碼部分: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寫在了構造函數Bprototype屬性上面.prototype

須要指出的是,經過這兩個構造函數new出來的對象具備同樣的屬性和方法,可是它們的區別咱們能夠經過下面的一個圖來講明:code

clipboard.png

咱們經過使用構造函數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,這個變量咱們是不但願經過函數內部之外的方法 去操做這個變量,因此咱們把操做這個變量的方法都寫在了函數的內部.而把一些能夠公開的方法寫在了Personprototype屬性上,好比方法getFullgetProfile.

把方法寫在構造函數的內部,增長了經過構造函數初始化一個對象的成本,把方法寫在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"}

相關文章
相關標籤/搜索