首頁 >web前端 >js教程 >深入理解JavaScript系列(26):設計模式之建構函數模式詳解_javascript技巧

深入理解JavaScript系列(26):設計模式之建構函數模式詳解_javascript技巧

WBOY
WBOY原創
2016-05-16 16:11:28813瀏覽

介紹

建構函式大家都很熟悉了,不過如果你是新手,還是有必要來了解一下什麼叫構造函數的。建構函數用於創建特定類型的對象——不僅聲明了使用的對象,構造函數還可以接受參數以便第一次創建對象的時候設置對象的成員值。你可以自訂自己的建構函數,然後在裡面宣告自訂類型物件的屬性或方法。

基本用法

在JavaScript裡,建構子通常是認為用來實作實例的,JavaScript沒有類別的概念,但是有特殊的建構子。透過new關鍵字來呼叫定義的否早函數,你可以告訴JavaScript你要建立一個新物件且新物件的成員宣告都是建構函式裡定義的。在建構函數內部,this關鍵字引用的是新建立的物件。基本用法如下:

複製程式碼 程式碼如下:

function Car(model, year, miles) {
    this.model = model;
    this.year = year;
    this.miles = miles;
    this.output= function () {
        return this.model "走了" this.miles "公里";
    };
}

var tom= new Car("叔叔", 2009, 20000);
var dudu= new Car("Dudu", 2010, 5000);

console.log(tom.output());
console.log(dudu.output());

上面的範例是個非常簡單的建構子模式,但有點小問題。首先是使用繼承很麻煩了,其次output()在每次創建物件的時候都重新定義了,最好的方法是讓所有Car類型的實例都共享這個output()方法,這樣如果有大批量的實例的話,就會節省很多記憶體。

解決這個問題,我們可以使用以下方式:

複製程式碼 程式碼如下:

function Car(model, year, miles) {
    this.model = model;
    this.year = year;
    this.miles = miles;
    this.output= formatCar;
}

function formatCar() {
    return this.model "走了" this.miles "公里";
}


這個方式雖然可用,但是我們有以下更好的方式。

建構子與原型

JavaScript裡函數有個原型屬性叫prototype,當呼叫建構函式建立物件的時候,所有該建構函式原型的屬性在新建立物件上都可用。依照這樣,多個Car物件實例可以共享同一個原型,我們再擴充一下上例的程式碼:

複製程式碼 程式碼如下:

function Car(model, year, miles) {
    this.model = model;
    this.year = year;
    this.miles = miles;
}

/*
注意:這裡我們使用了Object.prototype.方法名,而不是Object.prototype
主要是用來避免重寫定義原型prototype物件
*/
Car.prototype.output= function () {
    return this.model "走了" this.miles "公里";
};

var tom = new Car("叔叔", 2009, 20000);
var dudu = new Car("Dudu", 2010, 5000);

console.log(tom.output());
console.log(dudu.output());


這裡,output()單一實例可以在所有Car物件實例裡共享使用。

另外:我們推薦建構子以大寫字母開頭,以便區分普通的函數。

只能用new嗎?

上面的範例對函數car都是用new來創建物件的,只有這一種方式麼?其實還有別的方式,我們列舉兩種:

複製程式碼 程式碼如下:

function Car(model, year, miles) {
    this.model = model;
    this.year = year;
    this.miles = miles;
    // 自訂一個output輸出內容
    this.output = function () {
        return this.model "走了" this.miles "公里";
    }
}

//方法1:作為函數呼叫
Car("叔叔", 2009, 20000);  //加入window物件上
console.log(window.output());

//方法2:在另一個物件的作用域內呼叫
var o = new Object();
Car.call(o, "Dudu", 2010, 5000);
console.log(o.output());


程式碼的方法1有點特殊,如果不適用new直接呼叫函數的話,this指向的是全域物件window,我們來驗證一下:
複製程式碼 程式碼如下:

//作為函數呼叫
var tom = Car("叔叔", 2009, 20000);
console.log(typeof tom); // "undefined"
console.log(window.output()); // "大叔走了20000公里"

這時候物件tom是undefined,而window.output()會正確輸出結果,而如果使用new關鍵字則沒有這個問題,驗證如下:
複製程式碼 程式碼如下:

//使用new 關鍵字
var tom = new Car("叔叔", 2009, 20000);
console.log(typeof tom); // "object"
console.log(tom.output()); // "叔叔走了20000公里"

強制使用new

上述的例子顯示了不使用new的問題,那麼我們有沒有辦法讓建構子強制使用new關鍵字呢,答案是肯定的,上碼:

複製程式碼 程式碼如下:

function Car(model, year, miles) {
    if (!(this instanceof Car)) {
        return new Car(model, year, miles);
    }
    this.model = model;
    this.year = year;
    this.miles = miles;
    this.output = function () {
        return this.model "走了" this.miles "公里";
    }
}

var tom = new Car("叔叔", 2009, 20000);
var dudu = Car("Dudu", 2010, 5000);

console.log(typeof tom); // "object"
console.log(tom.output()); // "叔叔走了20000公里"
console.log(typeof dudu); // "object"
console.log(dudu.output()); // "Dudu走了5000公里"


透過判斷this的instanceof是不是Car來決定回傳new Car還是繼續執行程式碼,如果使用的是new關鍵字,則(this instanceof Car)為真,會繼續執行下面的參數賦值,如果沒有用new,(this instanceof Car)就為假,就會重新new一個實例回傳。

原包裝函數

JavaScript裡面有3中原始包裝函數:number, string, boolean,有時候兩種都用:

複製程式碼 程式碼如下:

// 使用原始包裝函數
var s = new String("my string");
var n = new Number(101);
var b = new Boolean(true);


// 推薦這種
var s = "my string";
var n = 101;
var b = true;


推薦,只有在想保留數值狀態的時候使用這些包裝函數,關於差異可以參考下面的程式碼:
複製程式碼 程式碼如下:

// 原始string
var greet = "Hello there";
// 用split()方法分割
greet.split(' ')[0]; // "Hello"
// 為原始型別新增屬性不會報錯
greet.smile = true;
// 單沒辦法取得這個值(18章ECMAScript實作裡我們講了為什麼)
console.log(typeof greet.smile); // "undefined"

// 原始string
var greet = new String("Hello there");
// 用split()方法分割
greet.split(' ')[0]; // "Hello"
// 為包裝函數類型新增屬性不會報錯
greet.smile = true;
// 可以正常存取新屬性
console.log(typeof greet.smile); // "boolean"

總結

本章主要講解了建構函式模式的使用方法、呼叫方法以及new關鍵字的差別,希望大家在使用的時候有所注意。

參考:http://www.addyosmani.com/resources/essentialjsdesignpatterns/book/#constructorpatternjavascript

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn