JavaScript構造函數與原型之間的聯系

一、構造函數和原型

1、構造函數

構造函數是一種特殊的函數,主要用來初始化對象,即為對象成員變量賦初始值,它總與 new 一起使用。我們可以把對象中一些公共的屬性和方法抽取出來,然後封裝到這個函數裡面。
在 JS 中,

使用構造函數時要註意以下兩點:

  • 構造函數用於創建某一類對象,其首字母要大寫。
  • 構造函數要和new 一起使用才有意義。

new 在執行時會做四件事情:

  • 在內存中創建一個新的空對象。
  • 讓 this 指向這個新的對象。
  • 執行構造函數裡面的代碼,給這個新對象添加屬性和方法。
  • 返回這個新對象(所以構造函數裡面不需要 return )。

JavaScript 的構造函數中可以添加一些成員,可以在構造函數本身上添加,也可以在構造函數內部的 this 上添加。通過這兩種方式添加的成員,就分別稱為靜態成員和實例成員。
實例成員:在構造函數內部創建的對象成員稱為實例成員,隻能由實例化的對象來訪問。
靜態成員:在構造函數本上添加的成員稱為靜態成員,隻能由構造函數本身來訪問 。

例如:

 function A(uname,age){
            this.uname = uname;
            this.age  = age;
            this.say = function() {
                console.log(this.uname+'你好');
            }
        }
        var wh = new A('王歡',18);
        var xl = new A('小熊',18);

在上述代碼中,構造函數中通過this添加的name,age,say方法都是實例成員。隻能由實例化的對象來訪問。在構造函數本身上添加的成員叫靜態成員,

如:創建一個靜態成員。

A.sex='女';

2、構造函數的問題

構造函數方法很好用,但是存在浪費內存的問題

如下所示:

function Student(age,name){
            this.age = age;
            this.name = name;
            this.score = function(){
            console.log('孩子們成績都很好!');
        }
        }
        console.dir(Student);
        var xl = new Student(18,'小熊');
        var wh = new Student(17,'王歡');
        xl.score();
        wh.score();
       

通過下述代碼判斷兩次調用的方法是否地址相同。

 console.log(xl.score === wh.score);


打印結果為:

在這裡插入圖片描述

可知兩次調用A內部的say函數,地址並不相同,是因為開辟兩個內存空間,導致浪費內存。

在這裡插入圖片描述

3、構造函數原型 prototype

構造函數通過原型分配的函數是所有對象所共享的。JavaScript 規定,每一個構造函數都有一個 prototype屬性,指向另一個對象。註意這個 prototype 就是一個對象,這個對象的所有屬性和方法,都會被構造函數所擁有。

如下所示,創建一個構造函數:

function Student(age,name){
            this.age = age;
            this.name = name;
            this.score = function(){
                console.log('孩子們成績都很好!');
            }
        }
        console.dir(Student);

打印該構造函數裡面所有的方法,可知:

在這裡插入圖片描述

可以找到 prototype 對象。
可以把那些不變的方法,直接定義在prototype對象上,這樣所有對象的實例就可以共享這些方法。

function Student(age,name){
            this.age = age;
            this.name = name;
        }
        Student.prototype.score = function(){
            console.log('孩子們成績都很好!');
        }
        console.dir(Student);
        var xl = new Student(18,'小熊');
        var wh = new Student(17,'王歡');
        xl.score();
        wh.score();
        console.log(xl.score === wh.score);

打印結果為:

在這裡插入圖片描述

並且兩次調用函數隻開辟瞭一個內存空間,也減少瞭內存的浪費。

註意:一般情況下,公共屬性定義到構造函數裡面,公共方法定義到原型對象身上。

4、對象原型 __proto__

對象都會有一個屬性__proto__ 指向構造函數的 prototype 原型對象,之所以我們對象可以使用構造函數prototype原型對象的屬性和方法,就是因為對象有 __proto__原型的存在。

如下所示:

function Student(age,name){
            this.age = age;
            this.name = name;
           
        }
 Student.prototype.score = function(){
     console.log('孩子們成績都很好!');
 }
// console.dir(Student);
 var xl = new Student(18,'小熊');
 var wh = new Student(17,'王歡');
 console.log(xl);

通過以下代碼名看其是否具有__proto__對象原型

console.log(xl);//對象身上系統自己添加一個__proto__屬性指向構造函數的原型對象

輸出結果為:

在這裡插入圖片描述

可知存在。
在上述例子中輸入下述代碼判斷__proto__對象原型和原型對象prototype是否等價。

 console.log(xl.__proto__ === Student.prototype);

打印結果為:true
故: __proto__對象原型和原型對象prototype是等價的

通過實例對象調用score函數,如下所示:

xl.score();

輸出結果為:

在這裡插入圖片描述

可以調用,其方法查找規則是:首先看看xl對象身上是否有score方法,如果有,則執行這個對象上的score,如果沒有該方法,因為有__prooto__屬性的存在,就去構造函數原型對象 prototype身上去查找。

可用下圖描述:

在這裡插入圖片描述

__proto__對象原型的意義就在於為對象的查找機制提供一個方向,或者說一條路線,但是它是一個非標準屬性,因此實際開發中,不可以使用這個屬性,它隻是內部指向原型對象 prototype

5、constructor 構造函數

根據前面的例子,分別打印實例對象(Student)的對象原型( __proto__)和構造函數(xl)(prototype)原型對象

console.log(Student.prototype);
console.log(xl.__proto__);

打印結果為:

在這裡插入圖片描述

 可知:對象原型( __proto__)和構造函數(prototype)原型對象裡面都有一個屬性 constructor 屬性 ,constructor我們稱為構造函數,因為它指回構造函數本身。

再分別打印對象原型和構造函數原型的constroctor屬性。觀察其返回值。

 console.log(Student.prototype.constructor);
 console.log(xl.__proto__.constructor);

打印結果為:

在這裡插入圖片描述

可知他們均指向Student構造函數。
constructor 主要用於記錄該對象引用於哪個構造函數,它可以讓原型對象重新指向原來的構造函數。

  一般情況下,對象的方法都在構造函數的原型對象中設置。當給構造函數添加多個方法時,可以采用對象的方式,

如下所示:

 Student.prototype = {
            score: function(){
            console.log('孩子們成績都很好!')},
            study: function(){
            console.log('好好學習!');  
        }

當打印修改後的原型對象的consructor屬性時:

在這裡插入圖片描述

        發現原型對象的指向發生變化,這是因為給原型對象采取對象形式賦值,這樣就會覆蓋構造函數原型對象原來的內容,這樣修改後的原型對象constructor 就不再指向當前構造函數瞭。
此時,我們可以在修改後的原型對象中,添加一個 constructor指向原來的構造函數。

如下:

Student.prototype = {
            constructor:Student,
            score: function(){
            console.log('孩子們成績都很好!')},
            study: function(){
            console.log('好好學習!');  
        }

最後,打印的結果為

在這裡插入圖片描述

成功指回原構造函數。

6、構造函數、實例、原型對象三者之間的關系

根據上例:構造函數、實例、原型對象三者之間的關系可用下圖描述:

在這裡插入圖片描述

7、JavaScript 的成員查找機制(規則)

  • 當訪問一個對象的屬性(包括方法)時,首先查找這個對象自身有沒有該屬性。
  • 如果沒有就查找它的原型(也就是 __proto__指向的 prototype原型對象)。
  • 如果還沒有就查找原型對象的原型(Object的原型對象)。

依此類推一直找到 Object 為止(null)。
__proto__對象原型的意義就在於為對象成員查找機制提供一個方向,或者說一條路線。

8、 擴展內置對象

可以通過原型對象,對原來的內置對象進行擴展自定義的方法。
首先打印數組的原型對象,查看有哪些內置對象。

 console.log(Array.prototype);

打印結果為:

在這裡插入圖片描述

例如現在給數組增加自定義求偶數和的功能。

 Array.prototype.sum = function(){
            var sum = 0;
            for(var i=0;i<this.length;i++){
                sum += this[i];
            }
            return sum;
        }

檢查內置對象是否擴展成功,再次輸入:

console.log(Array.prototype);

在這裡插入圖片描述

構建成功,在給一個具體的實例對象,判斷是否可以正常使用:

var arr = [1,2,3];
        console.log(arr.sum());

打印結果為:

在這裡插入圖片描述

二、類的本質

  • class本質還是function.
  • 類的所有方法都定義在類的prototype屬性上
  • 類創建的實例,裡面也有__proto__ 指向類的prototype原型對象
  • ES6的類它的絕大部分功能,ES5都可以做到,新的class寫法隻是讓對象原型的寫法更加清晰、更像面向對象編程的語法而已。

到此這篇關於JavaScript構造函數與原型的文章就介紹到這瞭,更多相關JavaScript構造函數、原型內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: