JavaScript高級程序設計-引用類型(一)object、Array、Date、RegExp

5 引用類型

ES5中的引用類型是一種數據結構,用於將數據與功能組織在一起,通常被稱爲類,有時候也被稱爲對象定義。

對象是某個特定引用類型的實例。新對象是使用 new 操作符後跟一個構造函數來創建的。構造函數本身就是一個函數,只不過該函數是出於創建新對象的目的而定義的

5.1 Object類型

Object 也是ECMAScript 中使用最多的一個類型。
創建 Object 實例的方式有兩種。

  • 第一種是使用 new 操作符後跟 Object 構造函數
var person = new Object();
person.name = "april";
person.age = 26;
  • 第二種對象字面量表示法。對象字面量是對象定義的一種簡寫形式,目的在於簡化創建包含大量屬性的對象的過程。
var person = {
    name:"april",
    age:26
};

ECMAScript 中的表達式上下文指的是能夠返回一個值(表達式)。賦值操作符表示後面是一個值,所以左花括號在這裏表示一個表達式的開始。同樣的花括號,如果出現在一個語句上下文( statement context)中,例如跟在 if 語句條件的後面,則表示一個語句塊的開始。使用逗號分隔兩個不同的屬性,在最後一個屬性中不添加逗號,若添加會在IE7及Opera中出錯。
使用對象字面量語法時,如果留空其花括號,則可以定義只包含默認屬性和方法的對象

var person = {}; //與new object()相同
person.name = "april";
person.age = 26;

一般來說,訪問對象屬性時使用的都是點表示法,這也是很多面向對象語言中通用的語法。不過,在 JavaScript 也可以使用方括號表示法來訪問對象的屬性。在使用方括號語法時,應該將要訪問的屬性以字符串的形式放在方括號中

alert(person["name"]); //april
alert(person.name);    //april

但方括號語法的主要優點是可以通過變量來訪問屬性

var propertyName = "name";
alert(person[propertyName]); //april

5.2 Array類型

ECMAScript 中的數組與其他多數語言中的數組有着相當大的區別。雖然 ECMAScript 數組與其他語言中的數組都是數據的有序列表,但與其他語言不同的是, ECMAScript 數組的每一項可以保存任何類型的數據。也就是說,可以用數組的第一個位置來保存字符串,用第二位置來保存數值,用第三個位置來保存對象,以此類推。而且, ECMAScript 數組的大小是可以動態調整的,即可以隨着數據的添加自動增長以容
納新增數據。
創建數組的基本方式有兩種。第一種是使用 Array 構造函數

var colors = new Array();

預先分配定長數組

var colors = new Array(20);

向 Array 構造函數傳遞數組中應該包含的項

var colors = new Array("red", "blue", "green");

使用Array構造函數創建數組時也可以省略new操作符

var colors = Array(3);      //包含三項的數組

var names = Array("april"); //包含一項

創建數組的第二種基本方式是使用數組字面量表示法。數組字面量由一對包含數組項的方括號表示,多個數組項之間以逗號隔開

var colors = ["red", "blue", "green"]; // 創建一個包含 3 個字符串的數組
var names = []; // 創建一個空數組
var values = [1,2,]; // 不要這樣!這樣會創建一個包含 2 或 3 項的數組
var options = [,,,,,]; // 不要這樣!這樣會創建一個包含 5 或 6 項的數組
//chrome console下面的結果
values
(2) [1, 2]
options
(5) [empty × 5]
names
[]
colors
(3) ["red", "blue", "green"]

在讀取和設置數組的值時,要使用方括號並提供相應值的基於 0 的數字索引

var colors = ["red", "blue", "green"]; // 定義一個字符串數組
alert(colors[0]); // 顯示第一項
colors[2] = "black"; // 修改第三項
colors[3] = "brown"; // 新增第四項

方括號中的索引表示要訪問的值。如果索引小於數組中的項數,則返回對應項的值

數組的 length 屬性很有特點——它不是隻讀的。因此,通過設置這個屬性,可以從數組的末尾移除項或向數組中添加新項。

var colors = ["red", "blue", "green"]; // 創建一個包含 3 個字符串的數組
colors.length = 2;
alert(colors[2]); //undefined

利用 length 屬性也可以方便地在數組末尾添加新項

var colors = ["red", "blue", "green"]; // 創建一個包含 3 個字符串的數組
colors[colors.length] = "black"; //(在位置 3)添加一種顏色
colors[colors.length] = "brown"; //(在位置 4)再添加一種顏色

::: danger
數組最多可以包含 4 294 967 295 個項,這幾乎已經能夠滿足任何編程需求了。如果想添加的項數超過這個上限值,就會發生異常。而創建一個初始大小與這個上限值接近的數組,則可能會導致運行時間超長的腳本錯誤。
:::

5.2.1 檢測數組

使用 instanceof 操作符

if(value instanceof Array){
    //對數組執行某些操作
}

instanceof 操作符的問題在於,它假定只有一個全局執行環境。如果網頁中包含多個框架,那實際上就存在兩個以上不同的全局執行環境,從而存在兩個以上不同版本的 Array 構造函數。如果你從一個框架向另一個框架傳入一個數組,那麼傳入的數組與在第二個框架中原生創建的數組分別具有各自不同的構造函數。

爲了解決這個問題, ECMAScript 5 新增了 Array.isArray()方法。這個方法的目的是最終確定某個值到底是不是數組,而不管它是在哪個全局執行環境中創建的。

if(Array.isArrsy(value)){
    //對數組執行某些操作
}

5.2.2 轉換方法

所有對象都具有 toLocaleString()、 toString()和 valueOf()方法。其中,調用數組的 toString()方法會返回由數組中每個值的字符串形式拼接而成的一個以逗號分隔的字符串。而調用 valueOf()返回的還是數組

//Chrome console
colors.toString()
"red,blue,green"
colors.valueOf()
(3) ["red", "blue", "green"]
colors
(3) ["red", "blue", "green"]

當調用數組的 toLocaleString()方法時,它也會創建一個數組值的以逗號分隔的字符串。而與前兩個方法唯一的不同之處在於,這一次爲了取得每一項的值,調用的是每一項的 toLocaleString()方法,而不是 toString()方法。

var person1 = {
    toLocaleString : function () {
        return "ThreeST";
    },
    toString : function() {
        return "threeST";
    }
};
var person2 = {
    toLocaleString : function () {
        return "April";
    },
    toString : function() {
        return "april";
    }
};
var people = [person1, person2];
alert(people); //threeST,april
people.toString()
"threeST,april"
people.toLocaleString()
"ThreeST,April"

數組繼承的 toLocaleString()、 toString()和 valueOf()方法,在默認情況下都會以逗號分隔的字符串的形式返回數組項。而如果使用 join()方法,則可以使用不同的分隔符來構建這個字符串。 join()方法只接收一個參數,即用作分隔符的字符串,然後返回包含所有數組項的字符串。

var colors = ["red", "green", "blue"];
alert(colors.join(",")); //red,green,blue
alert(colors.join("||")); //red||green||blue

5.2.3 棧方法

棧是一種 LIFO( Last-In-First-Out,後進先出)的數據結構,也就是最新添加的項最早被移除。而棧中項的插入(叫做推入)和移除(叫做彈出),只發生在一個位置——棧的頂部。 ECMAScript 爲數組專門提供了 push()和 pop()方法

  • push()方法可以接收任意數量的參數,把它們逐個添加到數組末尾,並返回修改後數組的長度。
  • pop()方法則從數組末尾移除最後一項,減少數組的 length 值,然後返回移除的項。
var colors = new Array(); // 創建一個數組
var count = colors.push("red", "green"); // 推入兩項
alert(count); //2
count = colors.push("black"); // 推入另一項
alert(count); //3
var item = colors.pop(); // 取得最後一項
alert(item); //"black"
alert(colors.length); //2

5.2.4 隊列方法

棧數據結構的訪問規則是 LIFO(後進先出),而隊列數據結構的訪問規則是 FIFO( First-In-First-Out,先進先出)。隊列在列表的末端添加項,從列表的前端移除項。

  • push()是向數組末端添加項的方法
  • shift()能夠移除數組中的第一個項並返回該項,同時將數組長度減 1
var colors = new Array(); //創建一個數組
var count = colors.push("red", "green"); //推入兩項
alert(count); //2
count = colors.push("black"); //推入另一項
alert(count); //3
var item = colors.shift(); //取得第一項
alert(item); //"red"
alert(colors.length); //2

