第5章 原生对象

本章主要内容

● Object对象

● Math对象

● 字符串对象

● 数组对象

● 日期对象

● 正则

● Set数据结构

● Map数据结构

对象分为内置对象(如Math)、本地对象(如String、Array)和宿主对象(BOM和DOM),本章将对这些内容进行详细介绍。

5.1 Object对象

Object是JavaScript 中常用的一个数据类型,并且在JavaScript 中所有的对象都是继承自 Object 对象。在对象一章中只是简单地用Object 结合new 构造函数来创建一个对象,并没有过多地使用。但是Object对象其实包含了很多有用的属性和方法。因此,本节将从最基本的开始详细介绍Object对象常用的方法以及应用。

5.1.1 Object的常用方法

1.Object.create()

在ECMAScript 5 中定义了名为Object.creat()的方法,该方法可以创建一个拥有指定原型和若干个指定属性的对象。其中第一个参数是要继承的原型,如果不是一个子函数,则可以传入一个null,第二个参数是对象的属性描述符,这个参数是可选的。

Object.create()是一个静态函数,而不是提供给某个对象调用的方法,只需传入所需的原型对象和属性描述即可。

在JavaScript中,属性有两种类型,分别是数据属性和访问器属性。

(1)数据属性

数据属性可以理解为平时定义对象时赋予的属性,它可以进行读和写。但是,ES5中定义了一些特性,这些特性用来描述属性的各种特征,特性是内部值,不能直接访问到。属性的特性会有一些默认值,要修改特性的默认值,必须使用ES5定义的新方法(Object.defineProperty方法)来修改。下面具体介绍每个特性。

1)writable:表示属性值是否可修改,默认为true。

2)value:表示属性的值,默认为undefined。

示例:

说明:上述示例中,通过value 这个特性,可以设定对象的默认值,且当writable 属性设置为false时,对象的属性值是不可以修改的。

3)configurable:该特性表示是否能够通过delete操作符删除属性,默认值为true。

示例:

说明:在上述示例中,创建一个对象obj,并且给它添加属性name,通过delete操作符删除name属性后,就访问不到name属性了。

示例:

说明:在上述示例中,创建了一个拥有指定原型 obj 的newObj 对象,并且设置configurable特性为false,最后发现依然能访问到name属性。

4)enumerable:表示是否能用for in 枚举出属性,默认值为true。

示例:

说明:在上述示例中,可以通过for in 语句枚举obj对象的属性。

示例:

说明:在上述示例中创建了一个拥有指定原型obj的newObj对象,并且设置enumerable特性为false,发现通过for in 语句不能枚举newObj对象的属性,但当设置为true时则可以。

(2)访问属性

1)get():表示访问。

2)set():表示设置。

示例:

说明:在上述示例中通过方法Object.creat()创建了一个拥有指定原型 obj 的对象newObj,通过设置不同的属性描述符,可以实现不同的操作,得到不同的结果。

2.Object.is()

它用来比较两个值是否严格相等,与严格比较运算符(===)的行为基本一致,不同之处只有两个:一是+0不等于-0,二是NaN等于自身。

示例:

3.Object.assign()

Object.assign 方法用于对象的合并,将原、源对象(source)的所有可枚举属性复制到目标对象(target)上。

Object.assign方法的第一个参数是目标对象,后面的参数都是源对象。

示例:

如果目标对象与源对象具有同名属性,或多个源对象具有同名属性,则后面的属性会覆盖前面的属性。

示例:

如果只有一个参数,Object.assign方法会直接返回该参数。

示例:

如果该参数不是对象,则会先转成对象,然后返回。

示例:

由于undefined和null无法转成对象,因此如果它们作为参数,就会报错。

示例:

Object.assign方法执行的是浅复制,而不是深复制。

示例:

4.Object.getOwnPropertyDescriptors()

此方法用于返回某个对象属性的描述对象。

示例:

5.1.2 属性的遍历(Object对象方法的使用)

1. 属性的可枚举性

