第5章:引用类型(中)

5.2.6 操作方法

ECMAScript为操作已经包含在数组中的项提供了很多方法。其中,concat()方法可以基于当前数组中的所有项创建一个数组。具体来说,这个方法会先创建当前数组一个副本,然后将接受到参数添加到这个副本的末尾,最后返回新构建的数组。在没有给concat()方法传递参数的情况下,它只是复制当前数组并返回副本。如果传递给concat()方法的是一个或者多个数组,则该方法会将这些数组中的每一项都添加到结果数组中。如果传递的值不是数组,这些值就会简单的加到数组的末尾。下面来看一个例子:

var colors = ["red","green","blue"];
var colors2 = colors.concat("yello",["black","brown"]);

alert(colors);  // red,green,blue
alert(colors2); // red,green,blue,black,brown

下一个方法是slice(),它能够基于当前数组中的一个或者多个项创建一个新数组。slice()方法可以接受一个 或者两个参数。即返回项的起始和结束位置。在只有 一个参数的情况下,slice()方法返回从该参数执行位置开始到当前数组末尾的所有项。如果有两个参数,该方法返回起始位置和结束位置之间的项–但不包括有结束位置的项。注意slice()方法不会影响到原始数组。请看下面的例子。

var colors = ["red","green","yellow","purple"];
var colors2 = colors.slice(1);
var colors3 = colors.slice(1,4);

alert(colors2); // green,blue,yellow,pruple
alert(colors3); // green,blue,green

这个例子中,开始定义的数组colors包含5项。调用slice()并传入1会得到一个包含4项的新数组;因为是从位置1开始复制,所以包含”green”而不会包含”red”。这个新数组colors2中包含的是”green”,”blue”,”yellow”和”purple”。接着,我们再次调用slice()并传入1和4,表示复制从位置1开始,到位置3结束。结果数组color3中包含了”green”,“blue”和”yellow”。

如果slice()方法的参数有一个负数,则用数组长度加上给数来确定相应的位置,例如,在一个包含5项的数组上调用slice(-2,-1)与调用slice(3,4)得到的结果相同。如果结束位置小于起始位置,则返回空数组。

下面介绍splice()方法,这个方法恐怕是最强大的数组方法了,它有很多方法。splice()的主要用途是向数组中插入项,但使用这种方法的方式则有如下3种:

  1. 删除:可以删除任意数量的项,只需要指定2个参数:要删除的第一项的位置和要删除的项数。例如:splice(0,2)会删除数组中的前两项。
  2. 插入:可以指定位置插入任意数量的项,只需要提供3个参数:起始位置、0(要删除的项数)和要插入的项,可以传入第四、第五,以至任意多个项。例如,splice(2,1,”red”,”green”)会从当前的数组位置2开始插入字符串”red”和”green”。
  3. 替换:可以向指定位置插入任意数量的项。且同时删除任意数量的项,只需要指定3个参数:起始位置、要删除的项数和要插入的任意数量的项。插入的项数不必与删除的项数相等。例如,splice(2,1,”red”,”green”)会删除当前位置2的项,然后在开始从位置2开始插入字符串”red”和”green”。

    splice()方法始终都会返回一个数组,给数组中包含从原始数组中删除的项(如果没有删除任何项,则返回一个空数组)。下面的代码展示了上述3中使用splice()方法的方式:

var colors = ["red","green","blue"];
var removed = colors.splice(0,1);   // 删除第一项
alert(colors);  // green,blue
alert(removed); // red

var removed = colors.splice(1,0,"yellow","orange"); //从位置1开始插入两项
alert(colors);  // green,yellow,orange,blue
alert(removed); // 返回一个空数组

removed = colors.splice(1,1,"red"."pruple");
alert(colors);  // green,red,purple,orange,blue
alert(removed); // yellow

上面的例子首先定义了一个包含3项的数组colors。第一次调用splice()方法只删除了这个数组的第一项。之后colors还包含“green”和”blue”两项。第二次调用splice()方法时在位置1插入了两项,结果colors中包含”green”、“yellow”、“orange”和”blue”。这一次操作没有删除项,因此返回了一个空数组。最后一次调用splice()方法直接删除了位置1处的一项,然后又插入了”red”和”purple”。在完成以上曹组之后,数组colors中包含的是”green”、”red”、”pruple”、”orange”和”blue”。