ECMAScript 還爲數組提供了一個 unshift()方法。顧名思義, unshift()與 shift()的用途相反:它能在數組前端添加任意個項並返回新數組的長度。因此,同時使用 unshift()和 pop()方法,可以從相反的方向來模擬隊列,即在數組的前端添加項,從數組末端移除項,

var colors = new Array(); //創建一個數組
var count = colors.unshift("red", "green"); //推入兩項
alert(count); //2
count = colors.unshift("black"); //推入另一項
alert(count); //3
var item = colors.pop(); //取得最後一項
alert(item); //"green"
alert(colors.length); //2

5.2.5 重排序方法

數組中已經存在兩個可以直接用來重排序的方法: reverse()和 sort()

  • reverse()反轉數組排序
  • sort()
var values = [1,2,3,4,5];
values.reverse();
(5) [5, 4, 3, 2, 1]
values.sort();
(5) [1, 2, 3, 4, 5]

sort()方法會調用每個數組項的 toString()轉型方法,然後比較得到的字符串,以確定如何排序。即使數組中的每一項都是數值, sort()方法比較的也是字符串

var values = [0, 1, 5, 10, 15];
values.sort();
(5) [0, 1, 10, 15, 5]

比較函數接收兩個參數,如果第一個參數應該位於第二個之前則返回一個負數,如果兩個參數相等則返回 0,如果第一個參數應該位於第二個之後則返回一個正數。

function compare(value1,value2){
    if(value1<value2){
        return -1;
    }else if(value1>value2){
        return 1;
    }else{
        return 0;
    }
}
//調用上面函數
var values = [0, 1, 5, 10, 15];
values.sort(compare);
alert(values); //0,1,5,10,15

//倒序排序
function compareReverse(value1,value2){
    if(value1<value2){
        return 1;
    }else if(value1>value2){
        return -1;
    }else{
        return 0;
    }
}
values.sort(compareReverse);
alert(values); // 15,10,5,1,0

比較簡單的方法

function compare(value1,value2){
    return value2 - value1;
}

5.2.6 操作方法

  • concat()方法可以基於當前數組中的所有項創建一個新數組。具體來說,這個方法會先創建當前數組一個副本,然後將接收到的參數添加到這個副本的末尾,最後返回新構建的數組。在沒有給 concat()方法傳遞參數的情況下,它只是複製當前數組並返回副本。如果傳遞給 concat()方法的是一或多個數組,則該方法會將這些數組中的每一項都添加到結果數組中。如果傳遞的值不是數組,這些值就會被簡單地添加到結果數組的末尾
var colors = ["red", "green", "blue"];
var colors2 = colors.concat("yellow", ["black", "brown"]);
alert(colors); //red,green,blue
alert(colors2); //red,green,blue,yellow,black,brown
  • slice(),它能夠基於當前數組中的一或多個項創建一個新數組。 slice()方法可以接受一或兩個參數,即要返回項的起始和結束位置。在只有一個參數的情況下, slice()方法返回從該參數指定位置開始到當前數組末尾的所有項。如果有兩個參數,該方法返回起始和結束位置之間的項——但不包括結束位置的項。注意, slice()方法不會影響原始數組
var colors = ["red", "green", "blue", "yellow", "purple"];
var colors2 = colors.slice(1);
var colors3 = colors.slice(1,4);
alert(colors2); //green,blue,yellow,purple
alert(colors3); //green,blue,yellow

::: tip
如果 slice()方法的參數中有一個負數,則用數組長度加上該數來確定相應的位置。例如,在一個包含 5 項的數組上調用 slice(-2,-1)與調用 slice(3,4)得到的結果相同。如果結束位置小於起始位置,則返回空數組。
:::

  • splice()方法,這個方法恐怕要算是最強大的數組方法了,它有很多種用法。splice()的主要用途是向數組的中部插入項,但使用這種方法的方式則有如下 3 種
    • 刪除:可以刪除任意數量的項,只需指定 2 個參數:要刪除的第一項的位置和要刪除的項數。例如, splice(0,2)會刪除數組中的前兩項。
    • 插入:可以向指定位置插入任意數量的項,只需提供 3 個參數:起始位置、 0(要刪除的項數)和要插入的項。如果要插入多個項,可以再傳入第四、第五,以至任意多個項。例如,splice(2,0,“red”,“green”)會從當前數組的位置 2 開始插入字符串"red"和"green"。
    • 替換:可以向指定位置插入任意數量的項,且同時刪除任意數量的項,只需指定 3 個參數:起始位置、要刪除的項數和要插入的任意數量的項。插入的項數不必與刪除的項數相等。例如,splice (2,1,“red”,“green”)會刪除當前數組位置 2 的項,然後再從位置 2 開始插入字符串"red"和"green"。
      splice()方法始終都會返回一個數組,該數組中包含從原始數組中刪除的項(如果沒有刪除任何項,則返回一個空數組)。