对象的每个属性都有一个描述对象(Descriptor)用来控制该属性的行为。Object.getOwnPropertyDescriptor方法可以获取该属性的描述对象。

描述对象的enumerable 属性,称为“可枚举性”,如果该属性为false,则表示某些操作会忽略当前属性。

1)for...in循环:只遍历对象自身的和继承的可枚举的属性。

2)Object.keys():返回对象自身的所有可枚举的属性的键名。

3)JSON.stringify():只串行化对象自身的可枚举的属性。

4)Object.assign():只复制对象自身的可枚举的属性。

2. 遍历的方式

ES6中一共有5种方法可以遍历对象的属性。

(1)for...in

for...in循环遍历对象自身的和继承的可枚举属性(不含Symbol属性)。

(2)Object.keys(obj)

Object.keys 返回一个数组,包括对象自身的(不含继承的)所有可枚举属性(不含Symbol属性)。

(3)Object.getOwnPropertyNames(obj)

Object.getOwnPropertyNames返回一个数组,包含对象自身的所有属性(不含Symbol属性,但是包括不可枚举属性)。

(4)Object.getOwnPropertySymbols(obj)

Object.getOwnPropertySymbols返回一个数组,包含对象自身的所有Symbol属性。

(5)Reflect.ownKeys(obj)

Reflect.ownKeys 返回一个数组,包含对象自身的所有属性,不管是属性名或Symbol 或字符串,也不管是否可枚举。

通过以上5种方法遍历对象的属性,都遵守同样的属性遍历的次序规则,具体如下:

1)首先遍历所有属性名为数值的属性,按照数字排序。

2)其次遍历所有属性名为字符串的属性,按照生成时间排序。

3)最后遍历所有属性名为Symbol值的属性,按照生成时间排序。

5.2 Math对象

JavaScript 自身有很多内置的对象,Math 就是其中之一,但是相比较其他的内置对象,Math对象可以用于执行数学任务,如获取一个随机数、画一个圆等。

Math是JavaScript本来就存在的对象,即内置对象,它具有数学常数和函数的属性与方法。Math 对象不像Date 和String 是对象的类,因此没有构造函数,也不需要实例化,它所有的属性和方法都是静态的,只需把Math作为对象使用即可。

5.2.1 Math对象的属性

1)Math.PI:圆周率。

2)Math.E:返回欧拉常数e的值。

3)Math.LN2:返回2的自然数对数。

5.2.2 Math对象的方法

Math对象的方法具体见表5-1。

表5-1

(1)abs()

abs()用于返回一个数的绝对值。

示例:

(2)round()、floor()、ceil()和trunc()

1)round()用于对一个数进行四舍五入。

2)floor()用于对一个数进行向下取整。

3)ceil()用于对一个数进行向上取整。

4)trunc()用于返回一个数的整数部分,去除小数。

示例:

如果参数是一个正数,则trunc()相当于floor();否则trunc() 相当于ceil()。

(3)max()和min()

max()和min()用于取一组数中的最大值和最小值。

示例:

(4)random()

random()用于取一个随机数。

示例:

(5)pow(x,y)和sqrt()

pow(x,y)用于取x的y次幂,sqrt()用于获取一个数的平方根。

示例:

(6)sin()、cos()、tan()、asin()、acos()和atan()

使用时,需要将角度转化为弧度才能进行计算,在JavaScript中,用Math.PI表示π,转换公式为1deg=Math.PI/180。

示例:

5.3 字符串对象

String 对象也是JavaScript 内置对象之一。当操作的数据是文本形式的数据时,用字符串对象处理再合适不过了。下面介绍有关字符串对象的创建,以及其具有的属性和方法。

5.3.1 创建String对象

创建String对象的语法格式如下:

通过构造函数会返回一个新创建的String对象,用来存放字符串s:

不通过构造函数调用String()时,它只把s转换成原始的字符串,并返回转换后的值。

5.3.2 字符串对象的属性

1)constructor:用于返回构造函数的引用。

2)length:用于计算字符串的长度。

示例:

说明:在上述示例中,通过调用String对象的length属性,可以很方便地获得该字符串的长度。

