1.1Number 对象的静态属性
Number
对象拥有以下一些静态属性(即直接定义在Number
对象上的属性,而不是定义在实例上的属性)。
Number.POSITIVE_INFINITY
:正的无限,指向Infinity
。Number.NEGATIVE_INFINITY
:负的无限,指向-Infinity
。Number.NaN
:表示非数值,指向NaN
。Number.MIN_VALUE
:表示最小的正数(即最接近0的正数,在64位浮点数体系中为5e-324
),相应的,最接近0的负数为-Number.MIN_VALUE
。Number.MAX_SAFE_INTEGER
:表示能够精确表示的最大整数,即9007199254740991
。Number.MIN_SAFE_INTEGER
:表示能够精确表示的最小整数,即-9007199254740991
。
Number.POSITIVE_INFINITY // Infinity
Number.NEGATIVE_INFINITY // -Infinity
Number.NaN // NaN
Number.MAX_VALUE
// 1.7976931348623157e+308
Number.MAX_VALUE < Infinity
// true
Number.MIN_VALUE
// 5e-324
Number.MIN_VALUE > 0
// true
Number.MAX_SAFE_INTEGER // 9007199254740991
Number.MIN_SAFE_INTEGER // -9007199254740991
1.2实例方法
Number
对象有4个实例方法,都跟将数值转换成指定格式有关。
Number.prototype.toString()
Number
对象部署了自己的toString
方法,用来将一个数值转为字符串形式。
(10).toString() // "10"
toString
方法可以接受一个参数,表示输出的进制。如果省略这个参数,默认将数值先转为十进制,再输出字符串;否则,就根据参数指定的进制,将一个数字转化成某个进制的字符串。
(10).toString(2) // "1010"
(10).toString(8) // "12"
(10).toString(16) // "a"
Number.prototype.toFixed()
toFixed()
方法先将一个数转为指定位数的小数,然后返回这个小数对应的字符串。
(10).toFixed(2) // "10.00"
10.005.toFixed(2) // "10.01"
10
和10.005
先转成2位小数,然后转成字符串。其中10
必须放在括号里,否则后面的点会被处理成小数点。
toFixed()
方法的参数为小数位数,有效范围为0到100,超出这个范围将抛出 RangeError 错误。
Number.prototype.toExponential()
toExponential
方法用于将一个数转为科学计数法形式。
(10).toExponential() // "1e+1"
(10).toExponential(1) // "1.0e+1"
(10).toExponential(2) // "1.00e+1"
(1234).toExponential() // "1.234e+3"
(1234).toExponential(1) // "1.2e+3"
(1234).toExponential(2) // "1.23e+3"
toExponential
方法的参数是小数点后有效数字的位数,范围为0到100,超出这个范围,会抛出一个 RangeError 错误。
Number.prototype.toPrecision()
Number.prototype.toPrecision()
方法用于将一个数转为指定位数的有效数字。
(12.34).toPrecision(1) // "1e+1"
(12.34).toPrecision(2) // "12"
(12.34).toPrecision(3) // "12.3"
(12.34).toPrecision(4) // "12.34"
(12.34).toPrecision(5) // "12.340"
该方法的参数为有效数字的位数,范围是1到100,超出这个范围会抛出 RangeError 错误。
Number.prototype.toLocaleString()
Number.prototype.toLocaleString()
方法接受一个地区码作为参数,返回一个字符串,表示当前数字在该地区的当地书写形式。
(123).toLocaleString('zh-Hans-CN-u-nu-hanidec')
// "一二三"
该方法还可以接受第二个参数配置对象,用来定制指定用途的返回字符串。该对象的style
属性指定输出样式,默认值是decimal
,表示输出十进制形式。如果值为percent
,表示输出百分数。
(123).toLocaleString('zh-Hans-CN', { style: 'percent' })
// "12,300%"
2.String 对象
2.1静态方法
String.fromCharCode()
String
对象提供的静态方法(即定义在对象本身,而不是定义在对象实例的方法),主要是String.fromCharCode()
。该方法的参数是一个或多个数值,代表 Unicode 码点,返回值是这些码点组成的字符串。
String.fromCharCode() // ""
String.fromCharCode(97) // "a"
String.fromCharCode(104, 101, 108, 108, 111)
// "hello"
String.fromCharCode
方法的参数为空,就返回空字符串;否则,返回参数对应的 Unicode 字符串。
2.2实例属性
String.prototype.length
字符串实例的length
属性返回字符串的长度。
'abc'.length // 3
2.3实例方法
String.prototype.charAt()
charAt
方法返回指定位置的字符,参数是从0
开始编号的位置。
let s = new String('abc');
s.charAt(1) // "b"
s.charAt(s.length - 1) // "c"
String.prototype.charCodeAt()
charCodeAt()
方法返回字符串指定位置的 Unicode 码点(十进制表示),相当于String.fromCharCode()
的逆操作。
'abc'.charCodeAt(1) // 98
如果没有任何参数,charCodeAt
返回首字符的 Unicode 码点,如果参数为负数,或大于等于字符串的长度,charCodeAt
返回NaN
。
String.prototype.concat()
concat
方法用于连接两个字符串,返回一个新字符串,不改变原字符串。
let s1 = 'abc';
let s2 = 'def';
s1.concat(s2) // "abcdef"
s1 // "abc"
String.prototype.slice()
slice()
方法用于从原字符串取出子字符串并返回,不改变原字符串。它的第一个参数是子字符串的开始位置,第二个参数是子字符串的结束位置(不含该位置)。
'JavaScript'.slice(0, 4) // "Java"
如果省略第二个参数,则表示子字符串一直到原字符串结束。如果参数是负值,表示从结尾开始倒数计算的位置,即该负值加上字符串长度。如果第一个参数大于第二个参数(正数情况下),slice()
方法返回一个空字符串。
String.prototype.substring()
substring
方法用于从原字符串取出子字符串并返回,不改变原字符串,跟slice
方法很相像。它的第一个参数表示子字符串的开始位置,第二个位置表示结束位置(返回结果不含该位置)。
'JavaScript'.substring(0, 4) // "Java"
如果省略第二个参数,则表示子字符串一直到原字符串的结束。如果第一个参数大于第二个参数,substring
方法会自动更换两个参数的位置。如果参数是负数,substring
方法会自动将负数转为0。
String.prototype.substr()
substr
方法用于从原字符串取出子字符串并返回,不改变原字符串,跟slice
和substring
方法的作用相同。
substr
方法的第一个参数是子字符串的开始位置(从0开始计算),第二个参数是子字符串的长度。
'JavaScript'.substr(4, 6) // "Script"
如果省略第二个参数,则表示子字符串一直到原字符串的结束。如果第一个参数是负数,表示倒数计算的字符位置。如果第二个参数是负数,将被自动转为0,因此会返回空字符串。
String.prototype.indexOf(),String.prototype.lastIndexOf()
indexOf
方法用于确定一个字符串在另一个字符串中第一次出现的位置,返回结果是匹配开始的位置。如果返回-1
,就表示不匹配。
'hello world'.indexOf('o') // 4
'JavaScript'.indexOf('script') // -1
indexOf
方法还可以接受第二个参数,表示从该位置开始向后匹配。
String.prototype.trim()
trim
方法用于去除字符串两端的空格,返回一个新字符串,不改变原字符串。
' hello world '.trim()
// "hello world"
String.prototype.toLowerCase(),String.prototype.toUpperCase()
toLowerCase
方法用于将一个字符串全部转为小写,toUpperCase
则是全部转为大写。它们都返回一个新字符串,不改变原字符串。
'Hello World'.toLowerCase()
// "hello world"
'Hello World'.toUpperCase()
// "HELLO WORLD"
String.prototype.match()
match
方法用于确定原字符串是否匹配某个子字符串,返回一个数组,成员为匹配的第一个字符串。如果没有找到匹配,则返回null
。
'cat, bat, sat, fat'.match('at') // ["at"]
'cat, bat, sat, fat'.match('xt') // null
String.prototype.search(),String.prototype.replace()
search
方法的用法基本等同于match
,但是返回值为匹配的第一个位置。如果没有找到匹配,则返回-1
。
'cat, bat, sat, fat'.search('at') // 1
String.prototype.split()
split
方法按照给定规则分割字符串,返回一个由分割出来的子字符串组成的数组。
'a|b|c'.split('|') // ["a", "b", "c"]
如果分割规则为空字符串,则返回数组的成员是原字符串的每一个字符。如果省略参数,则返回数组的唯一成员就是原字符串。如果满足分割规则的两个部分紧邻着(即两个分割符中间没有其他字符),则返回数组之中会有一个空字符串。
'a|b|c'.split() // ["a|b|c"]
'a||c'.split('|') // ['a', '', 'c']
'|b|c'.split('|') // ["", "b", "c"]
'a|b|'.split('|') // ["a", "b", ""]
String.prototype.localeCompare()
localeCompare
方法用于比较两个字符串。它返回一个整数,如果小于0,表示第一个字符串小于第二个字符串;如果等于0,表示两者相等;如果大于0,表示第一个字符串大于第二个字符串。
'apple'.localeCompare('banana') // -1
'apple'.localeCompare('apple') // 0
3.Math 对象
3.1静态属性
Math
对象的静态属性,提供以下一些数学常数。
Math.E
:常数e
。Math.LN2
:2 的自然对数。Math.LN10
:10 的自然对数。Math.LOG2E
:以 2 为底的e
的对数。Math.LOG10E
:以 10 为底的e
的对数。Math.PI
:常数π
。Math.SQRT1_2
:0.5 的平方根。Math.SQRT2
:2 的平方根。
Math.E // 2.718281828459045
Math.LN2 // 0.6931471805599453
Math.LN10 // 2.302585092994046
Math.LOG2E // 1.4426950408889634
Math.LOG10E // 0.4342944819032518
Math.PI // 3.141592653589793
Math.SQRT1_2 // 0.7071067811865476
Math.SQRT2 // 1.4142135623730951
这些属性都是只读的,不能修改。
3.2静态方法
Math
对象提供以下一些静态方法。
Math.abs()
:绝对值Math.ceil()
:向上取整Math.floor()
:向下取整Math.max()
:最大值Math.min()
:最小值Math.pow()
:幂运算Math.sqrt()
:平方根Math.log()
:自然对数Math.exp()
:e
的指数Math.round()
:四舍五入Math.random()
:随机数
Math.abs()
Math.abs
方法返回参数值的绝对值。
Math.abs(1) // 1
Math.abs(-1) // 1
Math.max(),Math.min()
Math.max
方法返回参数之中最大的那个值,Math.min
返回最小的那个值。如果参数为空, Math.min
返回Infinity
, Math.max
返回-Infinity
。
Math.max(2, -1, 5) // 5
Math.min(2, -1, 5) // -1
Math.min() // Infinity
Math.max() // -Infinity
Math.floor(),Math.ceil()
Math.floor
方法返回小于或等于参数值的最大整数(地板值)。
Math.floor(3.2) // 3
Math.floor(-3.2) // -4
Math.ceil
方法返回大于或等于参数值的最小整数(天花板值)。
Math.ceil(3.2) // 4
Math.ceil(-3.2) // -3
这两个方法可以结合起来,实现一个总是返回数值的整数部分的函数。
function ToInteger(x) {
x = Number(x);
return x < 0 ? Math.ceil(x) : Math.floor(x);
}
ToInteger(3.2) // 3
ToInteger(3.5) // 3
ToInteger(3.8) // 3
ToInteger(-3.2) // -3
ToInteger(-3.5) // -3
ToInteger(-3.8) // -3
不管正数或负数,ToInteger
函数总是返回一个数值的整数部分。
Math.round()
Math.round
方法用于四舍五入。
Math.round(0.1) // 0
Math.round(0.5) // 1
Math.round(0.6) // 1
// 等同于
Math.floor(x + 0.5)
注意,它对负数的处理(主要是对0.5
的处理)。
Math.round(-1.1) // -1
Math.round(-1.5) // -1
Math.round(-1.6) // -2
Math.pow()
Math.pow
方法返回以第一个参数为底数、第二个参数为指数的幂运算值。
// 等同于 2 ** 2
Math.pow(2, 2) // 4
// 等同于 2 ** 3
Math.pow(2, 3) // 8
Math.sqrt()
Math.sqrt
方法返回参数值的平方根。如果参数是一个负值,则返回NaN
。
Math.sqrt(4) // 2
Math.sqrt(-4) // NaN
Math.log()
Math.log
方法返回以e
为底的自然对数值。
Math.log(Math.E) // 1
Math.log(10) // 2.302585092994046
Math.exp()
Math.exp
方法返回常数e
的参数次方。
Math.exp(1) // 2.718281828459045
Math.exp(3) // 20.085536923187668
Math.random()
Math.random()
返回0到1之间的一个伪随机数,可能等于0,但是一定小于1。
Math.random() // 0.7151307314634323
任意范围的随机数生成函数如下。
function getRandomArbitrary(min, max) {
return Math.random() * (max - min) + min;
}
getRandomArbitrary(1.5, 6.5)
// 2.4942810038223864
random_str
函数接受一个整数作为参数,返回变量ALPHABET
内的随机字符所组成的指定长度的字符串。
4.Date 对象
4.1普通函数的用法
Date
对象可以作为普通函数直接调用,返回一个代表当前时间的字符串。
Date()
// Sun Feb 19 2023 20:27:25 GMT+0800 (GMT+08:00)
4.2构造函数的用法
Date
还可以当作构造函数使用。对它使用new
命令,会返回一个Date
对象的实例。如果不加参数,实例代表的就是当前时间。
Date
实例有一个独特的地方。其他对象求值的时候,都是默认调用.valueOf()
方法,但是Date
实例求值的时候,默认调用的是toString()
方法。这导致对Date
实例求值,返回的是一个字符串,代表该实例对应的时间。
let today = new Date();
today
等同于
today.toString()
4.3静态方法
Date.now()
Date.now
方法返回当前时间距离时间零点(1970年1月1日 00:00:00 UTC)的毫秒数,相当于 Unix 时间戳乘以1000。
Date.now() // 1676820588476
Date.parse()
Date.parse
方法用来解析日期字符串,返回该时间距离时间零点(1970年1月1日 00:00:00)的毫秒数。
日期字符串应该符合 RFC 2822 和 ISO 8061 这两个标准,即YYYY-MM-DDTHH:mm:ss.sssZ
格式,其中最后的Z
表示时区。但是,其他格式也可以被解析,请看下面的例子。
Date.parse('Aug 9, 1995')
Date.parse('January 26, 2011 13:51:50')
Date.parse('Mon, 25 Dec 1995 13:30:00 GMT')
Date.parse('Mon, 25 Dec 1995 13:30:00 +0430')
Date.parse('2011-10-10')
Date.parse('2011-10-10T14:48:00')
Date.UTC()
Date.UTC
方法接受年、月、日等变量作为参数,返回该时间距离时间零点(1970年1月1日 00:00:00 UTC)的毫秒数。
// 格式
Date.UTC(year, month[, date[, hrs[, min[, sec[, ms]]]]])
// 用法
Date.UTC(2011, 0, 1, 2, 3, 4, 567)
// 1293847384567
4.4实例方法
Date
的实例对象,有几十个自己的方法,除了valueOf
和toString
,可以分为以下三类。
to
类:从Date
对象返回一个字符串,表示指定的时间。get
类:获取Date
对象的日期和时间。set
类:设置Date
对象的日期和时间。
Date.prototype.valueOf()
valueOf
方法返回实例对象距离时间零点(1970年1月1日00:00:00 UTC)对应的毫秒数,该方法等同于getTime
方法。
let d = new Date();
d.valueOf() // 1676820673938
d.getTime() // 1676820673938
4.5to 类方法
(1)Date.prototype.toString()
toString
方法返回一个完整的日期字符串。
let d = new Date(2013, 0, 1);
d.toString()
// "Tue Jan 01 2013 00:00:00 GMT+0800 (CST)"
d
// "Tue Jan 01 2013 00:00:00 GMT+0800 (CST)"
(2)Date.prototype.toUTCString()
toUTCString
方法返回对应的 UTC 时间,也就是比北京时间晚8个小时。
let d = new Date(2013, 0, 1);
d.toUTCString()
// "Mon, 31 Dec 2012 16:00:00 GMT"
(3)Date.prototype.toISOString()
toISOString
方法返回对应时间的 ISO8601 写法。
let d = new Date(2013, 0, 1);
d.toISOString()
// "2012-12-31T16:00:00.000Z"
(4)Date.prototype.toJSON()
toJSON
方法返回一个符合 JSON 格式的 ISO 日期字符串,与toISOString
方法的返回结果完全相同。
let d = new Date(2013, 0, 1);
d.toJSON()
// "2012-12-31T16:00:00.000Z"
(5)Date.prototype.toDateString()
toDateString
方法返回日期字符串(不含小时、分和秒)。
let d = new Date(2013, 0, 1);
d.toDateString() // "Tue Jan 01 20
(6)Date.prototype.toTimeString()
toTimeString
方法返回时间字符串(不含年月日)。
let d = new Date(2013, 0, 1);
d.toTimeString() // "00:00:00 GMT+0800 (CST)"
本地时间
以下三种方法,可以将 Date 实例转为表示本地时间的字符串。
Date.prototype.toLocaleString()
:完整的本地时间。Date.prototype.toLocaleDateString()
:本地日期(不含小时、分和秒)。Date.prototype.toLocaleTimeString()
:本地时间(不含年月日)。
options
配置对象有以下属性。
dateStyle
:可能的值为full
、long
、medium
、short
。timeStyle
:可能的值为full
、long
、medium
、short
。month
:可能的值为numeric
、2-digit
、long
、short
、narrow
。year
:可能的值为numeric
、2-digit
。weekday
:可能的值为long
、short
、narrow
。day
、hour
、minute
、second
:可能的值为numeric
、2-digit
。timeZone
:可能的值为 IANA 的时区数据库。timeZoneName
:可能的值为long
、short
。hour12
:24小时周期还是12小时周期,可能的值为true
、false
。
let d = new Date(2013, 0, 1);
d.toLocaleDateString('en-US', {
weekday: 'long',
year: 'numeric',
month: 'long',
day: 'numeric'
})
// "Tuesday, January 1, 2013"
d.toLocaleDateString('en-US', {
day: "2-digit",
month: "long",
year: "2-digit"
});
// "January 01, 13"
d.toLocaleTimeString('en-US', {
timeZone: 'UTC',
timeZoneName: 'short'
})
// "4:00:00 PM UTC"
d.toLocaleTimeString('en-US', {
timeZone: 'Asia/Shanghai',
timeZoneName: 'long'
})
// "12:00:00 AM China Standard Time"
d.toLocaleTimeString('en-US', {
hour12: false
})
// "00:00:00"
d.toLocaleTimeString('en-US', {
hour12: true
})
// "12:00:00 AM"
4.6get 类方法
Date
对象提供了一系列get*
方法,用来获取实例对象某个方面的值。
getTime()
:返回实例距离1970年1月1日00:00:00的毫秒数,等同于valueOf
方法。getDate()
:返回实例对象对应每个月的几号(从1开始)。getDay()
:返回星期几,星期日为0,星期一为1,以此类推。getFullYear()
:返回四位的年份。getMonth()
:返回月份(0表示1月,11表示12月)。getHours()
:返回小时(0-23)。getMilliseconds()
:返回毫秒(0-999)。getMinutes()
:返回分钟(0-59)。getSeconds()
:返回秒(0-59)。getTimezoneOffset()
:返回当前时间与 UTC 的时区差异,以分钟表示,返回结果考虑到了夏令时因素。
所有这些get*
方法返回的都是整数,不同方法返回值的范围不一样。
- 分钟和秒:0 到 59
- 小时:0 到 23
- 星期:0(星期天)到 6(星期六)
- 日期:1 到 31
- 月份:0(一月)到 11(十二月)
let d = new Date('January 6, 2013');
d.getDate() // 6
d.getMonth() // 0
d.getFullYear() // 2013
d.getTimezoneOffset() // -480
get*
方法返回的都是当前时区的时间,Date
对象还提供了这些方法对应的 UTC 版本,用来返回 UTC 时间。
getUTCDate()
getUTCFullYear()
getUTCMonth()
getUTCDay()
getUTCHours()
getUTCMinutes()
getUTCSeconds()
getUTCMilliseconds()
4.7set 类方法
Date
对象提供了一系列set*
方法,用来设置实例对象的各个方面。
setDate(date)
:设置实例对象对应的每个月的几号(1-31),返回改变后毫秒时间戳。setFullYear(year [, month, date])
:设置四位年份。setHours(hour [, min, sec, ms])
:设置小时(0-23)。setMilliseconds(ms)
:设置毫秒(0-999)。setMinutes(min [, sec, ms])
:设置分钟(0-59)。setMonth(month [, date])
:设置月份(0-11)。setSeconds(sec [, ms])
:设置秒(0-59)。setTime(milliseconds)
:设置毫秒时间戳
5.RegExp 对象
5.1实例属性
正则对象的实例属性分成两类。
一类是修饰符相关,用于了解设置了什么修饰符。
RegExp.prototype.ignoreCase
:返回一个布尔值,表示是否设置了i
修饰符。RegExp.prototype.global
:返回一个布尔值,表示是否设置了g
修饰符。RegExp.prototype.multiline
:返回一个布尔值,表示是否设置了m
修饰符。RegExp.prototype.flags
:返回一个字符串,包含了已经设置的所有修饰符,按字母排序。
上面四个属性都是只读的。
let r = /abc/igm;
r.ignoreCase // true
r.global // true
r.multiline // true
r.flags // 'gim'
5.2实例方法
RegExp.prototype.test()
正则实例对象的test
方法返回一个布尔值,表示当前模式是否能匹配参数字符串。
/cat/.test('cats and dogs') // true
如果正则表达式带有g
修饰符,则每一次test
方法都从上一次结束的位置开始向后匹配。
let r = /x/g;
let s = '_x_x';
r.lastIndex // 0
r.test(s) // true
r.lastIndex // 2
r.test(s) // true
r.lastIndex // 4
r.test(s) // false
RegExp.prototype.exec()
正则实例对象的exec()
方法,用来返回匹配结果。如果发现匹配,就返回一个数组,成员是匹配成功的子字符串,否则返回null
。
let s = '_x_x';
let r1 = /x/;
let r2 = /y/;
r1.exec(s) // ["x"]
r2.exec(s) // null
正则对象r1
匹配成功,返回一个数组,成员是匹配结果;正则对象r2
匹配失败,返回null
。
exec()
方法的返回数组还包含以下两个属性:
input
:整个原字符串。index
:模式匹配成功的开始位置(从0开始计数)。
let r = /a(b+)a/;
let arr = r.exec('_abbba_aba_');
arr // ["abbba", "bbb"]
arr.index // 1
arr.input // "_abbba_aba_"
如果正则表达式加上g
修饰符,则可以使用多次exec()
方法,下一次搜索的位置从上一次匹配成功结束的位置开始。
let reg = /a/g;
let str = 'abc_abc_abc'
let r1 = reg.exec(str);
r1 // ["a"]
r1.index // 0
reg.lastIndex // 1
let r2 = reg.exec(str);
r2 // ["a"]
r2.index // 4
reg.lastIndex // 5
let r3 = reg.exec(str);
r3 // ["a"]
r3.index // 8
reg.lastIndex // 9
let r4 = reg.exec(str);
r4 // null
reg.lastIndex // 0
上面代码连续用了四次exec()
方法,前三次都是从上一次匹配结束的位置向后匹配。当第三次匹配结束以后,整个字符串已经到达尾部,匹配结果返回null
,正则实例对象的lastIndex
属性也重置为0
,意味着第四次匹配将从头开始。
字符串的实例方法
字符串的实例方法之中,有4种与正则表达式有关。
String.prototype.match()
:返回一个数组,成员是所有匹配的子字符串。String.prototype.search()
:按照给定的正则表达式进行搜索,返回一个整数,表示匹配开始的位置。String.prototype.replace()
:按照给定的正则表达式进行替换,返回替换后的字符串。String.prototype.split()
:按照给定规则进行字符串分割,返回一个数组,包含分割后的各个成员。
String.prototype.match()
字符串实例对象的match
方法对字符串进行正则匹配,返回匹配结果。
let s = '_x_x';
let r1 = /x/;
let r2 = /y/;
s.match(r1) // ["x"]
s.match(r2) // null
String.prototype.search()
字符串对象的search
方法,返回第一个满足条件的匹配结果在整个字符串中的位置。如果没有任何匹配,则返回-1
。
'_x_x'.search(/x/)
// 1
第一个匹配结果出现在字符串的1
号位置。
String.prototype.replace()
字符串对象的replace
方法可以替换匹配的值。它接受两个参数,第一个是正则表达式,表示搜索模式,第二个是替换的内容。
str.replace(search, replacement)
正则表达式如果不加g
修饰符,就替换第一个匹配成功的值,否则替换所有匹配成功的值。
'aaa'.replace('a', 'b') // "baa"
'aaa'.replace(/a/, 'b') // "baa"
'aaa'.replace(/a/g, 'b') // "bbb"
最后一个正则表达式使用了g
修饰符,导致所有的a
都被替换掉了。
replace
方法的一个应用,就是消除字符串首尾两端的空格。
let str = ' #id div.class ';
str.replace(/^\s+|\s+$/g, '')
// "#id div.class"
replace
方法的第二个参数可以使用美元符号$
,用来指代所替换的内容。
$&
:匹配的子字符串。$`
:匹配结果前面的文本。$'
:匹配结果后面的文本。$n
:匹配成功的第n
组内容,n
是从1开始的自然数。$$
:指代美元符号$
。
'hello world'.replace(/(\w+)\s(\w+)/, '$2 $1')
// "world hello"
'abc'.replace('b', '[$`-$&-$\']')
// "a[a-b-c]c"
String.prototype.split()
字符串对象的split
方法按照正则规则分割字符串,返回一个由分割后的各个部分组成的数组。
str.split(separator, [limit])
该方法接受两个参数,第一个参数是正则表达式,表示分隔规则,第二个参数是返回数组的最大成员数。
// 非正则分隔
'a, b,c, d'.split(',')
// [ 'a', ' b', 'c', ' d' ]
// 正则分隔,去除多余的空格
'a, b,c, d'.split(/, */)
// [ 'a', 'b', 'c', 'd' ]
// 指定返回数组的最大成员
'a, b,c, d'.split(/, */, 2)
[ 'a', 'b' ]
匹配规则
字面量字符和元字符
大部分字符在正则表达式中,就是字面的含义,比如/a/
匹配a
,/b/
匹配b
。如果在正则表达式之中,某个字符只表示它字面的含义(就像前面的a
和b
),那么它们就叫做“字面量字符”
/dog/.test('old dog') // true
正则表达式的dog
,就是字面量字符,所以/dog/
匹配old dog
,因为它就表示d
、o
、g
三个字母连在一起。
除了字面量字符以外,还有一部分字符有特殊含义,不代表字面的意思。它们叫做“元字符”(metacharacters),主要有以下几个。
(1)点字符(.)
点字符(.
)匹配除回车(\r
)、换行(\n
) 、行分隔符(\u2028
)和段分隔符(\u2029
)以外的所有字符。注意,对于码点大于0xFFFF
字符,点字符不能正确匹配,会认为这是两个字符。
/c.t/
c.t
匹配c
和t
之间包含任意一个字符的情况,只要这三个字符在同一行,比如cat
、c2t
、c-t
等等,但是不匹配coot
。
(2)位置字符
位置字符用来提示字符所处的位置,主要有两个字符。
^
表示字符串的开始位置$
表示字符串的结束位置
// test必须出现在开始位置
/^test/.test('test123') // true
// test必须出现在结束位置
/test$/.test('new test') // true
// 从开始位置到结束位置只有test
/^test$/.test('test') // true
/^test$/.test('test test') // false
(3)选择符(|
)
竖线符号(|
)在正则表达式中表示“或关系”(OR),即cat|dog
表示匹配cat
或dog
。
/11|22/.test('911') // true
多个选择符可以联合使用。
// 匹配fred、barney、betty之中的一个
/fred|barney|betty/
转义符
正则表达式中那些有特殊含义的元字符,如果要匹配它们本身,就需要在它们前面要加上反斜杠。比如要匹配+
,就要写成\+
。
/1+1/.test('1+1')
// false
/1\+1/.test('1+1')
// true
第一个正则表达式之所以不匹配,因为加号是元字符,不代表自身。第二个正则表达式使用反斜杠对加号转义,就能匹配成功。
正则表达式中,需要反斜杠转义的,一共有12个字符:^
、.
、[
、$
、(
、)
、|
、*
、+
、?
、{
和\
。需要特别注意的是,如果使用RegExp
方法生成正则对象,转义需要使用两个斜杠,因为字符串内部会先转义一次。
(new RegExp('1\+1')).test('1+1')
// false
(new RegExp('1\\+1')).test('1+1')
// true
RegExp
作为构造函数,参数是一个字符串。但是,在字符串内部,反斜杠也是转义字符,所以它会先被反斜杠转义一次,然后再被正则表达式转义一次,因此需要两个反斜杠转义。
特殊字符
正则表达式对一些不能打印的特殊字符,提供了表达方法。
\cX
表示Ctrl-[X]
,其中的X
是A-Z之中任一个英文字母,用来匹配控制字符。[\b]
匹配退格键(U+0008),不要与\b
混淆。\n
匹配换行键。\r
匹配回车键。\t
匹配制表符 tab(U+0009)。\v
匹配垂直制表符(U+000B)。\f
匹配换页符(U+000C)。\0
匹配null
字符(U+0000)。\xhh
匹配一个以两位十六进制数(\x00
-\xFF
)表示的字符。\uhhhh
匹配一个以四位十六进制数(\u0000
-\uFFFF
)表示的 Unicode 字符。
字符类
字符类(class)表示有一系列字符可供选择,只要匹配其中一个就可以了。所有可供选择的字符都放在方括号内,比如[xyz]
表示x
、y
、z
之中任选一个匹配。
/[abc]/.test('hello world') // false
/[abc]/.test('apple') // true
字符串hello world
不包含a
、b
、c
这三个字母中的任一个,所以返回false
;字符串apple
包含字母a
,所以返回true
。
(1)脱字符(^)
如果方括号内的第一个字符是[^]
,则表示除了字符类之中的字符,其他字符都可以匹配。比如,[^xyz]
表示除了x
、y
、z
之外都可以匹配。
/[^abc]/.test('bbc news') // true
/[^abc]/.test('bbc') // false
字符串bbc news
包含a
、b
、c
以外的其他字符,所以返回true
;字符串bbc
不包含a
、b
、c
以外的其他字符,所以返回false
。
如果方括号内没有其他字符,即只有[^]
,就表示匹配一切字符,其中包括换行符。相比之下,点号作为元字符(.
)是不包括换行符的。
let s = 'Please yes\nmake my day!';
s.match(/yes.*day/) // null
s.match(/yes[^]*day/) // [ 'yes\nmake my day']
(2)连字符(-)
对于连续序列的字符,连字符(-
)用来提供简写形式,表示字符的连续范围。比如,[abc]
可以写成[a-c]
,[0123456789]
可以写成[0-9]
,同理[A-Z]
表示26个大写字母。
/a-z/.test('b') // false
/[a-z]/.test('b') // true
预定义模式
预定义模式指的是某些常见模式的简写方式。
\d
匹配0-9之间的任一数字,相当于[0-9]
。\D
匹配所有0-9以外的字符,相当于[^0-9]
。\w
匹配任意的字母、数字和下划线,相当于[A-Za-z0-9_]
。\W
除所有字母、数字和下划线以外的字符,相当于[^A-Za-z0-9_]
。\s
匹配空格(包括换行符、制表符、空格符等),相等于[ \t\r\n\v\f]
。\S
匹配非空格的字符,相当于[^ \t\r\n\v\f]
。\b
匹配词的边界。\B
匹配非词边界,即在词的内部。
// \s 的例子
/\s\w*/.exec('hello world') // [" world"]
// \b 的例子
/\bworld/.test('hello world') // true
/\bworld/.test('hello-world') // true
/\bworld/.test('helloworld') // false
// \B 的例子
/\Bworld/.test('hello-world') // false
/\Bworld/.test('helloworld') // true
\s
表示空格,所以匹配结果会包括空格。\b
表示词的边界,所以world
的词首必须独立(词尾是否独立未指定),才会匹配。同理,\B
表示非词的边界,只有world
的词首不独立,才会匹配。
重复类
使用大括号({}
)表示。{n}
表示恰好重复n
次,{n,}
表示至少重复n
次,{n,m}
表示重复不少于n
次,不多于m
次。
/lo{2}k/.test('look') // true
/lo{2,5}k/.test('looook') // true
第一个模式指定o
连续出现2次,第二个模式指定o
连续出现2次到5次之间。
量词符
量词符用来设定某个模式出现的次数。
?
问号表示某个模式出现0次或1次,等同于{0, 1}
。*
星号表示某个模式出现0次或多次,等同于{0,}
。+
加号表示某个模式出现1次或多次,等同于{1,}
。
// t 出现0次或1次
/t?est/.test('test') // true
/t?est/.test('est') // true
// t 出现1次或多次
/t+est/.test('test') // true
/t+est/.test('ttest') // true
/t+est/.test('est') // false
// t 出现0次或多次
/t*est/.test('test') // true
/t*est/.test('ttest') // true
/t*est/.test('tttest') // true
/t*est/.test('est') // true
贪婪模式
上一小节的三个量词符,默认情况下都是最大可能匹配,即匹配到下一个字符不满足匹配规则为止。这被称为贪婪模式。
let s = 'aaa';
s.match(/a+/) // ["aaa"]
模式是/a+/
,表示匹配1个a
或多个a
,那么到底会匹配几个a
呢?因为默认是贪婪模式,会一直匹配到字符a
不出现为止,所以匹配结果是3个a
。
除了贪婪模式,还有非贪婪模式,即最小可能匹配。只要一发现匹配,就返回结果,不要往下检查。如果想将贪婪模式改为非贪婪模式,可以在量词符后面加一个问号。
let s = 'aaa';
s.match(/a+?/) // ["a"]
模式结尾添加了一个问号/a+?/
,这时就改为非贪婪模式,一旦条件满足,就不再往下匹配,+?
表示只要发现一个a
,就不再往下匹配了。
除了非贪婪模式的加号(+?
),还有非贪婪模式的星号(*?
)和非贪婪模式的问号(??
)。
+?
:表示某个模式出现1次或多次,匹配时采用非贪婪模式。*?
:表示某个模式出现0次或多次,匹配时采用非贪婪模式。??
:表格某个模式出现0次或1次,匹配时采用非贪婪模式。
'abb'.match(/ab*/) // ["abb"]
'abb'.match(/ab*?/) // ["a"]
'abb'.match(/ab?/) // ["ab"]
'abb'.match(/ab??/) // ["a"]
/ab*/
表示如果a
后面有多个b
,那么匹配尽可能多的b
;/ab*?/
表示匹配尽可能少的b
,也就是0个b
。
修饰符
修饰符(modifier)表示模式的附加规则,放在正则模式的最尾部。
修饰符可以单个使用,也可以多个一起使用。
// 单个修饰符
let regex = /test/i;
// 多个修饰符
let regex = /test/ig;
(1)g 修饰符
默认情况下,第一次匹配成功后,正则对象就停止向下匹配了。g
修饰符表示全局匹配(global),加上它以后,正则对象将匹配全部符合条件的结果,主要用于搜索和替换。
let regex = /b/;
let str = 'abba';
regex.test(str); // true
regex.test(str); // true
regex.test(str); // true
(2)i 修饰符
默认情况下,正则对象区分字母的大小写,加上i
修饰符以后表示忽略大小写
/abc/.test('ABC') // false
/abc/i.test('ABC') // true
加了i
修饰符以后,不考虑大小写,所以模式abc
匹配字符串ABC
。
(3)m 修饰符
m
修饰符表示多行模式(multiline),会修改^
和$
的行为。默认情况下(即不加m
修饰符时),^
和$
匹配字符串的开始处和结尾处,加上m
修饰符以后,^
和$
还会匹配行首和行尾,即^
和$
会识别换行符(\n
)。
/world$/.test('hello world\n') // false
/world$/m.test('hello world\n') // true
字符串结尾处有一个换行符。如果不加m
修饰符,匹配不成功,因为字符串的结尾不是world
;加上以后,$
可以匹配行尾。
/^b/m.test('a\nb') // true
上面代码要求匹配行首的b
,如果不加m
修饰符,就相当于b
只能处在字符串的开始处。加上m
修饰符以后,换行符\n
也会被认为是一行的开始。
组匹配
(1)概述
正则表达式的括号表示分组匹配,括号中的模式可以用来匹配分组的内容。
/fred+/.test('fredd') // true
/(fred)+/.test('fredfred') // true
第一个模式没有括号,结果+
只表示重复字母d
,第二个模式有括号,结果+
就表示匹配fred
这个词。
下面是另外一个分组捕获的例子。
let m = 'abcabc'.match(/(.)b(.)/);
m
// ['abc', 'a', 'c']
正则表达式/(.)b(.)/
一共使用两个括号,第一个括号捕获a
,第二个括号捕获c
。
注意,使用组匹配时,不宜同时使用g
修饰符,否则match
方法不会捕获分组的内容。
let m = 'abcabc'.match(/(.)b(.)/g);
m // ['abc', 'abc']
(2)非捕获组
(?:x)
称为非捕获组(Non-capturing group),表示不返回该组匹配的内容,即匹配的结果中不计入这个括号。
非捕获组的作用请考虑这样一个场景,假定需要匹配foo
或者foofoo
,正则表达式就应该写成/(foo){1, 2}/
,但是这样会占用一个组匹配。这时,就可以使用非捕获组,将正则表达式改为/(?:foo){1, 2}/
,它的作用与前一个正则是一样的,但是不会单独输出括号内部的内容。
let m = 'abc'.match(/(?:.)b(.)/);
m // ["abc", "c"]
一共使用了两个括号。其中第一个括号是非捕获组,所以最后返回的结果中没有第一个括号,只有第二个括号匹配的内容。
// 正常匹配
let url = /(http|ftp):\/\/([^/\r\n]+)(\/[^\r\n]*)?/;
url.exec('http://google.com/');
// ["http://google.com/", "http", "google.com", "/"]
// 非捕获组匹配
let url = /(?:http|ftp):\/\/([^/\r\n]+)(\/[^\r\n]*)?/;
url.exec('http://google.com/');
// ["http://google.com/", "google.com", "/"]
前一个正则表达式是正常匹配,第一个括号返回网络协议;后一个正则表达式是非捕获匹配,返回结果中不包括网络协议。
(3)先行断言
x(?=y)
称为先行断言(Positive look-ahead),x
只有在y
前面才匹配,y
不会被计入返回结果。比如,要匹配后面跟着百分号的数字,可以写成/\d+(?=%)/
。
“先行断言”中,括号里的部分是不会返回的。
let m = 'abc'.match(/b(?=c)/);
m // ["b"]
b
在c
前面所以被匹配,但是括号对应的c
不会被返回。
(4)先行否定断言
x(?!y)
称为先行否定断言(Negative look-ahead),x
只有不在y
前面才匹配,y
不会被计入返回结果。比如,要匹配后面跟的不是百分号的数字,就要写成/\d+(?!%)/
/\d+(?!\.)/.exec('3.14')
// ["14"]
正则表达式指定,只有不在小数点前面的数字才会被匹配,因此返回的结果就是14
。
“先行否定断言”中,括号里的部分是不会返回的。
let m = 'abd'.match(/b(?!c)/);
m // ['b']
上面的代码使用了先行否定断言,b
不在c
前面所以被匹配,而且括号对应的d
不会被返回。
6.JSON 对象
JSON 对值的类型和格式有严格的规定。
-
复合类型的值只能是数组或对象,不能是函数、正则表达式对象、日期对象。
-
原始类型的值只有四种:字符串、数值(必须以十进制表示)、布尔值和
null
(不能使用NaN
,Infinity
,-Infinity
和undefined
)。 -
字符串必须使用双引号表示,不能使用单引号。
-
对象的键名必须放在双引号里面。
-
数组或对象最后一个成员的后面,不能加逗号。
以下都是合法的 JSON。
["one", "two", "three"]
{ "one": 1, "two": 2, "three": 3 }
{"names": ["张三", "李四"] }
[ { "name": "张三"}, {"name": "李四"} ]
以下都是不合法的 JSON。
{ name: "张三", 'age': 32 } // 属性名必须使用双引号
[32, 64, 128, 0xFFF] // 不能使用十六进制值
{ "name": "张三", "age": undefined } // 不能使用 undefined
{ "name": "张三",
"birthday": new Date('Fri, 26 Aug 2011 07:13:10 GMT'),
"getName": function () {
return this.name;
}
} // 属性值不能使用函数和日期对象
注意,null
、空数组和空对象都是合法的 JSON 值。
JSON
对象是 JavaScript 的原生对象,用来处理 JSON 格式数据。它有两个静态方法:JSON.stringify()
和JSON.parse()
。
JSON.stringify()
JSON.stringify()
方法用于将一个值转为 JSON 字符串。该字符串符合 JSON 格式,并且可以被JSON.parse()
方法还原。
JSON.stringify('abc') // ""abc""
JSON.stringify(1) // "1"
JSON.stringify(false) // "false"
JSON.stringify([]) // "[]"
JSON.stringify({}) // "{}"
JSON.stringify([1, "false", false])
// '[1,"false",false]'
JSON.stringify({ name: "张三" })
// '{"name":"张三"}'
注意,对于原始类型的字符串,转换结果会带双引号。
JSON.stringify('foo') === "foo" // false
JSON.stringify('foo') === "\"foo\"" // true
字符串foo
,被转成了"\"foo\""
。这是因为将来还原的时候,内层双引号可以让 JavaScript 引擎知道,这是一个字符串,而不是其他类型的值。
JSON.stringify(false) // "false"
JSON.stringify('false') // "\"false\""
如果对象的属性是undefined
、函数或 XML 对象,该属性会被JSON.stringify()
过滤。
let obj = {
a: undefined,
b: function () {}
};
JSON.stringify(obj) // "{}"
第二个参数
JSON.stringify()
方法还可以接受一个数组,作为第二个参数,指定参数对象的哪些属性需要转成字符串。
let obj = {
'prop1': 'value1',
'prop2': 'value2',
'prop3': 'value3'
};
let selectedProperties = ['prop1', 'prop2'];
JSON.stringify(obj, selectedProperties)
// "{"prop1":"value1","prop2":"value2"}"
JSON.stringify()
方法的第二个参数指定,只转prop1
和prop2
两个属性。
这个类似白名单的数组,只对对象的属性有效,对数组无效。
JSON.stringify(['a', 'b'], ['0'])
// "["a","b"]"
JSON.stringify({0: 'a', 1: 'b'}, ['0'])
// "{"0":"a"}"
上面代码中,第二个参数指定 JSON 格式只转0
号属性,实际上对数组是无效的,只对对象有效。
第二个参数还可以是一个函数,用来更改JSON.stringify()
的返回值
function f(key, value) {
if (typeof value === "number") {
value = 2 * value;
}
return value;
}
JSON.stringify({ a: 1, b: 2 }, f)
// '{"a": 2,"b": 4}'
第三个参数
JSON.stringify()
还可以接受第三个参数,用于增加返回的 JSON 字符串的可读性。
默认返回的是单行字符串,对于大型的 JSON 对象,可读性非常差。第三个参数使得每个属性单独占据一行,并且将每个属性前面添加指定的前缀(不超过10个字符)。
// 默认输出
JSON.stringify({ p1: 1, p2: 2 })
// JSON.stringify({ p1: 1, p2: 2 })
// 分行输出
JSON.stringify({ p1: 1, p2: 2 }, null, '\t')
// {
// "p1": 1,
// "p2": 2
// }
上面例子中,第三个属性\t
在每个属性前面添加一个制表符,然后分行显示。
第三个属性如果是一个数字,则表示每个属性前面添加的空格(最多不超过10个)。
JSON.stringify({ p1: 1, p2: 2 }, null, 2);
/*
"{
"p1": 1,
"p2": 2
}"
*/
参数对象的 toJSON() 方法
如果参数对象有自定义的toJSON()
方法,那么JSON.stringify()
会使用这个方法的返回值作为参数,而忽略原对象的其他属性。
下面是一个普通的对象。
let user = {
firstName: '三',
lastName: '张',
get fullName(){
return this.lastName + this.firstName;
}
};
JSON.stringify(user)
// "{"firstName":"三","lastName":"张","fullName":"张三"}"
现在,为这个对象加上toJSON()
方法。
let user = {
firstName: '三',
lastName: '张',
get fullName(){
return this.lastName + this.firstName;
},
toJSON: function () {
return {
name: this.lastName + this.firstName
};
}
};
JSON.stringify(user)
// "{"name":"张三"}"
JSON.stringify()
发现参数对象有toJSON()
方法,就直接使用这个方法的返回值作为参数,而忽略原对象的其他参数。
JSON.parse()
JSON.parse()
方法用于将 JSON 字符串转换成对应的值。
JSON.parse('{}') // {}
JSON.parse('true') // true
JSON.parse('"foo"') // "foo"
JSON.parse('[1, 5, "false"]') // [1, 5, "false"]
JSON.parse('null') // null
var o = JSON.parse('{"name": "张三"}');
o.name // 张三
JSON.parse()
方法可以接受一个处理函数,作为第二个参数,用法与JSON.stringify()
方法类似。
function f(key, value) {
if (key === 'a') {
return value + 10;
}
return value;
}
JSON.parse('{"a": 1, "b": 2}', f)
// {a: 11, b: 2}