RegExp对象

概述

JavaScript 的正则表达式体系是参照 Perl 5 建立的。

新建正则表达式有两种方法。一种是使用字面量,以斜杠表示开始和结束。

另一种是使用RegExp构造函数。

区别是,第一种方法在引擎编译代码时,就会新建正则表达式,第二种方法在运行时新建正则表达式,所以前者的效率较高。而且,前者比较便利和直观,所以实际应用中,基本上都采用字面量定义正则表达式。

RegExp构造函数还可以接受第二个参数,表示修饰符。

实例属性

正则对象的实例属性分成两类。

一类是修饰符相关,返回一个布尔值,表示对应的修饰符是否设置。

三个属性都是只读的。

另一类是与修饰符无关的属性,主要是下面两个。

实例方法

RegExp.prototype.test()

正则实例对象的test方法返回一个布尔值,表示当前模式是否能匹配参数字符串。

如果正则表达式带有g修饰符,则每一次test方法都从上一次结束的位置开始向后匹配。

带有g修饰符时,可以通过正则对象的lastIndex属性指定开始搜索的位置。

lastIndex属性只对同一个正则表达式有效。

如果正则模式是一个空字符串,则匹配所有字符串。

RegExp.prototype.exec()

正则实例对象的exec方法,用来返回匹配结果。如果发现匹配,就返回一个数组,成员是匹配成功的子字符串,否则返回null。

exec方法的返回数组还包含以下两个属性:

如果正则表达式加上g修饰符,则可以使用多次exec方法,下一次搜索的位置从上一次匹配成功结束的位置开始。

正则实例对象的lastIndex属性不仅可读,还可写。设置了g修饰符的时候,只要手动设置了lastIndex的值,就会从指定位置开始匹配。

字符串的实例方法

String.prototype.match()

字符串实例对象的match方法对字符串进行正则匹配,返回匹配结果。

字符串的match方法与正则对象的exec方法非常类似:匹配成功返回一个数组,匹配失败返回null。

如果正则表达式带有g修饰符,则该方法与正则对象的exec方法行为不同,会一次性返回所有匹配成功的结果。

设置正则表达式的lastIndex属性,对match方法无效,匹配总是从字符串的第一个字符开始。

设置正则对象的lastIndex属性是无效的。

字符串对象的search方法,返回第一个满足条件的匹配结果在整个字符串中的位置。如果没有任何匹配,则返回-1。

String.prototype.replace()

字符串对象的replace方法可以替换匹配的值。它接受两个参数,第一个是正则表达式,表示搜索模式,第二个是替换的内容。

正则表达式如果不加g修饰符,就替换第一个匹配成功的值,否则替换所有匹配成功的值。

replace方法的第二个参数可以使用美元符号$,用来指代所替换的内容。

replace方法的第二个参数还可以是一个函数,将每一个匹配内容替换为函数返回值。

String.prototype.split()

字符串对象的split方法按照正则规则分割字符串,返回一个由分割后的各个部分组成的数组。

该方法接受两个参数,第一个参数是正则表达式,表示分隔规则,第二个参数是返回数组的最大成员数。

正则默认是贪婪匹配。

匹配规则

字面量字符和元字符

如果在正则表达式之中,某个字符只表示它字面的含义(就像前面的a和b),那么它们就叫做“字面量字符”(literal characters)。

除了字面量字符以外,还有一部分字符有特殊含义,不代表字面的意思。它们叫做“元字符”(metacharacters),主要有以下几个。

点字符(.)

点字符(.)匹配除回车(\r)、换行(\n) 、行分隔符(\u2028)和段分隔符(\u2029)以外的所有字符。注意,对于码点大于0xFFFF的 Unicode 字符,点字符不能正确匹配,会认为这是两个字符。

位置字符

位置字符用来提示字符所处的位置,主要有两个字符。

选择符(|)

竖线符号(|)在正则表达式中表示“或关系”(OR),即cat|dog表示匹配cat或dog。

转义符

正则表达式中那些有特殊含义的元字符,如果要匹配它们本身,就需要在它们前面要加上反斜杠。比如要匹配+,就要写成\ +。