var colors = ["red", "green", "blue"];
var removed = colors.splice(0,1); // 刪除第一項
alert(colors); // green,blue
alert(removed); // red,返回的數組中只包含一項
removed = colors.splice(1, 0, "yellow", "orange"); // 從位置 1 開始插入兩項
alert(colors); // green,yellow,orange,blue
alert(removed); // 返回的是一個空數組
removed = colors.splice(1, 1, "red", "purple"); // 插入兩項,刪除一項
alert(colors); // green,red,purple,orange,blue
alert(removed); // yellow,返回的數組中只包含一項

5.2.7 位置方法

ECMAScript 5 爲數組實例添加了兩個位置方法: indexOf()和 lastIndexOf()。這兩個方法都接收兩個參數:要查找的項和(可選的)表示查找起點位置的索引。其中, indexOf()方法從數組的開頭(位置 0)開始向後查找, lastIndexOf()方法則從數組的末尾開始向前查找。
這兩個方法都返回要查找的項在數組中的位置,或者在沒找到的情況下返回-1

var numbers = [1,2,3,4,5,4,3,2,1];
alert(numbers.indexOf(4)); //3
alert(numbers.lastIndexOf(4)); //5
alert(numbers.indexOf(4, 4)); //5
alert(numbers.lastIndexOf(4, 4)); //3
var person = { name: "Nicholas" };
var people = [{ name: "Nicholas" }];
var morePeople = [person];
alert(people.indexOf(person)); //-1
alert(morePeople.indexOf(person)); //0

5.2.8 迭代方法

ECMAScript 5 爲數組定義了 5 個迭代方法。每個方法都接收兩個參數:要在每一項上運行的函數和(可選的)運行該函數的作用域對象——影響 this 的值。傳入這些方法中的函數會接收三個參數:數組項的值、該項在數組中的位置和數組對象本身。根據使用的方法不同,這個函數執行後的返回值可能會也可能不會影響方法的返回值。以下是這 5 個迭代方法的作用。

  • every():對數組中的每一項運行給定函數,如果該函數對每一項都返回 true,則返回 true。
  • filter():對數組中的每一項運行給定函數,返回該函數會返回 true 的項組成的數組。
  • forEach():對數組中的每一項運行給定函數。這個方法沒有返回值。
  • map():對數組中的每一項運行給定函數,返回每次函數調用的結果組成的數組。
  • some():對數組中的每一項運行給定函數,如果該函數對任一項返回 true,則返回 true。
    以上方法都不會修改數組中的包含的值。
var numbers = [1,2,3,4,5,4,3,2,1];
var everyResult = numbers.every(function(item,index,array){
    return (item>2);
});
alert(everyResult);   //false

var someResult = numbers.some(function(item,index,array){
    return (item>2);
});

alert(someResult); //true

var filterResult = numbers.filter(function(item,index,array){
    return (item>2);
});

alert(filterResult); //[3,4,5,4,3]

var mapResult = numbers.map(function(item.index,array){
    return item*2;
});

alert(mapResult); //[2,4,6,8,10,8,6,4,2]

numbers.forEach(function(item,index,array){
    //todo
});

5.2.9歸併方法

reduce()和 reduceRight()。這兩個方法都會迭
代數組的所有項,然後構建一個最終返回的值。其中, reduce()方法從數組的第一項開始,逐個遍歷
到最後。而 reduceRight()則從數組的最後一項開始,向前遍歷到第一項。
這兩個方法都接收兩個參數:一個在每一項上調用的函數和(可選的)作爲歸併基礎的初始值。給 reduce()和 reduceRight()的函數接收 4 個參數:前一個值、當前值、項的索引和數組對象。這
個函數返回的任何值都會作爲第一個參數自動傳給下一項。第一次迭代發生在數組的第二項上,因此第
一個參數是數組的第一項,第二個參數就是數組的第二項。

