還在問什麼是JavaScript建構函式、例項、原型物件以及原型鏈?看完這篇你就懂

233發表於2020-08-24

1概述

ES6, 全稱 ECMAScript 6.0 ,2015.06 發版。在ES6之前,物件不是基於建立的,而是用一種稱為建構函式特殊函式來定義物件和它們的特徵。

2建構函式

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

    // 利用建構函式建立物件
    function Person(uname, age) {
        this.uname = uname;
        this.age = age;
        this.sing = function() {
            console.log('我會唱歌');
        }
    }
    var ldh = new Person('劉德華', 18);
    var zxy = new Person('張學友', 19);
    console.log(ldh);
    ldh.sing();
    zxy.sing();

在 JS 中,使用建構函式時要注意以下兩點:

  1. 建構函式用於建立某一類物件,其首字母要大寫
  2. 建構函式要和 new 一起使用才有意義

(1)建構函式執行過程

new 在執行時會做四件事情:
① 在記憶體中建立一個新的空物件。
② 讓 this 指向這個新的物件。
③ 執行建構函式裡面的程式碼,給這個新物件新增屬性和方法。
④ 返回這個新物件(所以建構函式裡面不需要 return )。

(2)建構函式的成員

JavaScript 的建構函式中可以新增一些成員,可以在建構函式本身上新增,也可以在建構函式內部的 this 上新增。通過這兩種方式新增的成員,就分別稱為靜態成員和例項成員。

  • 靜態成員:在建構函式本上新增的成員稱為靜態成員,只能由建構函式本身來訪問
//靜態成員 在建構函式本身上新增的成員  sex 就是靜態成員
// 靜態成員只能通過建構函式來訪問
Person.sex = '男';
 console.log(Person.sex);
  • 例項成員:在建構函式內部建立的物件成員稱為例項成員,只能由例項化的物件來訪問
function Person(uname) {
            this.uname = uname;
            }
例項成員就是建構函式內部通過this新增的成員 uname就是例項成員

3建構函式原型prototype

建構函式通過原型分配的函式是所有物件所共享的。
JavaScript 規定,每一個建構函式都有一個 prototype 屬性,指向另一個物件。注意這個 prototype 就是一個物件,這個物件的所有屬性和方法,都會被建構函式所擁有。
我們可以把那些不變的方法,直接定義在 prototype 物件上,這樣所有物件的例項就可以共享這些方法。

    function Person(uname, age) {
        this.uname = uname;
        this.age = age;
        // this.sing = function() {
        //     console.log('我會唱歌');
        // }
    }
    //  一般情況下,我們的公共屬性定義到建構函式裡面, 公共的方法我們放到原型物件身上
    Person.prototype.sing = function() {
        console.log('我會唱歌');
    }
    var ldh = new Person('劉德華', 18);
    var zxy = new Person('張學友', 19);
    console.log(ldh.sing === zxy.sing); //true
    ldh.sing();
    zxy.sing();
  1. 原型是什麼 ?
    一個物件,我們也稱為 prototype 為原型物件。
  2. 原型的作用是什麼 ?
    共享方法。

4 物件原型 __proto__

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

    function Person(uname, age) {
        this.uname = uname;
        this.age = age;
    }
    Person.prototype.sing = function() {
        console.log('我會唱歌');
    }
    var mingo = new Person('小明', 18);
    var zxy = new Person('張學友', 19);
    mingo.sing();
    console.log(mingo); // 物件身上系統自己新增一個 __proto__ 指向我們建構函式的原型物件 prototype
    console.log(mingo.__proto__ === Person.prototype); //true
  • __proto__物件原型和原型物件prototype是等價的

    (如圖箭頭指向,mingo是Person建構函式建立出來的一個物件例項,這個物件例項的原型(__proto__),就是Person建構函式的原型物件prototype。)

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

5constructor 建構函式