5.2.8 迭代方法

ECMAScript 5位数组定义了5个迭代的方法。每个方法都接受两个参数:要在每一项上运行的函数和(可选的)运行该函数的作用域对象–影响this的值。传入这些方法中的额函数会接受三个参数:数组项的值、该项在数组中的位置和数组对象本身。根据使用方法不同,这个函数执行后的的返回值可能会也可能不会影响方法的返回值。以下是这5个迭代方法的作用。

  1. every():对数组中的每一项运行给定函数,如果该函数对每一项都返回true,则返回true。
  2. filter():对数组中每一项运行给定函数,返回给函数会返回true的项组成的数组。
  3. forEach():对数组中的每一项给定函数,这个方法没有返回值。
  4. map():对数组中的每一项运行给定函数,返回每次函数调用的结果组成的数组。
  5. some():对数组中的每一项运行给定函数,如果该函数对任一项返回true,则返回true。

以上方法都不会修改数组中包含的值。
在这些方法中,最相似的是every()和some(),它们都用于查询数组中的项是否满足某个条件。对every()来说,传入的函数必须对每一项都返回true,这个方法才会返回true,就会返回true。请看下面的例子:

var numbers = [1,2,3,4,5,4,3,2,1];
var everyResult = number.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

以上代码调用了every()和some(),传入的函数只要给定项数大于2就会返回true。对于every(),它返回的是false,因为只有部分数组项符合条件。对于some(),结果就是true,因为至少有一项是大于2的。

下面再看filter()函数,它利用指定的函数确定是否返回的数组中包含某一项。例如,要返回一个所有值都大于2的数组,可以使用下面代码。

var numbers = [1,2,3,4,5,4,3,2,1];
var filterResult = numbers.filter(item,index,array){
    return (item > 2);
});
alert(filterResult);

这里通过filter()方法创建并且返回了包含3、4、5、4、3的数组,因为传入的函数对它们每一项都返回true。这个方法对查询某些条件的所有数组项非常有用。

map()也返回一个数组,而这个数组每一项都是在原始数组中的对应项上传入函数的结果。例如,可以给数组的每一项乘以2,然后返回这些乘积租场的数组,如下所示:

var numbers = [1,2,3,4,5,4,3,2,1];
var mapResult = numbers.map(function(item,indexmarray){
    return item * 2;
});
alert(mapResult);

以上代码返回的数组中包含每个数乘以2之后的结果。这个方法适合创建与另一个数组一一对应的数组。

最后一个方法是forEach(),它只是对数组中的每一项运行传入的函数。这个方法没有返回值,本质上与for循环迭代数组一样,来看一个例子:

var numnbers = [1,2,3,4,5,4,3,2,1];
numbers.forEach(function(item,index,array){
    // 执行某些操作
});

这些数组方法通过执行不同的操作,可以大大方便处理数组的任务,支持这些迭代方法的浏览器有IE9+,FireFox 2+,Safari 3+,Opera9.5和chrome。

5.2.9 归并方法

ECMAscript 5还新增加了两个归并数组的方法:reduce()和reduceRigth(),这两个方法都会迭代数组的所有项,然后构建一个最终返回的值。其中,reduce()方法从数组第一项开始,逐个遍历到最后。而reduceRight()方法则从最后一项开始,向前遍历到第一项。

这两个方法接受两个参数:一个在每一项上调用的函数和(可选的)最为递归并寄出的初始值。传给reduce()和reduceRight()的函数接受4个参数:前一个值、当前值、项的索引和数组对象。这个函数返回的任何值都会作为第一个参数自动传给下一项。第一次迭代发生在数组的第二项上,因此第一个参数是数组的第一项,第二个参数就是数组的第二项。

使用reduce()方法可以执行数组中所有值之和的操作,比如:

var numbers = [1,2,3,4,5];
var sum = numbers.reduceRight(function(prev,cur,index,array){
    return prev + cur;
});
alert(sum); // 15

在这个例子中,第一次执行回调函数,prev是5,cur是4。当然,最终结果相同,因为执行都是简单的加操作。

使用reduce()函数reduceRight(),主要取决于从那头开始遍历数组。除此之外,它们完全相同。

5.3 Date类型

ECMAScript 中的Date类型是在早期Java.util.Date类基础上构建的,Date使用UTC1970年1月1日午夜(零时)开始经过的毫秒数来保存日期。在使用这种数据存储格式的条件下,Date()类型保存的日期能够精确到1970年1月1日之前或者之后的285616年。

要创建一个日期对象,使用new操作符和Date构造函数即可,如下所示。

var now = new Date();

在调用Date构造函数而不传递参数的情况下,新创建的对象会自动获取当前日期和时间。如果想根据特定的时间和日期获取日期对象,必须传入表示该日期的毫秒数(即从UTC时间1970年1月1日午夜至该日期经过的毫秒数)。为了简化这一计算过程,ECMAScript提供两个方法:Date.parse()和Date.UTC()。

其中,Date.parse()方法接收一个表示日期的字符串参数,然后尝试根据这个字符串返回相应的毫秒数。ECMA-262没有定义Date.parse()应该支持那种日期格式,因此这个方法的行为因实现而异,而且通常是因地区而异。将地区设置为美国浏览器通常都接受下列日期格式:

  1. “月/日/年”,如6/13、2004;
  2. “英文月名 日,年”,如January 12,2004;
  3. “英文星期几 英文月名 日 年 时:分:秒 时区”,如Tue May 25 2004 00:00:00 GMT-0700。
  4. ISO 8601 扩展格式 YYYY-MM-DDTHH:mm:ss:ssZ(例如 2004-05-25T00:00:00)。只有兼容ECMAScript5的实现支持这中格式。

    例如,要为2004年5月25号创建一个日期对象,可以使用下面的代码:

var someDate = new Date(Date.parse("May 25,2004"));

如果传入Date.parse()方法的字符串不能表示日期,那么它会返回NaN。实际上,如果直接将表示日期的字符串传递给Date构造函数,也会在后台调用Date.parse()。换句话说,下面的代码与之前的例子是等价的:

var someDate = new Date("May 25,2004");

这行代码会得到与之前相同的日期对象。

日期对象一起在不同浏览器中的实现由许多奇怪的行为。其中有一种倾向于是将超出范围的值替换为当前的值,以便生成输出。例如,在解析”January 32,2007”时,有的浏览器会将其解释为”Febrary 1 2007”。而Opera则倾向于插入当前月份的当前日期。返回”January 当前日期,2007”。也就是说,如果在2007年9月21日运行当前的代码,将会得到”January 21,2007”。

Date.UTC()方法同样返回表示日期的毫秒数,但是它与Date.parse()在构建值的时候使用不同的信息。Date.UTC()的参数分别是年份、基于0的月份、月中那一天(1到31)、小时数(0到23)、分钟、秒以及毫秒。在这些参数中,只有前两个参数(年、月)是必须的。如果没有提供月中的条数,则假设天数为1;如果省略其他参数,则统统假设为0。以下是使用Date.UTC()方法的例子:

// GMT 时间 2000年1月1日 午夜零时
var y2k = new Date(Date.UTC(20000,0));

// GMT事件2005年5月5日下午5:55:55
var allFives = new Date(Date.UTC(2005,4,5,17,55,55));

这个例子创建了两个日期对象。第一个日期对象表示GMT事件是2000年1月1日午夜零时,传入的值一个是表示年份2000,一个是表示月份0(即1月份)。因为其它参数都是自动填充的(即月中的天数为1,其它所有参数均为0),所以结果就是该月第一天的午夜零时。第二个对象表示GMT时间2005年5月5日下午5:55:55,即使日期和事件只包含5,也需要传入不一样的参数:月份必须是4(因为月份是基于0的)、小时必须设置为17(因为小时是0-23表示的),剩下的参数就很直观了。

如同模仿Date.parse()一样,Date()构造函数也会模仿Date.UTC(),但是有一点不同的是:日期和事件都是基于本地时区而非GMT来创建。不过Date构造函数接受的参数仍然与Date.UTC()相同。因此,如果第一个参数是数值,Date构造函数就会假设该值是日期中的年份,而第二个参数就是月份,一次类推。据此,可以将前面的例子重写一下。

// 本地时间2000年1月1日午夜零时
var y2k = new Date(2000,0);

// 本地时间2005年5月5日下午5:55:5
var allFives = new Date(2005,4,5,17,55,55)

以上代码创建了与前面例子中相同的两个日期对象,只不过这次日期都是基于系统设置的本地时区创建的。

ECMAScript 5添加了Date.now()方法,返回表示调用这个方法的日期和时间的毫秒数。这个方法简化了Data对象分析代码的工作。

// 取得开始时间
var start = Date.now();
// 调用函数
doSometing();
// 取得停止时间
var stop = Date.now(),
result = stop - start;

支持Data.now()方法的浏览器包括IE9+、Firefox 3+ 、Opera 10.5和chrome。在不支持它的浏览器中,使用+操作符转换成字符串,也可以达到同样的目的。

// 取得开始时间
var start = +new Date();
// 调用函数
doSometing();
// 取得停止时间
var stop = +new Date(),
    result = stop - start;

5.3.1 继承方法

与其他应用类型一样,Date类型也重写了toLocaleString()、toString()和valueOf()方法;但是这些返回的值与其他类型中的方法不同.Date类型的toLocaleString()方法会按照与浏览器设置的时区相适应的格式返回日期和时间。这大致意味着时间格式中包括AM或PM,但是不会包含时区信息(当然具体的格式也会因为浏览器而异常)。而toString()方法则通过返回带有时区信息的日期和时间,其中时间以军用时间(即小时是0到23表示)。下面出现了在不同浏览器中调用toLocaleString()和toString()方法,输出PST时间2007年2月1日午夜零时的结果。

显然,这两个方法在不同浏览器中返回的日期和时间格式可谓大相径庭。事实上,toLocaleString()和toString()的这一差别仅在调试代码的时候比较有用,而在显示日期和时间没有什么价值。

只有Date类型的valueOf()方法,根本不返回字符串,而是返回日期的毫秒数。因此,可以方便的使用比较操作符(大于或者小于)来比较日期。请看下面的例子:

var date1 = new Date(2007,0,1); // "January 1,2007"
var date2 = new Date(2007,1,1); // "Febrary 1,2007"
alert(date1 < date2);   // true
alert(date1 < date2);   // false

从逻辑上将,2007年1月1日要早于2007年2月1日,此时我们说前者小于后者比较符合常理。而表示2007年1月1日的毫秒数小于2007年2月1日的毫秒数,因此在首先使用小于操作符比较日期时,返回的结果是true。这样,就为我们比较日期提供了很大的方便。

5.3.2 日期格式化方法

Date类型还有一些专门用于将日期格式化字符串的方法,这些方法如下:

  1. toDateString() – 以特定于实现的格式显示星期几、月、日和年;
  2. toTimeString() – 以特定于实现的格式显示星期几、月、日和年;
  3. toLocaleDateString() – 以特定于地区的格式显示星期几、月、日和年;
  4. toLocaleTimeString() – 以特定于实现格式显示时、分和秒;
  5. toUTCString() – 以特定于实现的格式完整的UTC日期

5.3.3 日期/时间组件方法

这里写图片描述

5.4 RegExp 类型

ES通过RegExp类型来支持正则表达式。使用下面类似Perl的语法,就可以创建一个正则表达式。

var expression = /pattern/flags;

其中的模式(pattern)部分可以是任何简单或者复杂的正则表达式,可以包含字符类、限定符。分组、向前查找以及反向引用。每个正则表达式可以带有一个或者多个标志(flags),用以表明正则表达式的行为。正则表达式的匹配模式支持3个标志。

  1. g:全局对象(global)模式,即模式将被应用于所有字符串,而非在发现第一个匹配项时立即停止;
  2. i:表示不区分大小写(case-insensitive)模式,即咱确定匹配项的时候忽略模式与字符串的大小写;
  3. m:表示多行(multiline)模式,即在到达一行文本结尾时还会基础查找下一行中是否存在于模式匹配的项。

    因此一个正则表达式就是一个模式与上述3个标志的组合体。不同组合产生不同结果,如下面的例子所示。

/**
* 匹配字符串中所有带有"at"的实例
*/
var pattern1 = /at/g;
/**
* 匹配第一个"bat"或者"cat",不区分大小写
*/
var pattern2 = /[bc]at/i;
/**
* 匹配所有以"at"结尾的三个字符的组合,不区分大小写
*/
var pattern3 = /.at/gi;

与其他语言的正则表达式类似,模式中使用的所有元字符都必须转义。正则表达式中的元字符包括:

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

这些元字符在正则表达式中都有一或者多种用途,因此如果想要匹配字符串中包含这些字符,就必须对它们进行转义。下面给出几个例子。

/**
* 匹配第一个"bat"或者"cat",不区分大小写
* /
var pattern1 = /[bc]at/i;

/**
* 匹配第一个"[bc]at",不区分大小写
* /
var pattern2 = /\[bc\]at/i;
/**
* 匹配所有以"at"结尾的3个字符的组合,不区分大小写
*/
var pattern3 = /.at/gi;

与其他语言的正则表达式类似,模式中使用的所有元字符都必须转义。正则表达式中的元字符包括:

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

这些元字符在正则表达式中都有一种或者多种特殊用途,因此如果想要匹配字符串中这些字符,就必须对它们进行转义。下面给出几个例子:

// 匹配第一个"bat"或者"cat",不区分大小写
var pattern1 = /[bc]at/i;

// 与pattern1相同,不过是使用构造函数创建的
var pattern2 = new RegExp("[bc]at","i");

在此,pattern1和pattern2是完成等价的正则表达式。需要注意的是,传递给RegExp构造函数的两个参数都是字符串(不能把正则表达式字面量传递给RegExp构造函数)。由于RegExp构造函数的模式参数是字符串,所以在某些情况下要对字符进行双重转义。所有元字符都必须双重转义,那些已经转义过的字符也是如此。例如\n(字符\在字符串中通常被转义为\,而在正则表达式字符串中就会变成\\)。下表给出了一下模式的字面量形式,右边使用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构造函数创建的正则表达式不一样。在ECMAScript3中,正则表达式字面量始终会共享同一个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");
}

在第一个循环中,即使是循环体中指定的,但是实际上只为/cat/创建了一个RegExp实例。由于实例属性不会重置,所以在循环中再次调用test()找到了”cat”,但是第二次调用时从索引为3的字符(上一次匹配的末尾)开始的。所以就找不到它了。由于会检测到字符串末尾,所以下一次调用test()又从头开始了。

第二个循环使用RegExp构造函数在每次循环中创建正则表达式。因为每次迭代都会创建一个新的RegExp实例,所以每次调用test()都会返回true。

ECMAScript 5明确规定,使用正则表达式字面量必须像直接调用RegExp构造函数一样,每次都创建新的RegExp实例。

5.4.1 RegExp实例

RegExp的每个实例都具有下列属性,通过这些属性可以取得有关模式的各种信息。

  1. global:布尔值,表示是否设置了g标志。
  2. ignoreCase:布尔值,表示是否设置了i。
  3. lastIndex:整数,表示开始搜索下一个匹配项的字符位置,从0算起。
  4. multiline:布尔值,表示是否设置了m标志。
  5. 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属性是相同的。可见,source属性保存的是规范形式的字符串,即字面量形式所用的字符串。

5.4.2 RegExp实例方法

RegExp对象的主要方法是exec(),该方法是专门为捕获组而设计的。exec()接收一个参数,即要应用模式的字符串,然后返回包含第一个匹配项信息的数组;或者在没有匹配项的情况下返回null。返回的数组虽然是Array的实例,但是包含两个额外的属性:index和input。其中index表示匹配项在字符串中的位置,而input表示应用正则表达式的字符串。在数组中,第一项与整个模式匹配的字符串,其中其他项与模式中捕获组匹配的字符串(如果模式中没有捕获组,则该数组只包含一项)。请看下面的例子:

var text = "mom and dad bady";
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 bady"

这个例子中的模式包含两个捕获组。最内部的捕获组匹配”and baby”,而包含它的捕获组匹配”and dad”或者”and dad and baby”。当把字符串传入exec()方法之后,发现了一个匹配项。因为整个字符串本身与模式匹配,所以他返回的数组matchs的index属性为0。数组中的第一项是匹配整个字符串,第二项包含一个与第一个匹配组匹配的内容,第三项包含于第二个捕获组匹配内容。

对于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

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

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

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

这个例子中的第一个模式pattern1不是全局模式,因此每次调用exec()返回的都是第一个匹配的项目(“cat”)。而第二个模式pattern2是全局模式,因此每次调用exec()都会返回字符串的下一个匹配项,直至搜索到字符串末尾为止,此外,还应该注意到模式的lastIndex属性的变化情况。在全局匹配模式下,lastIndex的值在每次调用exec()后都会增加,而在非全局模式下则始终保持不变。

IE的JavaScript实现在lastIndex属性上存在偏差,即使在非全局模式下,lastIndex属性每次也会变化。

正则表达式的第二个方法是test(),它只接收一个字符串参数。在模式与给参数匹配的情况下返回true;否则,返回false。在只想知道目标字符串与某个模式是否匹配,但不需要知道其文本内容的情况下,使用这个非常方便。因为test()方法经常被用在if语句中,如下面的例子中:

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

在这个例子中,我们使用了真个表达式来检测一个数字序列。如果输入的文本与模式匹配,则显示一条消息。这种用法经常出现在验证用户输入的情况下,因为我们只想知道输入是不是有效,至于它为什么无效就无关紧要了。

RegExp实例继承的toLocaleString()和toString()方法都会返回正则表达式的字面量,与创建正则表达式的方式无关。例如:

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

即使上例中的模式是通过RegExp构造函数创建的,但是toLocaleString()和toString()方法仍然会像他是以字面量形式创建的一样显示其字符串表示。

正则表达式的valueOf()方法返回正则表达式本身。

5.4.3 RegExp构造函数属性

RegExp构造函数包含一些属性(这些属性在其他语言中被看做成是静态属性)。这些属性适用于作用域中的所有正则表达式,并且基于所执行的最近一次正则表达式操作而变化。关于这些属性的另一个独特之处,就是可以铜鼓两种方式访问它们。换句话说,这些属性分别有一个长属性名和一个短属性名(Opera是例外,它不支持短属性名)。下表列出RegExp构造函数属性。

长属性名短属性名说明
input$_最近一次要匹配的属性。Opera未实现此属性
lastMatch$&最近一次匹配项。Opera未实现此属性
lastParen$+最后一次匹配的捕获组。Opera未实现此属性
leftContent`$“Input字符串中lastMatch之前的版本
multiline$*布尔值,表示是否所有表达式都使用多行模式。IE和Opera未实现此属性
rightContent$'Input字符串中lastMatch之后的文本

使用这些属性可以从exec()或者test()执行的操作中提取出更加具体的信息,请看下面的例子:

var text = "this has been a short summer";
var pattern = /(.)hort/g;

/**
 * 注意:Opera不支持input、lastMatch、lastMatch、lastParen和multiline属性
 *  IE不支持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:属性返回的原始字符串;
  • leftContent属性返回单词short之前的字符串,而rightContext属性则返回了short之后的字符串;
  • lastMatch属性返回最近一次与整个正则表达式匹配的字符串,即short;
  • lastParen属性返回最近一次匹配的捕获组,即例子中的s。

    如前面所述,例子使用长属性名可以用短属性名来代替。只不过,由于短属性名大都不是有效的ECMAScript标识符,因此必须通过方括号来访问它们,如下所示:

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
}

除了上面介绍的几个属性之外,还有多达9个用于存储捕获的构造函数属性。访问这些属性的语法是RegExp.$1、RegExp.$2RegExp.$9 ,分别也是存储在第一、第二…第九个匹配的捕获组。在调用exec()或者test()方法时,这些属性会被自动填充。然后,我们就可以像下面这样来使用它们。

var test = "this has been a short summer";
var pattern = /(...)or(.)/g;
if(pattern.test(text)){
    alert(RegExp.$1);     // sh
    alert(RegExp.$2);     // t
}

这里创建了一个包含两个捕获组的模式,并用该模式测了一个字符串。即使test()方法返回一个布尔值,但RegExp构造函数的属性$1$2 也会被匹配相应捕获组的字符串自动填充。

5.4.4 模式的局限性

尽管JavaScript中的正则表达式功能还是比较完备的,但是仍然缺少某些语言(特别是Perl)所支持的高级正则表达式特性。下面列出ECMAScript正则表达式不支持的特性。

  • 匹配字符串开始和结尾的\A和\Z锚
  • 向后查找
  • 并集和交集
  • 原子组
  • Unicode支持
  • 命名的捕获组
  • s和x匹配模式
  • 天剑匹配
  • 正则表达式注释
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值