var values = [1,2,3,4,5];
var sum =values.reduce(function(prev,cur,index,array){
    return prev+cur;
});

alert(sum); //15

5.3 Date類型

ECMAScript 中的 Date 類型是在早期 Java 中的 java.util.Date 類基礎上構建的。爲此, Date類型使用自 UTC( Coordinated Universal Time,國際協調時間) 1970 年 1 月 1 日午夜(零時)開始經過的毫秒數來保存日期。在使用這種數據存儲格式的條件下, Date 類型保存的日期能夠精確到 1970 年 1月 1 日之前或之後的 285 616 年。

var date = new Date();//自動獲得當前時間

想根據特定的日期和時間創建日期對象,必須傳入表示該日期的毫秒數(即從UTC 時間 1970 年 1 月 1 日午夜起至該日期止經過的毫秒數)。爲了簡化這一計算過程, ECMAScript 提供了兩個方法: Date.parse()和 Date.UTC()。

  • Date.parse()方法接收一個表示日期的字符串參數,然後嘗試根據這個字符串返回相應日期的毫秒數。
    • “月/日/年”,如 6/13/2019;
    • “英文月名 日,年”,如 January 12,2019;
    • “英文星期幾 英文月名 日 年 時:分:秒 時區”,如 Tue May 25 2019 00:00:00 GMT-0700。
    • ISO 8601 擴展格式 YYYY-MM-DDTHH:mm:ss.sssZ(例如 2019-05-25T00:00:00)。只有兼容ECMAScript 5 的實現支持這種格式。
      例如,要爲 2020 年 5 月 25 日創建一個日期對象,可以使用下面的代碼:
var someDate = new Date(Date.parse("May 25, 2020"));

Date.UTC()的參數分別是年份、基於 0 的月份(一月是 0,二月是 1,以此類推)、月中的哪一天(1 到 31)、小時數( 0 到 23)、分鐘、秒以及毫秒數。在這些參數中,只有前兩個參數(年和月)是必需的。如果沒有提供月中的天數,則假設天數爲 1;如果省略其他參數,則統統假設爲 0。

// GMT 時間 2000 年 1 月 1 日午夜零時
var y2k = new Date(Date.UTC(2000, 0));
// GMT 時間 2015 年 5 月 5 日下午 5:55:55
var allFives = new Date(Date.UTC(2015, 4, 5, 17, 55, 55));
//取得開始時間
var start = Date.now();
//調用函數
doSomething();
//取得停止時間
var stop = Date.now(),
result = stop – start;

5.3.1 繼承的方法

Date 類型也重寫了 toLocaleString()、 toString()和 valueOf()方法;但這些方法返回的值與其他類型中的方法不同。 Date 類型的 toLocaleString()方法會按照與瀏覽器設置的地區相適應的格式返回日期和時間。這大致意味着時間格式中會包含 AM 或 PM,但不會包含時區信息(當然,具體的格式會因瀏覽器而異)。而 toString()方法則通常返回帶有時區信息的日期和時間,其中時間一般以軍用時間(即小時的範圍是 0 到 23)表示

至於 Date 類型的 valueOf()方法,則根本不返回字符串,而是返回日期的毫秒錶示。因此,可以方便使用比較操作符(小於或大於)來比較日期值。

5.3.2 日期格式化方法

Date 類型還有一些專門用於將日期格式化爲字符串的方法,這些方法如下。

  • toDateString()——以特定於實現的格式顯示星期幾、月、日和年;
  • toTimeString()——以特定於實現的格式顯示時、分、秒和時區;
  • toLocaleDateString()——以特定於地區的格式顯示星期幾、月、日和年;
  • toLocaleTimeString()——以特定於實現的格式顯示時、分、秒;
  • toUTCString()——以特定於實現的格式完整的 UTC 日期。
    與 toLocaleString()和 toString()方法一樣,以上這些字符串格式方法的輸出也是因瀏覽器而異的

5.3.3 日期/時間組件方法

直接取得和設置日期值中特定部分的方法了。需要注意的是, UTC 日期指的是在沒有時區偏差的情況下(將日期轉換爲 GMT 時間)的日期值

