ES6之前,JavaScript字符串是基于16位字符编码(UTF-16)进行构建。每16位序列是一个编码单元,代表一个字符。length
、charAt()
等字符串属性和方法都是基于这种编码单元构造的。
UTF-16码位
Unicode为每一个字符提供了唯一的标识符,又称为码位,它是从0开始的数值。而表示字符的这些数值或者码位,称之为字符编码。
在UTF-16中,前2^16个码位均以16位的编码单元表示,这个范围称作基本多文种平面(BMP)。超出这个范围的码位则要归属于某个辅助平面,因为用16位已经无法表示。为此,UTF-16引入了代理对,规定用两个16位编码单元表示一个码位。
所以,字符串有两种,一种是由一个编码单元16位表示的BMP字符,另一种是有两个编码单元32位表示的辅助平面字符
在ES5中,所有字符串的操作都是基于16位编码单元的BMP字符,如果直接用于32位编码单元的辅助平面字符,得到的结果可能与预期不符。
𠮷是辅助平面字符,ES5中的字符串操作方法将其视为两个16位字符。
text
的长度是1,但length
属性值为2text
被判定为两个字符,因此匹配单一字符的正则失效- 前后两个16位编码都不表示任何可打印字符,所以
charAt()
方法不会返回合法的字符串 charCodeAt()
方法同样不能正确识别字符,他会返回每个16位编码单元对应的数值
ES6新增的字符串操作方法
ES6新增的字符串方法,完全支持UTF-16(辅助平面字符)
codePointAt()方法
该方法接受编码单元(非字符位置)的位置做为参数,返回字符串中给定位置对应的码位,即一个整数值。
|
|
对于BMP字符集中的字符,codePointAt
和charCodeAt
方法返回的值是相同的。text
中第一个字符属于辅助平面字符,包含2个编码单元,length
值为3。charCodeAt
返回的只是位置0处的第一个编码单元,charPointAt
返回的是完整的码位,即使这个码位包含2个编码单元。
检测字符占用的编码单元的数量,可以用charPointAt
方法,返回值大于十六进制的上界值FFFF,则一定有两个编码单元来表示。(因为不足以表示所有字符,所以才用辅助平面字符来表示)
String.fromCodePoint()方法
String.fromCodePoint()
接收一个码位,返回一个字符
normalize()方法
Unicode中,对不同字符进行排序或比较操作,可能它们是等效的。有两种方式可以定义这种关系
- 规范的等效,两个序列的码位一致
- 兼容性,两个互相兼容的码位序列看起来不同,但是在特定的情况下可以被互相交换使用。
比如Ǒ(\u01D1),O(\u004F)和ˇ(\u030C)组合,可以互相使用,但从严格意义上来讲,它们不是等效。ES6为字符串提供了一个normalize
方法,它可以提供Unicode的标准化形式,该方法接收一个可选字符串参数。
- 以标准等价方式分解,然后以标准等价方式重组(’NFC’),默认选项
- 以标准等价方式分解(NFD)
- 以兼容等价方式分解(’NFKC’)
- 以兼容等价方式分解,然后易标准等价方式重组(’NFKD’)12console.log('\u01D1' === '\u004F\u030C'); //falseconsole.log('\u01D1'.normalize() === '\u004F\u030C'.normalize()); //true
在对比字符串之前,一定要先把它们标准化为同一格式。
将values
数组中的所有字符都转换成同一种标准格式,因此该数组可以被正确排序
sort
方法中的函数,返回值小于0,a在b前面。大于0,a在b后面。等于0,a,b相对位置不变。上面是升序
字符串中的子串识别
includes()
,字符串中检测到指定文本返回true
,否则false
startsWith()
,字符串起始部分检测到指定文本返回true
,否则false
endsWith()
,字符串结束部分检测到指定文本返回true
,否则false
以上方法,都接受两个参数,第一个参数指定要搜索的文本,第二个参数可选,指定开始搜索的位置的索引值。如果指定第二个参数,includes
和startWith
方法会从这个索引值的位置开始匹配。endsWith
方法则从字符串长度减去这个索引值的位置开始匹配。12345678let msg = 'hello world!'console.log(msg.startsWith('hello')); //trueconsole.log(msg.endsWith('!')); //trueconsole.log(msg.includes('o')); //trueconsole.log(msg.startsWith('o', 4)); //trueconsole.log(msg.endsWith('o',8)) //trueconsole.log(msg.includes('o',8)); //false
repeat()方法
重复字符串方法,接受一个number
型参数,表示重复次数
正则表达式变更
u修饰符
正则表达式默认将字符串中的每一个字符按照16位编码单元处理,为了解决这个问题,ES6新增u修饰符。当一个正则表达式添加了u修饰符时,它就从编码单元操作模式切换为字符模式。这样正则表达式就不会把辅助平面字符(代理对)为两个字符。
可以利用u修饰符,检测字符串的码位
检测u修饰符支持
这个函数使用了RegExp构造函数并传入一个修饰符u作为参数,老式浏览器支持这个语法,如果不支持u修饰符会抛出错误。这样可以避免发生语法错误。
y修饰符
y修饰符的正则表达式称为粘滞正则表达式,他会影响正则表达式搜索过程中的sticky
属性,当它在字符串中开始字符匹配时,会通知从正则表达式的lastIndex
属性开始进行。如果指定位置未能成功匹配,则停止继续匹配。
当lastIndex
属性改成1时,此时正则表达式从字符串的第二个字符开始匹配。没有修饰符的表达式自动忽略,所以第二个表达式向后匹配到了’hello2 ‘,使用了y修饰符的粘滞正则表达式,由于从第二个字符开始匹配不到相应的字符串,就此终止。所以stickyResult
为null
- 只有调用
exec()
和test()
这些正则表达式对象的方法时才会涉及lastIndex
属性,调用字符串方法如match()
不会触发粘滞行为。 - 对于粘滞正则表达式,如果使用
^
匹配字符串开端,只会从字符串的起始位置或者多行模式的首行进行匹配。如果此时lastIndex
值不为0,则永远不会匹配到结果
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/RegExp
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Regular_Expressions
正则表达式复制
|
|
在ES5环境中,RegExp
构造函数,第一个参数为正则表达式时不可以使用第二个参数,ES6中修改了这个行为
flags属性
flags
属性用来获取正则表达式的修饰符
模版字面量
ES6模版字面量语法支持创建领域专用语言(DSL),它比ES5及早起版本中的解决方案更家安全。
基础语法
|
|
模版字面量用反撇号(`)表示,如果在字符串中使用反撇号,需要用反斜杆(\)进行转义
多行字符串
在ES5中,可以利用一个语法bug,在一个新行的最前方添加反斜杆(\)可以承接上一行代码,来创造多行字符串
反斜杆在此处代表行的延续,而非真正代表新的一行,如果想要输入新的一行,需要手动加入换行符。
ES6之前版本中,通常通过数组活着字符串拼接的方式创建多行字符串
在ES6中的模版字面量,只需在代码中直接换行即可。
在反撇号中的所有空白符都输入字符串中的一部分
第二行之前的所有空白符都是字符串本身的一部分
字符串占位符
占位符由一个美元符和大括号${}
组成,中间可以包含任意的JavaScript
表达式。
第一个美元符会原样输出,因为后面没有紧跟一个左括号
模版字面量本身也是JavaScript
表达式,所以可以在一个模版中嵌入另一个
模版标签
在模版字面量第一个反撇号(`)前方标注的字符串(函数),就是标签。模版标签可以执行模版字面量上的转换,并返回最终的字符串值。
如示,应用于模版字面量的模版标签是tag
定义标签
标签是一个函数,调用时,第一个参数是一个数组,包含JavaScript
解释过后的字面量字符串(模板字符串中那些没有变量替换的部分),之后的所有参数都是每一个占位符的解释值。
标签函数通常使用不定参数特征来定义占位符来简化处理过程
示例中,tag
函数,作为一个模版字面量标签,会接受3个参数。literals
是一个数组,包含一下元素
- 第一个占位符前的空字符串(’’)
- 第一,二个占位符之间的字符串(’ items cost $’)
- 第二个占位符后的字符串(’.’)
第二个参数是变量count
的解释值,传参为10,最后一个是count * price).toFixed(2)
的解释值,传参为’2.50’
literals
里第一个元素是空字符串,确保literals[0]
是字符串始端,substitutions
的数量始终比literals
少一个,意味着substitutions.length === literals.length-1
表达式始终为true
模版标签可以到字符转义被转换成等价字符前的原生字符串,如String.raw()
标签
变量message1
中的\n
解释为一个新行,变量message2
获取的是\n
的原生形式\\n
原生字符串信息同样也传入标签模版,标签函数第一个参数,它有一个额外的属性raw
,是一个包含每一个字面量的原生等价信息的数组。如literals[0]
对应的原生字符串为literals.raw[0]