5.3.3 字符串对象的方法

字符串对象的方法可以大致分为获取、查找、截取、转换4种类型。接下来将一一进行介绍。

获取类型有以下3种:

(1)mystr.charAt(index);

charAt方法用来返回指定索引位置(index)的字符串,若是超出有效范围的索引值,将返回空字符串。

示例:

说明:需要注意index下标的有效范围是从0开始到str.length-1。

(2)mystr.charCodeAt(index);

charCodeAt方法返回指定位置字符的Unicode编码。

示例:

(3)String.fromCharCode(code1 [,code2...]);

fromCharCode是一个静态方法,它存储在内存的静态区中,一直存在,直接使用即可。

fromCharCode方法接收一个(或多个)指定的Unicode 值,然后返回一个(或多个)对应的字符串。

示例:

查找类型有以下5种:

(1)mystr.indexOf(str [,startIndex]);

indexOf方法返回指定字符在字符串中第一次出现的位置。如果字符不存在,则返回-1。

示例:

说明:当指定startIndex 的值时,会从指定位置开始起查找指定的字符串,否则从字符串的开始位置查找。

(2)mystr.lastIndexOf(str [,startIndex]);

lastIndexOf 方法返回指定字符在字符串中最后一次出现的位置。如果字符串不存在,则返回-1。

示例:

说明:若指定了startIndex的值,则作用与indexOf方法一样。

(3)mystr.match(str);

match 方法用于在字符串内检测指定的值,或查找一个或多个正则表达式的匹配,该方法类似于indexOf和lastIndexOf方法,但是返回指定的值,没有则返回null,而不是返回字符串的位置。

示例:

(4)mystr.search(reExp);

search方法返回与正则表达式查找内容相匹配的第一个字符串的位置,只能用于正则。

示例:

说明:reExp包含正则表达式模式和可用标志的正则表达式对象。

(5)mystr.replace(str/regExp,replacement);

replace方法用于将字符串中的一些字符替换成另外的字符,或替换一个与正则表达式匹配的字符串。返回结果为新字符串,不影响原字符串。

示例:

截取类型有以下3种:

(1)mystr.slice(start [,end]);

slice方法返回从指定位置开始截取,到指定位置结束(不包括)的字符串;start下标从0开始;如果没有指定结束位置,则从指定位置开始截取,到末尾结束;它可以接收负值,-1表示字符串的结尾。

示例:

(2)mystr.substring(start,end);

substring 方法返回从指定位置开始截取,到指定位置结束的字符串(不包括);如果没有指定结束位置,则从指定位置开始截取,到末尾结束;它接收到的负值会自动转换为0。

示例:

(3)mystr.substr(start [,length]);

substr方法返回从指定位置开始截取指定长度的字符串,如果没有指定长度,则从指定位置截取,到末尾结束;不支持负数。

示例:

说明:在上述示例中,当指定length字符个数时,会从指定的位置开始输出一定个数的字符。

转换类型有以下7种:

(1)mystr.split("以什么分割"[,limit]);

split方法将一个字符串分割成数组,limit用来限制返回数组中元素的个数。

示例:

(2)toLowerCase();

toLowerCase方法用于把字符串中的字母转换为小写,并返回一个字符串。

示例:

(3)toUpperCase();

toUpperCase方法用于将字符串中的字母转换为大写,并返回一个字符串。

示例:

(4)trim();

trim方法用于删除字符串两边的空格。

示例:

(5)charCodeAt();与codePointAt();

在JavaScript 内部,字符以UTF-16 的格式储存,每个字符固定为两个字节。对于那些需要4 个字节储存的字符(Unicode 码点大于0xFFFF 的字符),JavaScript 会认为它们是两个字符。

示例:

说明:上述示例中,汉字“吉”不是吉利的“吉”,是“吉”的异体字,码点为0x20BB7。需要4 个字节的字符存储。通过方法charCodeAt()分别返回了前两个字节和后两个字节的值。

ES6提供了codePointAt方法,能够正确处理4个字节储存的字符,返回一个字符的码点。

