JS建構函式精講

2020-07-16 10:05:08
JavaScript 建構函式(Constructor)也稱為構造器、型別函數,功能類似物件模板,一個建構函式可以生成任意多個範例,範例物件具有相同的屬性、行為特徵,但不相等。

定義建構函式

在語法和用法上,建構函式與普通函數沒有任何區別。定義建構函式的方法如下:
function 型別名稱 (設定引數) {
    this.屬性1 = 屬性值1;
    this.屬性2 = 屬性值2;
    ...
    this.方法1 = function () {
        //處理程式碼
    };
    ...
    //其他程式碼,可以包含return語句
}
建議建構函式的名稱首字母大寫,以便與普通函數進行區分。

建構函式有兩個顯著特點。
  • 函數體內使用 this,參照將要生成的範例物件。
  • 必需使用 new 命令呼叫函數,生成範例物件。

範例

下面範例演示定義一個建構函式,包含了兩個屬性和一個方法。
function Point (x, y) {  //建構函式
    this.x = x;  //私有屬性
    this.y = y;  //私有屬性
    this.sum = function () {  //方法
        return this.x + this.y;
    }
}
在上面程式碼中,Point 就是建構函式,它提供模板,用來生成範例物件。

呼叫建構函式

使用 new 命令可以呼叫建構函式,建立範例,並返回這個物件。

範例

下面使用 new 命令呼叫建構函式,生成兩個範例,然後分別讀取屬性,呼叫方法 sum()。
function Point (x, y) {  //建構函式
    this.x = x;  //私有屬性
    this.y = y;  //私有屬性
    this.sum = function () {  //私有方法
        return this.x +this.y;
    }
}
var p1 = new Point(100, 200);  //範例化物件1
var p2 = new Point(300, 400);  //範例化物件2
console.log(p1.x);  //100
console.log(p2.x);  //300
console.log(p1.sum());  //300
console.log(p2.sum());  //700

函數構造可以接收引數,以便初始化範例物件。如果不需要傳遞引數,可以省略小括號,直接使用 new 命令斯奧用,下面兩行程式碼是等價的。
var p1 = new Point();
var p2 = new Point;
如果不使用 new 命令,直接使用小括號呼叫建構函式,這時建構函式就是普通函數,不會生成範例物件,this 就代表呼叫函數的物件,在用戶端指代全域性物件 window。

為了避免誤用,最有效的方法是在函數中啟用嚴格模式。程式碼如下。
function Point (x, y) {  //建構函式
    'use strict';  //啟用嚴格模式
    this.x = x;  //私有屬性
    this.y = y;  //私有屬性
    this.sum = function () {  //私有方法
        return this.x + this.y;
    }
}
這樣呼叫建構函式時,必須使用 new 命令,否則將丟擲異常。

或者使用 if 對 this 進行檢測,如果 this 不是範例物件,則強迫返回範例物件。
function Point(x, y) {  //建構函式
    if (! (this instanceof Point)) return new Point(x, y);  //檢測this是否為範例物件
    this.x = x;  //私有屬性
    this.y = y;  //私有屬性
    this.sum = function () {  //私有方法
        return this.x + this.y;
    }
}

建構函式的返回值

建構函式執行使用 return 語句。如果返回值為簡單值,則將被忽略,直接返回 this 指代的範例物件;如果返回值為物件,則將覆蓋 this 指代的範例,返回 return 後面跟隨的物件。

為什麼會出現這種情況?這與 new 命令解析過程有關係,使用 new 命令呼叫函數的解析過程如下:
  • 當使用 new 命令呼叫函數時,先建立一個空物件,作為範例返回。
  • 設定範例的原型,指向建構函式的 prototype 屬性。
  • 設定建構函式體內的 this 值,讓它指向範例。
  • 開始執行建構函式內部的程式碼。
  • 如果建構函式內部有 return 語句,而且 return 後面跟著一個物件,會返回 return 語句指定的物件;否則會忽略 return 返回值,直接返回 this 物件。

範例

下面範例在建構函式內部定義 return 返回一個物件直接量,當使用 new 命令呼叫建構函式時,返回的不是 this 指代的範例,而是這個物件直接量,因此當讀取 x 和 y 屬性值時,與預期的結果是不同的。
function Point (x, y) {  //建構函式
    this.x = x;  //私有屬性
    this.y = y;  //私有屬性
    return {x : true, y : false}
}
var p1 = new Point(100, 200);  //範例化物件1
console.log(p1.x);  //true
console.log(p1.y);  //false

參照建構函式

在普通函數內,使用 arguments.callee 可以參照函數自身。如果在嚴格模式下,是不允許使用 arguments.cellee 參照函數的,這時可以使用 new.target 來存取建構函式。

範例

function Point (x, y) {  //建構函式
    'use strict';  //啟用嚴格模式
    if (! (this instanceof new.target)) return new new.target(x, y);  //檢測this是否為範例物件
    this.x = x;  //私有屬性
    this.y = y;  //私有屬性
}
var p1 = new Point(100, 200);  //範例化物件1
console.log(p1.x);  //100
IE 瀏覽器對其支援不是很完善,使用時要考慮相容性。