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

本文的目的是讓你們理解什麼狀況下把函數的方法寫在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

相關文章
相關標籤/搜索