正则表达式中,需要反斜杠转义的,一共有12个字符:^、.、[、$、(、)、|、*、+、?、{和\。需要特别注意的是,如果使用RegExp方法生成正则对象,转义需要使用两个斜杠,因为字符串内部会先转义一次。

特殊字符

正则表达式对一些不能打印的特殊字符,提供了表达方法。

字符类

字符类(class)表示有一系列字符可供选择,只要匹配其中一个就可以了。所有可供选择的字符都放在方括号内,比如[xyz] 表示x、y、z之中任选一个匹配。

脱字符( ^ )

如果方括号内的第一个字符是[^ ],则表示除了字符类之中的字符,其他字符都可以匹配。比如,[^ xyz]表示除了x、y、z之外都可以匹配。

如果方括号内没有其他字符,即只有[^],就表示匹配一切字符,其中包括换行符。相比之下,点号作为元字符(.)是不包括换行符的。

注意,脱字符只有在字符类的第一个位置才有特殊含义,否则就是字面含义。

连字符(-)

某些情况下,对于连续序列的字符,连字符(-)用来提供简写形式,表示字符的连续范围。比如,[abc]可以写成[a-c],[0123456789]可以写成[0-9],同理[A-Z]表示26个大写字母。

[1-31],不代表1到31,只代表1到3。

连字符还可以用来指定 Unicode 字符的范围。

预定义模式

重复类

模式的精确匹配次数,使用大括号({})表示。{n}表示恰好重复n次,{n,}表示至少重复n次,{n,m}表示重复不少于n次,不多于m次。

量词符

量词符用来设定某个模式出现的次数。

贪婪模式

三个量词符,默认情况下都是最大可能匹配,即匹配直到下一个字符不满足匹配规则为止。这被称为贪婪模式。

如果想将贪婪模式改为非贪婪模式,可以在量词符后面加一个问号。

模式结尾添加了一个问号/a+?/,这时就改为非贪婪模式,一旦条件满足,就不再往下匹配。

修饰符

修饰符(modifier)表示模式的附加规则,放在正则模式的最尾部。

修饰符可以单个使用,也可以多个一起使用。

g 修饰符

默认情况下,第一次匹配成功后,正则对象就停止向下匹配了。g修饰符表示全局匹配(global),加上它以后,正则对象将匹配全部符合条件的结果,主要用于搜索和替换。

i 修饰符

默认情况下,正则对象区分字母的大小写,加上i修饰符以后表示忽略大小写(ignorecase)。

m修饰符表示多行模式(multiline),会修改\^和\$的行为。默认情况下(即不加m修饰符时),\^
和\$匹配字符串的开始处和结尾处,加上m修饰符以后,\^和\$还会匹配行首和行尾,即\^和\$会识别换行符(\n)。

组匹配

概述

正则表达式的括号表示分组匹配,括号中的模式可以用来匹配分组的内容。

注意,使用组匹配时,不宜同时使用g修饰符,否则match方法不会捕获分组的内容。

这时必须使用正则表达式的exec方法,配合循环,才能读到每一轮匹配的组捕获。

正则表达式内部,还可以用\n引用括号匹配的内容,n是从1开始的自然数,表示对应顺序的括号。

非捕获组

(?:x)称为非捕获组(Non-capturing group),表示不返回该组匹配的内容,即匹配的结果中不计入这个括号。

先行断言

x(?=y)称为先行断言(Positive look-ahead),x只有在y前面才匹配,y不会被计入返回结果。比如,要匹配后面跟着百分号的数字,可以写成/\d+(?=%)/。

先行否定断言

x(?!y)称为先行否定断言(Negative look-ahead),x只有不在y前面才匹配,y不会被计入返回结果。比如,要匹配后面跟的不是百分号的数字,就要写成/\d+(?!%)/。

JSON对象

JSON 格式

JSON 格式(JavaScript Object Notation 的缩写)是一种用于数据交换的文本格式。

相比 XML 格式,JSON 格式有两个显著的优点:书写简单,一目了然;符合 JavaScript 原生语法,可以由解释引擎直接处理,不用另外添加解析代码。所以,JSON 迅速被接受,已经成为各大网站交换数据的标准格式,并被写入标准。

每个 JSON 对象就是一个值,可能是一个数组或对象,也可能是一个原始类型的值。总之,只能是一个值,不能是两个或更多的值。

JSON 对值的类型和格式有严格的规定。

JSON 对象

JSON对象是 JavaScript 的原生对象,用来处理 JSON 格式数据。它有两个静态方法:JSON.stringify()和JSON.parse()。

JSON.stringify()

基本用法

JSON.stringify方法用于将一个值转为 JSON 字符串。该字符串符合 JSON 格式,并且可以被JSON.parse方法还原。

注意,对于原始类型的字符串,转换结果会带双引号。

如果对象的属性是undefined、函数或 XML 对象,该属性会被JSON.stringify过滤。

如果数组的成员是undefined、函数或 XML 对象,则这些值被转成null。

正则对象会被转成空对象。

JSON.stringify方法会忽略对象的不可遍历属性。

第二个参数

JSON.stringify方法还可以接受一个数组,作为第二个参数,指定需要转成字符串的属性。

这个类似白名单的数组,只对对象的属性有效,对数组无效。

第二个参数还可以是一个函数,用来更改JSON.stringify的返回值。

f函数,接受两个参数,分别是被转换的对象的键名和键值。

这个处理函数是递归处理所有的键。

第三个参数

JSON.stringify还可以接受第三个参数,用于增加返回的 JSON 字符串的可读性。如果是数字,表示每个属性前面添加的空格(最多不超过10个);如果是字符串(不超过10个字符),则该字符串会添加在每行前面。

参数对象的 toJSON 方法

如果参数对象有自定义的toJSON方法,那么JSON.stringify会使用这个方法的返回值作为参数,而忽略原对象的其他属性。

JSON.parse()

JSON.parse方法用于将 JSON 字符串转换成对应的值。

如果传入的字符串不是有效的 JSON 格式,JSON.parse方法将报错。

单引号字符串不符合 JSON 格式,会报错。

为了处理解析错误,可以将JSON.parse方法放在try…catch代码块中。

JSON.parse方法可以接受一个处理函数,作为第二个参数,用法与JSON.stringify方法类似。

console对象

console对象是 JavaScript 的原生对象,它有点像 Unix 系统的标准输出stdout和标准错误stderr,可以输出各种信息到控制台,并且还提供了很多有用的辅助方法。

console的常见用途有两个。

浏览器实现

可以使用下面三种方法的打开它。

面板。

Console面板基本上就是一个命令行窗口,你可以在提示符下,键入各种命令。

console 对象的静态方法

console对象提供的各种静态方法,用来与控制台窗口互动。

console.log(),console.info(),console.debug()

console.log方法用于在控制台输出信息。它可以接受一个或多个参数,将它们连接起来输出。

console.log方法会自动在每次输出的结尾,添加换行符。

如果第一个参数是格式字符串(使用了格式占位符),console.log方法将依次用后面的参数替换占位符,然后再进行输出。

不同类型的数据必须使用对应的占位符。

console.info是console.log方法的别名,用法完全一样。只不过console.info方法会在输出信息的前面,加上一个蓝色图标。

console.debug方法与console.log方法类似,会在控制台输出调试信息。但是,默认情况下,console.debug输出的信息不会显示,只有在打开显示级别在verbose的情况下,才会显示。

console对象的所有方法,都可以被覆盖。因此,可以按照自己的需要,定义console.log方法。

console.warn(),console.error()

warn方法和error方法也是在控制台输出信息,它们与log方法的不同之处在于,warn方法输出信息时,在最前面加一个黄色三角,表示警告;error方法输出信息时,在最前面加一个红色的叉,表示出错。同时,还会高亮显示输出文字和错误发生的堆栈。其他方面都一样。

console.table()

对于某些复合类型的数据,console.table方法可以将其转为表格显示。

console.count()

count方法用于计数,输出它被调用了多少次。

该方法可以接受一个字符串作为参数,作为标签,对执行次数进行分类。

console.dir(),console.dirxml()

dir方法用来对一个对象进行检查(inspect),并以易于阅读和打印的格式显示。

dir方法的输出结果,比log方法更易读,信息也更丰富。

对于输出 DOM 对象非常有用,因为会显示 DOM 对象的所有属性。

Node 环境之中,还可以指定以代码高亮的形式输出。

dirxml方法主要用于以目录树的形式,显示 DOM 节点。

如果参数不是 DOM 节点,而是普通的 JavaScript 对象,console.dirxml等同于console.dir。

console.assert()

console.assert方法主要用于程序运行过程中,进行条件判断,如果不满足条件,就显示一个错误,但不会中断程序执行。这样就相当于提示用户,内部状态不正确。

它接受两个参数,第一个参数是表达式,第二个参数是字符串。只有当第一个参数为false,才会提示有错误,在控制台输出第二个参数,否则不会有任何结果。

console.time(),console.timeEnd()

这两个方法用于计时,可以算出一个操作所花费的准确时间。

time方法表示计时开始,timeEnd方法表示计时结束。它们的参数是计时器的名称。调用timeEnd方法之后,控制台会显示“计时器名称: 所耗费的时间”。

console.group(),console.groupEnd(),console.groupCollapsed()

console.group和console.groupEnd这两个方法用于将显示的信息分组。它只在输出大量信息时有用,分在一组的信息,可以用鼠标折叠/展开。

console.groupCollapsed方法与console.group方法很类似,唯一的区别是该组的内容,在第一次显示时是收起的(collapsed),而不是展开的。

console.trace(),console.clear()

console.trace方法显示当前执行的代码在堆栈中的调用路径。

console.clear方法用于清除当前控制台的所有输出,将光标回置到第一行。如果用户选中了控制台的“Preserve log”选项,console.clear方法将不起作用。

命令行API

浏览器控制台中,除了使用console对象,还可以使用一些控制台自带的命令行方法。

$_

$_属性返回上一个表达式的值。

\$0 - \$4

控制台保存了最近5个在 Elements 面板选中的 DOM 元素,\$0代表倒数第一个(最近一个),\$1代表倒数第二个,以此类推直到\$4。

$(selector)

\$(selector)返回第一个匹配的元素,等同于document.querySelector()。注意,如果页面脚本对\$有定义,则会覆盖原始的定义。比如,页面里面有 jQuery,控制台执行\$(selector)就会采用 jQuery 的实现,返回一个数组。

$$(selector)

$$(selector)返回选中的 DOM 对象,等同于document.querySelectorAll。

$x(path)

$x(path)方法返回一个数组,包含匹配特定 XPath 表达式的所有 DOM 元素。

inspect(object)

inspect(object)方法打开相关面板,并选中相应的元素,显示它的细节。DOM 元素在Elements面板中显示,比如inspect(document)会在 Elements 面板显示document元素。JavaScript 对象在控制台面板Profiles面板中显示,比如inspect(window)。

getEventListeners(object)

getEventListeners(object)方法返回一个对象,该对象的成员为object登记了回调函数的各种事件(比如click或keydown),每个事件对应一个数组,数组的成员为该事件的回调函数。

keys(object),values(object)

keys(object)方法返回一个数组,包含object的所有键名。
values(object)方法返回一个数组,包含object的所有键值。

monitorEvents(object[, events]) ,unmonitorEvents(object[, events])

monitorEvents(object[, events])方法监听特定对象上发生的特定事件。事件发生时,会返回一个Event对象,包含该事件的相关信息。unmonitorEvents方法用于停止监听。

monitorEvents允许监听同一大类的事件。所有事件可以分成四个大类。

其他方法

命令行 API 还提供以下方法。

debugger 语句

debugger语句主要用于除错,作用是设置断点。如果有正在运行的除错工具,程序运行到debugger语句时会自动停下。如果没有除错工具,debugger语句不会产生任何结果,JavaScript 引擎自动跳过这一句。

Chrome 浏览器中,当代码运行到debugger语句时,就会暂停运行,自动打开脚本源码界面。

属性描述对象

概述

JavaScript 提供了一个内部数据结构,用来描述对象的属性,控制它的行为,比如该属性是否可写、可遍历等等。这个内部数据结构称为“属性描述对象”(attributes object)。每个属性都有自己对应的属性描述对象,保存该属性的一些元信息。

属性描述对象提供6个元属性。

value

value是该属性的属性值,默认为undefined。

writable

writable是一个布尔值,表示属性值(value)是否可改变(即是否可写),默认为true。

enumerable

enumerable是一个布尔值,表示该属性是否可遍历,默认为true。如果设为false,会使得某些操作(比如for…in循环、Object.keys(),JSON.stringify方法)跳过该属性。

configurable

configurable是一个布尔值,表示可配置性,默认为true。如果设为false,将阻止某些操作改写该属性,比如无法删除该属性,也不得改变该属性的属性描述对象(value属性除外)。也就是说,configurable属性控制了属性描述对象的可写性。

get

get是一个函数,表示该属性的取值函数(getter),默认为undefined。

set

set是一个函数,表示该属性的存值函数(setter),默认为undefined。

Object.getOwnPropertyDescriptor()

Object.getOwnPropertyDescriptor方法可以获取属性描述对象。它的第一个参数是一个对象,第二个参数是一个字符串,对应该对象的某个属性名。

注意,Object.getOwnPropertyDescriptor方法只能用于对象自身的属性,不能用于继承的属性。

Object.getOwnPropertyNames()

Object.getOwnPropertyNames方法返回一个数组,成员是参数对象自身的全部属性的属性名,不管该属性是否可遍历。

这跟Object.keys的行为不同,Object.keys只返回对象自身的可遍历属性的全部属性名。

Object.defineProperty(),Object.defineProperties()

Object.defineProperty方法允许通过属性描述对象,定义或修改一个属性,然后返回修改后的对象,它的用法如下。

Object.defineProperty方法接受三个参数,依次如下。

如果一次性定义或修改多个属性,可以使用Object.defineProperties方法。

注意,一旦定义了取值函数get(或存值函数set),就不能将writable属性设为true,或者同时定义value属性,否则会报错。

Object.defineProperty()和Object.defineProperties()的第三个参数,是一个属性对象。它的writable、configurable、enumerable这三个属性的默认值都为false。

Object.prototype.propertyIsEnumerable()

实例对象的propertyIsEnumerable方法返回一个布尔值,用来判断某个属性是否可遍历。

元属性

属性描述对象的各个属性称为“元属性”,因为它们可以看作是控制属性的属性。

value

value属性是目标属性的值。

writable

enumerable

configurable

存取器

除了直接定义以外,属性还可以用存取器(accessor)定义。其中,存值函数称为setter,使用属性描述对象的set属性;取值函数称为getter,使用属性描述对象的get属性。

一旦对目标属性定义了存取器,那么存取的时候,都将执行对应的函数。利用这个功能,可以实现许多高级特性,比如某个属性禁止赋值。

注意,取值函数get不能接受参数,存值函数set只能接受一个参数(即属性的值)。

存取器往往用于,属性的值依赖对象内部数据的场合。

next属性的存值函数和取值函数,都依赖于内部属性$n。

对象的拷贝

有时,我们需要将一个对象的所有属性,拷贝到另一个对象。

可以通过Object.defineProperty方法来拷贝属性。

hasOwnProperty那一行用来过滤掉继承的属性,否则会报错,因为Object.getOwnPropertyDescriptor读不到继承属性的属性描述对象。

控制对象状态

有时需要冻结对象的读写状态,防止对象被改变。JavaScript 提供了三种冻结方法,最弱的一种是Object.preventExtensions,其次是Object.seal,最强的是Object.freeze。

Object.preventExtensions()

Object.preventExtensions方法可以使得一个对象无法再添加新的属性。

Object.isExtensible()

Object.isExtensible方法用于检查一个对象是否使用了Object.preventExtensions方法。也就是说,检查是否可以为一个对象添加属性。

Object.seal()

Object.seal方法使得一个对象既无法添加新属性,也无法删除旧属性。

Object.seal实质是把属性描述对象的configurable属性设为false,因此属性描述对象不再能改变了。

Object.seal只是禁止新增或删除属性,并不影响修改某个属性的值。

Object.isSealed()

Object.isSealed方法用于检查一个对象是否使用了Object.seal方法。

这时,Object.isExtensible方法也返回false。

Object.freeze()

Object.freeze方法可以使得一个对象无法添加新属性、无法删除旧属性、也无法改变属性的值,使得这个对象实际上变成了常量。

这些操作并不报错,只是默默地失败。如果在严格模式下,则会报错。

Object.isFrozen()

Object.isFrozen方法用于检查一个对象是否使用了Object.freeze方法。

使用Object.freeze方法以后,Object.isSealed将会返回true,Object.isExtensible返回false。

局限性

上面的三个方法锁定对象的可写性有一个漏洞:可以通过改变原型对象,来为对象增加属性。

一种解决方案是,把obj的原型也冻结住。

另外一个局限是,如果属性值是对象,上面这些方法只能冻结属性指向的对象,而不能冻结对象本身的内容。

obj.bar属性指向一个数组,obj对象被冻结以后,这个指向无法改变,即无法指向其他值,但是所指向的数组是可以改变的。