物件原型 __proto__和建構函式prototype原型物件(其實指的就是同一個)裡面有一個屬性 constructor 屬性 ,constructor 我們稱 為建構函式,因為它指回建構函式本身。

constructor 主要用於記錄該物件引用於哪個建構函式它可以讓原型物件重新指向原來的建構函式
一般情況下,物件的方法都在建構函式的原型物件中設定。如果有多個物件的方法,我們可以給原型物件採取物件形式賦值,但是這樣就會覆蓋建構函式原型物件原來的內容,這樣修改後的原型物件 constructor 就不再指向當前建構函式了。
此時,我們可以在修改後的原型物件中,新增一個 constructor 指向原來的建構函式。

    function Person(uname, age) {
        this.uname = uname;
        this.age = age;
    }
    // 很多情況下,我們需要手動的利用constructor 這個屬性指回 原來的建構函式
    Person.prototype = {
        // 如果我們修改了原來的原型物件,給原型物件賦值的是一個物件,則必須手動的利用constructor指回原來的建構函式
        constructor: Person,
        sing: function() {
            console.log('我會唱歌');
        },
        movie: function() {
            console.log('我會演電影');
        }
    }
    var mingo = new Person('小明', 18);
    var zxy = new Person('張學友', 19);
    console.log(Person.prototype);
    console.log(mingo.__proto__);
    console.log(Person.prototype.constructor);
    console.log(mingo.__proto__.constructor);

6原型鏈

每個建構函式都有一個原型物件,原型物件都包含一個指向建構函式的指標,而例項都包含一個指向原型物件的內部指標。那麼假如我們讓原型物件等於另一個型別的例項,結果會怎樣?顯然,此時的原型物件將包含一個指向另一個原型的指標,相應地,另一個原型中也包含著一個指向另一個建構函式的指標。假如另一個原型又是另一個型別的例項,那麼上述關係依然成立。如此層層遞進,就構成了例項與原型的鏈條。這就是所謂的原型鏈的基本概念。——摘自《javascript高階程式設計》

    function Person(uname, age) {
        this.uname = uname;
        this.age = age;
    }
    Person.prototype.sing = function() {
        console.log('我會唱歌');
    }
    var mingo = new Person('小明', 18);
    // 1. 只要是物件就有__proto__ 原型, 指向原型物件
    console.log(Person.prototype);
    console.log(Person.prototype.__proto__ === Object.prototype);//true
    // 2.我們Person原型物件裡面的__proto__原型指向的是 Object.prototype
    console.log(Object.prototype.__proto__);
    // 3. 我們Object.prototype原型物件裡面的__proto__原型  指向為 null
    console.log(Object);

Object 是 JavaScript 中最頂級的物件,其它所有物件都是基於它的,包括你建立的函式

7JavaScript 的成員查詢機制(基於原型鏈規則)

① 當訪問一個物件的屬性(包括方法)時,首先查詢這個物件自身有沒有該屬性。
② 如果沒有就查詢它的原型(也就是 __proto__指向的 prototype 原型物件)。
③ 如果還沒有就查詢原型物件的原型(Object的原型物件)。
④ 依此類推一直找到 Object 為止(null)。
__proto__物件原型的意義就在於為物件成員查詢機制提供一個方向,或者說一條路線。

8 原型物件this指向

1建構函式中的this 指向我們例項物件
2原型物件裡面放的是方法, 這個方法裡面的this 指向的是 這個方法的呼叫者, 也就是這個例項物件

    function Person(uname, age) {
        this.uname = uname;
        this.age = age;
    }
    var that;
    Person.prototype.sing = function() {
        console.log('我會唱歌');
        that = this;
        console.log(this);
        
    }
    var mingo = new Person('小明', 18);
    // 1. 在建構函式中,裡面this指向的是物件例項 mingo
    // 2.原型物件函式裡面的this 指向的也是 例項物件 mingo
    mingo.sing();
    console.log(that === mingo); //true

相關文章