getTime() 返回表示日期的毫秒數;與valueOf()方法返回的值相同
setTime(毫秒) 以毫秒數設置日期,會改變整個日期
getFullYear() 取得4位數的年份(如2007而非僅07getUTCFullYear() 返回UTC日期的4位數年份
setFullYear() 設置日期的年份。傳入的年份值必須是4位數字(如2007而非僅07setUTCFullYear() 設置UTC日期的年份。傳入的年份值必須是4位數字(如2007而非僅07getMonth() 返回日期中的月份,其中0表示一月, 11表示十二月
getUTCMonth() 返回UTC日期中的月份,其中0表示一月, 11表示十二月
setMonth() 設置日期的月份。傳入的月份值必須大於0,超過11則增加年份
setUTCMonth() 設置UTC日期的月份。傳入的月份值必須大於0,超過11則增加年份
getDate() 返回日期月份中的天數( 131getUTCDate() 返回UTC日期月份中的天數( 131setDate() 設置日期月份中的天數。如果傳入的值超過了該月中應有的天數,則增加月份
setUTCDate() 設置UTC日期月份中的天數。如果傳入的值超過了該月中應有的天數,則增加月份
getDay() 返回日期中星期的星期幾(其中0表示星期日, 6表示星期六)
getUTCDay() 返回UTC日期中星期的星期幾(其中0表示星期日, 6表示星期六)
getHours() 返回日期中的小時數( 023getUTCHours() 返回UTC日期中的小時數( 023setHours() 設置日期中的小時數。傳入的值超過了23則增加月份中的天數
setUTCHours() 設置UTC日期中的小時數。傳入的值超過了23則增加月份中的天數
getMinutes() 返回日期中的分鐘數( 059getUTCMinutes() 返回UTC日期中的分鐘數( 059setMinutes() 設置日期中的分鐘數。傳入的值超過59則增加小時數
setUTCMinutes() 設置UTC日期中的分鐘數。傳入的值超過59則增加小時數
getSeconds() 返回日期中的秒數( 059getUTCSeconds() 返回UTC日期中的秒數( 059setSeconds() 設置日期中的秒數。傳入的值超過了59會增加分鐘數
setUTCSeconds() 設置UTC日期中的秒數。傳入的值超過了59會增加分鐘數
getMilliseconds() 返回日期中的毫秒數
getUTCMilliseconds() 返回UTC日期中的毫秒數
setMilliseconds(毫秒) 設置日期中的毫秒數
setUTCMilliseconds(毫秒) 設置UTC日期中的毫秒數
getTimezoneOffset() 返回本地時間與UTC時間相差的分鐘數。例如,美國東部標準時間返回300。在某地進入夏令時的情況下,這個值會有所變化

5.4 RegExp類型

ECMAScript 通過 RegExp 類型來支持正則表達式。使用下面類似 Perl 的語法,就可以創建一個正則表達式。

var expression = / pattern / flags ;
  • g:表示全局( global)模式,即模式將被應用於所有字符串,而非在發現第一個匹配項時立即停止;
  • i:表示不區分大小寫( case-insensitive)模式,即在確定匹配項時忽略模式與字符串的大小寫;
  • m:表示多行( multiline)模式,即在到達一行文本末尾時還會繼續查找下一行中是否存在與模式匹配的項。
/*
* 匹配字符串中所有"at"的實例
*/
var pattern1 = /at/g;
/*
* 匹配第一個"bat"或"cat",不區分大小寫
*/
var pattern2 = /[bc]at/i;
/*
* 匹配所有以"at"結尾的 3 個字符的組合,不區分大小寫
*/
var pattern3 = /.at/gi;

模式中使用的所有元字符都必須轉義。正則表達式中的元字符包括:

( [ { \ ^ $ | ) ? * + .]}

這些元字符在正則表達式中都有一或多種特殊用途,因此如果想要匹配字符串中包含的這些字符,就必須對它們進行轉義。

/*
* 匹配第一個"bat"或"cat",不區分大小寫
*/
var pattern1 = /[bc]at/i;
/*
* 匹配第一個" [bc]at",不區分大小寫
*/
var pattern2 = /\[bc\]at/i;
/*
* 匹配所有以"at"結尾的 3 個字符的組合,不區分大小寫
*/
var pattern3 = /.at/gi;
/*
* 匹配所有".at",不區分大小寫
*/
var pattern4 = /\.at/gi;

前面舉的這些例子都是以字面量形式來定義的正則表達式。另一種創建正則表達式的方式是使用RegExp 構造函數,它接收兩個參數:一個是要匹配的字符串模式,另一個是可選的標誌字符串

/*
* 匹配第一個"bat"或"cat",不區分大小寫
*/
var pattern1 = /[bc]at/i;
/*
* 與 pattern1 相同,只不過是使用構造函數創建的
*/
var pattern2 = new RegExp("[bc]at", "i");

pattern1 和 pattern2 是兩個完全等價的正則表達式。要注意的是,傳遞給 RegExp 構造函數的兩個參數都是字符串(不能把正則表達式字面量傳遞給 RegExp 構造函數)。由於 RegExp 構造函數的模式參數是字符串,所以在某些情況下要對字符進行雙重轉義。所有元字符都必須雙重轉義,那些已經轉義過的字符也是如此

字面量模式 等價的字符串
/[bc]at/ “\[bc\]at”
/.at/ “\.at”
/name/age/ “name\/age”
/\d.\d{1,2}/ “\d.\d{1,2}”
/\w\hello\123/ “\w\\hello\\123”

使用正則表達式字面量和使用 RegExp 構造函數創建的正則表達式不一樣。在 ECMAScript 3 中,正則表達式字面量始終會共享同一個 RegExp 實例,而使用構造函數創建的每一個新 RegExp 實例都是新實例

var re = null,
    i;
for (i=0; i < 10; i++){
    re = /cat/g;
    re.test("catastrophe");
}
for (i=0; i < 10; i++){
    re = new RegExp("cat", "g");
    re.test("catastrophe");
}

5.4.1 RegExp實例屬性

RegExp 的每個實例都具有下列屬性,通過這些屬性可以取得有關模式的各種信息。

  • global:布爾值,表示是否設置了 g 標誌。
  • ignoreCase:布爾值,表示是否設置了 i 標誌。
  • lastIndex:整數,表示開始搜索下一個匹配項的字符位置,從 0 算起。
  • multiline:布爾值,表示是否設置了 m 標誌。
  • source:正則表達式的字符串表示,按照字面量形式而非傳入構造函數中的字符串模式返回。
var pattern1 = /\[bc\]at/i;
alert(pattern1.global); //false
alert(pattern1.ignoreCase); //true
alert(pattern1.multiline); //false
alert(pattern1.lastIndex); //0
alert(pattern1.source); //"\[bc\]at"
var pattern2 = new RegExp("\\[bc\\]at", "i");
alert(pattern2.global); //false
alert(pattern2.ignoreCase); //true
alert(pattern2.multiline); //false
alert(pattern2.lastIndex); //0
alert(pattern2.source); //"\[bc\]at"

第一個模式使用的是字面量,第二個模式使用了 RegExp 構造函數,但它們的source 屬性是相同的

5.4.2 RegExp 實例方法

exec()接受一個參數,即要應用模式的字符串,然後返回包含第一個匹配項信息的數組;或者在沒有匹配項的情況下返回 null。返回的數組雖然是 Array 的實例,但包含兩個額外的屬性: index 和 input。其中, index 表示匹配項在字符串中的位置,而 input 表示應用正則表達式的字符串。

var text = "mom and dad and baby";
var pattern = /mom( and dad( and baby)?)?/gi;
var matches = pattern.exec(text);
alert(matches.index); // 0
alert(matches.input); // "mom and dad and baby"
alert(matches[0]); // "mom and dad and baby"
alert(matches[1]); // " and dad and baby"
alert(matches[2]); // " and baby"

對於 exec()方法而言,即使在模式中設置了全局標誌( g),它每次也只會返回一個匹配項。在不設置全局標誌的情況下,在同一個字符串上多次調用 exec()將始終返回第一個匹配項的信息。而在設置全局標誌的情況下,每次調用 exec()則都會在字符串中繼續查找新匹配項

var text = "cat, bat, sat, fat";
var pattern1 = /.at/;

var matches = pattern1.exec(text);
alert(matches.index); //0
alert(matches[0]); //cat
alert(pattern1.lastIndex); //0

matches = pattern1.exec(text);
alert(matches.index); //0
alert(matches[0]); //cat
alert(pattern1.lastIndex); //0

var pattern2 = /.at/g;
var matches = pattern2.exec(text);
alert(matches.index); //0
alert(matches[0]); //cat
alert(pattern2.lastIndex); //3

matches = pattern2.exec(text);
alert(matches.index); //5
alert(matches[0]); //bat
alert(pattern2.lastIndex); //8

第一個模式 pattern1 不是全局模式,因此每次調用 exec()返回的都是第一個匹配項( “cat”)。而第二個模式 pattern2 是全局模式,因此每次調用 exec()都會返回字符串中的下一個匹配項,直至搜索到字符串末尾爲止。此外,還應該注意模式的 lastIndex 屬性的變化情況。在全局匹配模式下, lastIndex 的值在每次調用 exec()後都會增加,而在非全局模式下則始終保持不變
::: danger
IE 的 JavaScript 實現在 lastIndex 屬性上存在偏差,即使在非全局模式下,lastIndex 屬性每次也會變化。
:::

正則表達式的第二個方法是 test(),它接受一個字符串參數。在模式與該參數匹配的情況下返回true;否則,返回 false。在只想知道目標字符串與某個模式是否匹配,但不需要知道其文本內容的情況下,使用這個方法非常方便。

var text = "000-00-0000";
var pattern = /\d{3}-\d{2}-\d{4}/;
if (pattern.test(text)){
    alert("The pattern was matched.");
}

var pattern = new RegExp("\\[bc\\]at", "gi");
alert(pattern.toString()); // /\[bc\]at/gi
alert(pattern.toLocaleString()); // /\[bc\]at/gi

5.4.3RegExp構造函數屬性

RegExp 構造函數包含一些屬性(這些屬性在其他語言中被看成是靜態屬性)。這些屬性適用於作用域中的所有正則表達式,並且基於所執行的最近一次正則表達式操作而變化。

長屬性名 短屬性名 說 明
input $_ 最近一次要匹配的字符串。 Opera未實現此屬性
lastMatch $& 最近一次的匹配項。Opera未實現此屬性
lastParen $+ 最近一次匹配的捕獲組。 Opera未實現此屬性
leftContext $` input字符串中lastMatch之前的文本
multiline $* 布爾值,表示是否所有表達式都使用多行模式。 IE和Opera未實現此屬性
rightContext $’ Input字符串中lastMatch之後的文本

使用這些屬性可以從 exec()或 test()執行的操作中提取出更具體的信息

var text = "this has been a short summer";
var pattern = /(.)hort/g;
/*
* 注意: Opera 不支持 input、 lastMatch、 lastParen 和 multiline 屬性
* Internet Explorer 不支持 multiline 屬性
*/
if (pattern.test(text)){
    alert(RegExp.input); // this has been a short summer
    alert(RegExp.leftContext); // this has been a
    alert(RegExp.rightContext); // summer
    alert(RegExp.lastMatch); // short
    alert(RegExp.lastParen); // s
    alert(RegExp.multiline); // false
}

以上代碼創建了一個模式,匹配任何一個字符後跟 hort,而且把第一個字符放在了一個捕獲組中,RegExp 構造函數的各個屬性返回了下列值:

  • input 屬性返回了原始字符串;
  • leftContext 屬性返回了單詞 short 之前的字符串,而 rightContext 屬性則返回了 short之後的字符串;
  • lastMatch 屬性返回最近一次與整個正則表達式匹配的字符串,即 short;
  • lastParen 屬性返回最近一次匹配的捕獲組,即例子中的 s。
var text = "this has been a short summer";
var pattern = /(.)hort/g;
/*
* 注意: Opera 不支持 input、 lastMatch、 lastParen 和 multiline 屬性
* Internet Explorer 不支持 multiline 屬性
*/
if (pattern.test(text)){
    alert(RegExp.$_); // this has been a short summer
    alert(RegExp["$`"]); // this has been a
    alert(RegExp["$'"]); // summer
    alert(RegExp["$&"]); // short
    alert(RegExp["$+"]); // s
    alert(RegExp["$*"]); // false
}

5.2.4 模式的侷限性

ECMAScript 正則表達式不支持的特性 www.regular-expressions.info

  • 匹配字符串開始和結尾的\A 和\Z 錨①
  • 向後查找( lookbehind) ②
  • 並集和交集類
  • 原子組( atomic grouping)
  • Unicode 支持(單個字符除外,如\uFFFF)
  • 命名的捕獲組③
  • s( single,單行)和 x( free-spacing,無間隔)匹配模式
  • 條件匹配
  • 正則表達式註釋
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章