上述示例中,codePointAt方法正确返回了汉字“吉”的码点。

因此,对于32位的UTF-16字符的码点,通过codePointAt方法能够正确返回。对于常规的两个字节存储的字符,codePointAt方法和charCodeAt方法相同。

(6)String.fromCharCode();与String.fromCodePoint();

ES6提供了String.fromCodePoint方法,它与ES5中的String.fromCharCode方法一样,用于从码点返回对应的字符。不同之处在于,String.fromCodePoint 方法可以识别 0xFFFF 的字符,弥补了String.fromCharCode方法的不足。在作用上,正好与codePointAt方法相反。

示例:

示例输出结果如图5-1所示。

图5-1

如果String.fromCodePoint方法有多个参数,则它们会被合并成一个字符串返回。

(7)repeat();

repeat方法返回一个新字符串,表示将原字符串重复n次。

示例:

5.4 数组对象

Array 数组对象也是JavaScript 的内置对象之一,常用于在单个变量中存储多个值,且允许数组中含有不同数据类型的元素,即数组元素可以是对象或其他的数组。

5.4.1 数组对象的属性

1)length:返回或设置数组元素的个数。因为数组的索引总是从0开始,所以一个数组的下标范围是从0到length-1。再次注意,JavaScript数组的length属性是可变的。

示例:

2)prototype属性:返回对象类型原型的引用。

示例:

3)constructor属性:表示创建对象的函数。

示例:

5.4.2 数组对象的方法

在数组中有很多预定义的方法,接下来按照分类一一进行介绍。

添加或删除类有以下5种:

(1)myarr.push(元素1,元素2,…);

push方法用来向数组的末尾添加元素,返回值为新数组的长度;一次可以添加多个元素。

示例:

说明:通过push 方法返回的是新数组的长度,当再次访问数组时返回的是添加后的结果,说明push方法会影响原数组。

(2)myarr.unshift(元素1,元素2,…);

unshift 方法用来向数组的开头添加元素,返回值为新数组的长度;一次可以添加多个元素。

示例:

说明:在上述示例中,返回一个新的数组,说明unshift方法也会影响原数组。

(3)myarr.pop();

pop方法用来删除数组中的最后一个元素,返回值为删除的元素。

示例:

说明:在上述示例中,返回来一个新的数组,说明pop方法也会影响原数组。

(4)myarr.shift();

shift方法用来删除数组的第一个元素,返回值为删除的元素。

示例:

说明:在上述示例中,说明shift方法也会影响原数组。

(5)myarr.splice(index,数量,item1,item2...);

splice方法用来删除、添加或替换数组中的元素,是个“万能”的方法。

index:表示从哪里开始删除或添加,必须是数值类型的(从0开始计算)。

数量:规定了删除的个数,如果为0,则表示不删除,否则表示添加。

示例:

说明:当用splice 方法删除或替换元素时,会返回被删除的元素;当用splice 方法插入元素时,会返回一个空数组。

数组的转换方法如下:

join 方法用于将数组按照指定的分隔符转换为字符串,如果没有分隔符,则默认为以“,”来分隔。返回值为组成的字符串。

示例:

说明:在上述示例中,使用了不同的分隔符。

数组的截取方法如下:

slice 方法从已有的数组中返回选定的元素,即从指定位置开始截取,到指定位置(不包括)结束。如果没有指定结束位置,则从指定位置开始,到末尾结束。支持负数(从-1 开始),返回值为新数组,不会破坏原数组。

示例:

说明:在上述示例中,当操作后再次返回数组arr时,还是原数组,表明slice方法不会影响原数组。

数组的连接方法如下:

concat方法用于连接两个或更多个数组,并返回新数组。该方法不会影响原数组。

示例:

数组的排序方法如下:

sort方法用于对数组中的元素进行排序。排序顺序可以按照字母顺序或数字顺序,并按升序或降序排列。如果没有参数,则从字符的编码开始按照顺序排列。

如果有参数,则这个参数必须是一个函数(回调函数)。这个回调函数有两个参数,即a、b。

示例:

说明:在上述示例中,sort 排序默认为升序。当元素为字符串时,会比较每个元素对应的Unicode编码大小并进行排序。

示例:

说明:在上述示例中,通过回调函数来设置数组排序的规则是升序还是降序。

数组的过滤方法如下:

filter方法会创建一个新数组,新数组中的元素为通过检查指定数组中符合条件的所有元素。在回调函数中定义判断条件,返回值为所有判断结果为真的值组成的新数组。

示例:

数组的映射方法如下:

map 方法会返回一个新数组,数组中的元素为原始数组元素调用函数处理后的值,即在回调函数中返回新值。

示例:

说明:在上述示例中,在回调函数中返回了新值,map 方法的返回值为回调函数中返回的新值组成的新数组。

数组的查找有以下两种方法:

(1)indexOf(item)

indexOf 用来返回数组中某个指定的字符串值第一次出现的位置,从指定位置起从前向后搜素。若没有找到,则返回-1。

如果没有指定起始位置,将从字符串的开始位置查找。

indexOf 方法最基本的思想还是先循环整个数组,将每次循环出来的结果与要查找的元素进行匹配,最终返回我们想要的结果。接下来先从for循环入手,实现元素的查找。

示例:

万变不离其宗,indexOf方法还是基于for循环的思想,只不过使用起来更加方便。下面再用indexOf方法实现上个示例。

示例:

说明:在上述示例中,indexOf方法实现的原理机制和for循环机制一样。

(2)lastIndexOf(item)

lastIndexOf 方法返回数组中某个指定的字符串值最后一次出现的位置,和indexOf 方法正好相反。

示例:

说明:在上述示例中,通过lastIndexOf方法从头到尾地检索,看数组中是否包含1,如果找到,则返回1在数组中最后一次出现的位置。

数组的判断有以下两种方法:

(1)every()

every 方法用来检测数组中的每一项元素是否符合条件,即在回调函数中进行判断,如果有一个元素不满足条件,则整个表达式返回false,且其他元素将不再进行判断;如果所有元素都满足条件,则返回true。注意,every方法不会对空数组进行检测。

既然 every 方法是检测数组中的每一个元素,那么此处还是先用for 循环来切入,方便大家理解记忆。

示例:

接下来再用every方法实现一下:

说明:通过以上两个示例可以发现,every方法的实现原理还是for循环。

(2)some(function(item,index){})

some 方法用于检测数组中的每一个元素是否满足指定的条件,即在回调函数中对每个元素依次进行判断,如果有一个元素满足条件,则表达式将返回true,且其他元素将不再进行判断;如果没有满足条件的元素,则返回false。注意,some 方法不会对空数组进行检测。

同理,此处读者也可以尝试用for循环先实现一下。

用some方法实现,示例如下:

说明:在上述示例中,通过some 方法可以检测数组中是否有元素小于3,并不会改变原始数组。

数组的转换有以下两种方法:

(1)Array.from()

Array.from 方法用于将两个类对象转换为真正的数组,即类似数组的对象(array-like object)和可遍历(iterable)的对象。

示例:

Array.from方法可以处理NodeList对象和arguments对象。

示例:

(2)Array.of()

Array.of方法用于将一组值转换为数组。

示例:

这个方法的主要目的是弥补数组构造函数Array()的不足。

示例:

5.4.3 数组对象的构造函数的方法

1)Array.from 方法用于将两类对象转换为真正的数组,即类似数组的对象和可遍历的对象。

示例:

2)Array.of 方法用于将一组值转换为数组。Array.of 方法总是返回参数值组成的数组。如果没有参数,则返回一个空数组。

示例:

3)Array.isArray方法用于判断某个值是否为数组,返回的值为布尔类型。

示例:

5.5 日期对象

现如今,无论是网页中还是一个游戏中,都会涉及对时间的处理,JavaScript 专门提供了日期对象。

Date对象是JavaScript提供的日期和时间的接口操作,在JavaScript中DATE使用UTC(国际协调时间),它能够表示的时间范围是1970年1月1日00:00:00前后的各一亿天,之前为负,之后为正,取值范围为285616年。

日期也是JavaScript 的一个内置对象,若想对日期进行获取和操作,就必须实例化对象。

5.5.1 定义日期对象

使用关键字new来定义一个Date对象,语法格式如下:

说明:日期对象会自动获取当前的日期和时间作为初始值,包括年、月、日、时、分、秒、星期、时区。

当传入参数时,即得到时间参数。可传入的参数格式如下:

"时:分:秒 月/日/年"或"月/日/年时:分:秒"或字符串。

年,月,日,时,分,秒,不能加""。

不传参会得到当前时间的信息。

Date对象可以作为普通函数直接调用,返回一个代表当前时间的字符串。

示例:

5.5.2 获取日期信息的方法

一般在项目中会获取一下时间相关的信息,表5-2所示是常用的获取日期信息的方法。

表5-2

(续)

5.5.3 设置日期的方法

设置日期的方法见表5-3。

表5-3

表5-2 和表5-3 列出了常用的操作日期的方法,通过Date 对象可以很方便地对日期进行操作,下面通过示例深入理解日期对象的用法。

在下面的示例中,通过setFullYear方法设置一个日期对象为指定的日期。

示例:

日期是可以进行比较的,下面获取当天的日期,与之前设置的日期进行比较。

示例:

下面对于日期对象的获取方法来展示一个示例:

对于日期对象的应用,在后续章节中将通过一些示例来更好地阐述。

5.6 正则

本节将重点介绍正则表达式,简单地说,正则表达式就是一个文本模式的匹配工具,在JavaScript 中,正则表达式一般被用来匹配字符串中的字符组合。但是为什么非得用正则表达式呢?下面通过一个示例来了解正则表达式在实际项目中的应用。

示例:

从上述示例中可知,如果不使用正则表达式,判断会比较麻烦,使用正则表达式则简单很多。

正则表达式虽然用起来比较简单,但是它的规则很多。

5.6.1 正则表达式的概念

正则表达式是一个描述字符模式的对象。简单地说,是一个用来描述或者匹配一系列符合某个语法规则的字符串的语言。在很多文本编辑器或其他工具里,正则表达式通常被用来检索、替换或拆分那些符合某个模式的文本内容。许多程序设计语言都支持利用正则表达式进行字符串操作。

5.6.2 应用场合

在实际项目开发中有很多场合都需要使用正则表达式,那么哪些场合下会用到正则呢?

1)数据验证。

2)文本替换。

3)内容检索。

4)过滤内容。执行字符串函数无法完成的特殊的匹配、拆分、替换功能等。

5.6.3 创建正则表达式

在JavaScript中,正则表达式是通过对象的方式来创建的,它有自己的方法。

1. 通过构造函数创建

通过构造函数创建表达式又称为显示的创建,即通过构造函数RegExp()来实现。语法格式如下:

构造函数RegExp()有两个参数要传,第一个参数为正则表达式,第二个参数是一个可选项,即模式修饰符。各个修饰符的含义如下:

1)g表示全局匹配,即匹配字符串中出现的所有模式。

2)i表示忽略字母大小写,即在匹配字符串时不区分字母大小写。

3)m表示进行多行匹配。

如下示例中都是有效的正则表达式。

通常将正则表达式字符串放在/RegExp/中间,//称为定界符。

匹配边界有以下两种情况:

(1)字符边界

1)^ 匹配字符串的开始。

2)$ 匹配字符串的结束,忽略换行符。

(2)单词边界限制

1)\b 匹配单词的边界。

2)\B 匹配除单词边界以外的部分。

2. 通过字面量方式创建

通过字面量方式创建又称为隐式创建,即将文字量的正则表达式赋值给一个变量。语法格式如下:

其中,正则表达式为指定的匹配模式,模式修饰符是可选项。

5.6.4 正则表达式的模式

1. 原子

原子是正则表达式中最小的元素,包括英文、标点符号等。每个原子都有自己特殊的含义,具体如下:

\d——匹配任意一个数字。

\D——与除了数字以外的任何一个字符匹配。

\w——与任意一个英文字母,数字或下画线匹配。

\W——除了字母,数字、下画线外,与任何一个字符匹配。

\s——与任意一个空白字符匹配。

\n——换行字符。

\f——换页字符。

\r——回车字符。

\t——制表符。

\v——垂直制表符。

\S——与除了空白符以外任意一个字符匹配。

2. 原子表

方括号可用于查找某个范围内的字符,具体如下:

[]——只匹配其中的一个原子。

[^]——匹配除了当前原子表中定义的原子外的字符。

[0-9]——匹配0~9中的任意一个数字。

[a-z]——匹配小写a~z中的任意一个字母。

[A-Z]——匹配大写A~Z中的任意一个字母。

3. 元字符

在正则表达式中有一些特殊字符,代表着特殊意义,叫作元字符。

. 代表除换行符以外的任何一个字符。

| 代表或的意思,匹配其中一项就代表匹配。

4. 原子分组

匹配多个字符时用()分组,分组代表一个原子集合或一个大原子,并压入堆栈(内存)用于调用。组号是从左到右计数的,调用时如果是字面量形式,则用\1;如果是构造函数方式,则用\1,这种方式称为反向引用。

示例:

5. 取消反向引用

使用形如(?:pattern)的正则就可以避免保存括号内的匹配结果,反向引用也将失效。

6. 量词

可以使用一些元字符,重复表示一些元子或元字符,具体如下:

*——重复零次或更多次。

+——重复一次或更多次。

?——重复零次或一次。

{n}——重复n次。

{n,}——重复n次或更多次。

{n,m}——重复n到m次。

7. 贪婪和吝啬

正则匹配是贪婪的,但并不意味着禁用,具体如下:

*?——重复任意次,但尽可能少重复。

+?——重复1次或更多次,但尽可能少重复。

??——重复0次或1次,但尽可能少重复。

{n,m}?——重复n到m次,但尽可能少重复。

{n,}?——重复n次以上,但尽可能少重复。

模式匹配的顺序(从高到低)见表5-4。

表5-4

5.6.5 正则方法

正则表达式对象RegExp提供了两个可用的方法:test()和exec()。

1.RegExp.test()

方法test()用来测试字符串中是否包含了匹配该正则表达式的子串,如果包含,则返回true,否则返回false。

示例:

说明:在上述示例中,使用正则表达式/uek/i 来检测str 字符串中是否包含匹配的子串,且匹配时不区分字母大小写。最终得到的结果是true,所以会返回并显示“找到了指定字符串”。

2.RegExp.exec()

方法exec()功能非常强大,是一个通用的方法,用来在字符串中匹配正则,并将结果保存在一个数组中即成功返回,失败返回null。

返回的数组包含特殊属性:

1)input表示被查找字符串。

2)index表示子字符串位置。

如果正则表达式没有设置g,那么exec方法不会对正则表达式有任何的影响。如果设置了g,那么exec执行后会更新正则表达式的lastIndex属性,表示本次匹配后所匹配的字符串的下一个字符的索引,下一次再用这个正则表达式匹配字符串时就会从上次的lastIndex属性开始匹配。

示例:

说明:方法exec()返回的数组对象还有一个扩展的属性,且这个属性在普通的数组中是没有的,这个属性是index,它返回的是匹配字符串的开始位置,除此之外,还有其他的扩展属性,示例如下:

说明:在上述示例中,通过方法exec()返回的数组对象扩展的属性可以获取更多详细的信息,最终返回结果为:“字符串长度为1;被搜索的字符串为abc de fg de;起始位置为4”。

5.6.6 字符串中用到正则的函数

在JavaScript中,正则表达式通常用于以下字符串方法:search()、replace()和split(),在字符串对象一节中也有提到过。

(1)search(regexp)

regexp 为正则表达式,返回索引位置,不支持全局索引(即g修饰符无效),找到即停止搜索。

示例:

说明:使用正则表达式搜索字符串Regexp,且不区分字母大小写。最终返回搜索字符串的起始位置7。

(2)replace(正则或字符串,替换内容)

支持全局g修饰符,如果模式不是全局,则当匹配到一个以后将不会继续匹配,反之则会继续往下匹配。

示例:

说明:在上述示例中,使用正则表达式且不区分字母大小写,将字符串中的Regexp 替换为UEK,最终返回的结果就是替换后的字符串“search UEK”。

(3)split 方法

split 方法用于拆分字符串,参数可以为字符串或正则表达式。

示例:

5.7 Set数据结构

5.7.1 Set基本用法

Set数据结构类似于数组,但是它的成员都是唯一的。

示例:

在函数Set()中可以传入一个数组类型的参数。

示例:

在Set 中判断是否重复,使用的是“Same-value equality”,类似于“===”,但是有例外,其中NaN不等于NaN。

示例:

5.7.2 Set属性和方法

1. 属性

1)Set.prototype.constructor:构造函数,默认是Set 函数。

2)Set.prototype.size:返回Set 实例的成员总数。

2. 方法

1)add(value):添加某个值,返回Set结构本身。

2)delete(value):删除某个值,返回一个布尔值,表示删除是否成功。

3)has(value):返回一个布尔值,表示该值是否为Set的成员。

4)clear():清除所有成员,没有返回值。

示例:

5.7.3 Set遍历方法

Set 的遍历顺序就是插入顺序。

1)keys():返回键名的遍历器。

2)values():返回键值的遍历器。

由于Set 结构没有键名,只有键值(或者说键名和键值是同一个值),因此keys 方法和values方法完全一致。

3)entries():返回键值对的遍历器。

4)forEach():使用回调函数遍历每个成员。

示例:

5.7.4 WeakSet

WeakSet的成员只能是对象,不能是其他类型的值。

WeakSet 中的对象都是弱引用,垃圾回收机制不考虑 WeakSet 对该对象的引用,也就是说,如果其他对象都不再引用该对象,那么垃圾回收机制会自动回收该对象所占用的内存,不考虑该对象是否还存在于WeakSet 中。这意味着,无法引用WeakSet 的成员,因此WeakSet是不可遍历的。

5.8 Map数据结构

JavaScript对象本质上是键值对的集合。之前,只能用字符串当作键。

Map 数据结构类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。

Object 结构提供了“字符串—值”的对应,Map结构提供了“值—值”的对应,是一种更完善的Hash结构实现。

所以需要“键值对”的数据结构时,Map比Object更合适。

5.8.1 Map 基本用法

1)Map 作为一个构造函数,可以接受一个数组当作参数。

2)Map 结构中,字符串“true”和布尔值true 是两个不同的键值。

示例:

只有对同一个对象的引用,Map结构才将其视为同一个键。所以下例中,set和get中的[1]不是同一个键。

虽然NaN不严格相等于自身,但Map将其视为同一个键。

示例:

5.8.2 Map 属性和方法

1. 属性

size属性用于返回Map结构的成员个数。

示例:

2. 方法

1)方法set()返回的是Map本身,因此也可以采用链式写法。

示例:

2)方法get()读取对应的键值,如果找不到传入的键值,则返回undefined。

示例:

3)方法has()返回一个布尔值,表示该键值是否在Map 结构中。

示例:

4)方法delete()用于删除某个键,删除成功,则返回true;如果删除失败,则返回false。

示例:

5)方法clear()用于清除所有成员,没有返回值。

示例:

5.8.3 Map 遍历方法

1)keys():返回键名的遍历器。

2)values():返回键值的遍历器。

3)entries():返回所有成员的遍历器。

示例:

5.8.4 Map与数组对象的转换

(1)Map转换为数组

示例:

(2)数组转换为Map

示例:

(3)Map转换为对象

如果所有Map的键都是字符串,则它可以转换为对象。

示例:

(4)对象转换为Map

示例:

5.8.5 WeakMap

WeakMap 结构与Map 结构基本类似,唯一的区别是它只接受对象作为键名(null 除外),不接受其他类型的值作为键名。

键名所指向的对象,不进入垃圾回收机制。

示例: