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,无间隔)匹配模式
  • 条件匹配
  • 正则表达式注释
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值