当ES6遇上 字符串和正则表达式

3,940 阅读8分钟

写在前面

字符串是一种重要的数据类型,而正则表达式则是赋予了编程人员更多操作字符串的能力。ES6的创作者为字符串和正则表达式添加了许多的新功能。下面玲珑将来进行一个全面的总结。

字符串和正则两个部分各两节,全文阅读时间大约10分钟

字符串

1.更好的Unicode支持

Unicode是一个字符集。将全世界所有的字符包含在一个集合里,计算机只要支持这一个字符集,就能显示所有的字符,再也不会有乱码了。

在es6出现之前,js字符串一直基于16位字符编码进行构建。每16位序列是一个编码单元,代表一个字符,Unicode0引入扩展字符集,16位字符编码将不再包含任何字符。编码规则也因此变更。

对于UTF-16来说,码位可以有多个编码单元表示,是表示不是组成。

对于UTF-16前2^16个码位都是16位的编码单元表示,这个范围被称作基本多文种平面BMP,当超过时,引入代理对,规定用两个16位编码单元表示一个码位,即32位辅助平面字符。一个32位代理对表示字符长度为1,但是length属性值为2.

如果想进一步了解他可以参考阮一峰的一片日志:www.ruanyifeng.com/blog/2014/1… 日志里面说到的码点就是码位

1.1codePointAt(0)方法

在es6之前,charCodeAt()方法返回字符每个16位编码单元对应的数值,再在es6中新增了codePointAt方法,codePointAt(0)返回的是位置0处的码点或者说是位置0处的码位,包含多个编码单元>16进制上限FFFF,charCodeAt(0)方法返回的是位置0的第一个编码单元.

因此可以用此方法判断一个字符占用编码单元的数量

function is32Bit(c) {
    
    return c.codePointAt(0) > 0xFFFF;
}
console.log(is32Bit("吉利"));  //true
console.log(is32Bit("a"));    //false

1.2 String.fromCodePoint()方法

codePointAt()方法在字符串中检索一个字符串的码位,也可以使用String.fromCodePoint()方法根据指定的码位生成一个字

console.log(String.fromCodePoint(134071));   //吉

1.3normalize()方法

在比较字符或者进行排序的时候,可能出现等价的情况,然而等价却存在两种情况

  • 规范的等效是无论从哪个角度来看,两个序列的码位都是没有区别的
  • 兼容的码位序列看起来不同,但是在特定的情况下可以交换使用。 但是在严格模式下不是等效的,除非通过某些方法把这种等效关系标准化

normalize()方法提供Unicode的标准化形式,,这个方法可以接受一个可选的字符串参数。Unicode标准化形式有四种

  • 以标准等价方式分解,然后以标准等价方式重组(“NFC”),默认值选项
  • 以标准等价方式分解(“NFD”)
  • 以兼容等价方式分解(“NFKC”)
  • 以兼容方式分解,然后以标准等价方式重组

1.4 正则表达式u修饰符

正则表达式后添加u修饰符会将编码单元模式切换成字符模式,这个时候的代理对不会被视为两个字符。

但是length这个属性返回的仍然是字符串编码单元的数量,而不是码位的数量。但是也可以通过带u修饰符的正则表达式来解决这个问题。

function codePointerLength(text) {
    let result = text.match(/[\s\S]/gu);
    return result ? result.length:0;
}
console.log(codePointerLength("吉abc")); //4

检测是否支持u修饰符

u修饰符在不兼容ES6的JavaScript引擎中使用会导致语法错误,可以通过以下函数检测是否支持。

function hasRegExpU() {
    try{
        var pattern = new Regexp(".","u");
        return ture;
    }catch (ex) {
        return false;
    }
}

2.其他字符串的变更

2.1字符串中的字串识别

开发者们用indexOf()方法在一段字符串中检测另一段子字符串。在es6中提供3个方法达到类似的效果

  • startWith()方法,在字符串起始部分检测指定文本返回true,否则返回false。
  • incledes()方法,如果在字符串中检测到指定文本返回true,否则返回false。
  • endWith()方法,顾名思义在末尾检测,用法与上面一致。

上面三个方法接受两个参数,第1个参数是指定要搜索的文本是一个字符。第2个是开始搜索位置的索引值是一个数字。不指定第二个参数endwith一般从字符串末尾处开始匹配。示范如下

let mes = "hello world";

console.log(mes.startWith("hello"));
console.log(mes.endWith("!"));
console.log(mes.includes("o"));

console.log(mes.startWith("o"));
console.log(mes.endWith("d!"));
console.log(mes.includes("x"));

console.log(mes.startWith("o",4));
console.log(mes.endWith("o",8));
console.log(mes.includes("o",8));
//9个结果依次为:true true true   false true false    true true false

console.log(mes.endWith("o",8));会从第7位第二个o开始匹配。索引值-要搜索文本的长度=8-1

2.2 repeat()方法

es6为字符串新增加的repeat()方法,接受一个number类型的参数,返回重复该次数的一个新字符串。

console.log(x.repeat(3)); //"xxx"


我是2条分割线,嘎嘎嘎

正则表达式

1.其他正则表达式的变更

1.1正则表达式y修饰符

y修饰符粘滞正则表达式,从正则表达式的lastIndex属性开始进行。如果指定位置没有匹配成功那么将停止匹配并返回结果。

let text =  'hello1 hello2 hello3';
let patt = /hello\d\s?/,
    result = patt.exec(text);
let gPatt = /helllo\d\s?/g,
    gResult = gPatt.exec(text);
let yPatt = /hello\d\s?/y,
    yResult = yPatt.exec(text);
    
console.log(resut[0]);   //"hello1 "
console.log(gResut[0]);   //"hello1 "
console.log(yResut[0]);   //"hello1 "

patt.lastIndex = 1;
gPatt.lastIndex = 1;
yPatt.lastIndex = 1;

result = patt.exec(text);
gResult = gPatt.exec(text);
yResult = yPatt.exec(text);

console.log(resut[0]);   //"hello1 "
console.log(gResut[0]);   //"hello2 "
console.log(yResut[0]);   //抛出错误

这里三个正则表达式中,第一个没有修饰符,第二个全局修饰符g,第三个用了y修饰符。

第一次匹配的时候是从h字符开始匹配。当lastIndex = 1;之后,对于没有修饰符的表达式自动忽略这个变化,结果还是hello1 ,g修饰符会从e字符开始匹配,输出hello2 ,yResul会从e字符开始匹配,ello h与之不等,最后结果为null,所以会抛出错误。

当执行y修饰符之后,会把上一次匹配最后的一个字符的后一位索引值保存在lastIndex中去,如果执行y修饰符匹配结果为空那么lastIndex值会被重置为0,g修饰符与此相同。

只有调用正则表达式对象的exec()和test()这些方法时才会设计lastIndex属性,如调用字符串的方法natch()就不会触发粘滞行为。

检测y修饰符是否存在可以用sticky属性,如果js引擎支持粘滞修饰符,则stickey属性值为true,否则为false

let patt = /hello\d/y;
console.log(patt.sticky);

1.2正则表达式的复制

在es5中,可以给正则表达式的构造函数传递正则表达式作为参数来复制这个正则表达式。但事实第一个参数为正则表达式的时候不能用第二个参数,es6中修改了这个行为,第二个参数可以是修饰符。

let re1 = /ab/i;
let re2 = new RegExp(re1,"g");
console.log(re1.toString());  // "/ab/i"
console.log(re2.toString());  // "/ab/g"

1.3flags属性

es6新增的flags属性会返回所有应用于当前正则表达式的修饰符

let re = /ab/g;
console.log(re.source);   //"ab"
console.log(re.flags);    //"g"

2.模板字面量

2.1基础语法

一句话概括,用反撇号(`)代替了双引号,单引号。

如果想在字符串中用反撇号,就用\转义即可。如

let message = `\`hello\`!`;
console.log(message);

结果是hello!

2.2简化的多行字符串

在es6之前,通过数组或字符串拼接的方法来创建多行字符串,es6中只需要在代码中直接换行,换行也改变length属性值。同时再反撇号中所有的空格符都属于字符串的一部分。

let message = `Multiline
string`;
console.log(message);
console.log(message.length);   //16=6+9+1

2.3 字符串占位符

在一个模板字面量中,你可以把任何合法的JavaScript表达式嵌入到占位符中并将其作为字符串的一部分输出到结果中。

占位符通常由${}组成,中间可包含任何JavaScript表达式。模板字面量本身也是JavaScript表达式,因此在一个模板字面量里面可以嵌入另外一个模板字面量。

let name = "sarah";
let message = `my${`name is${name}.`}`;
console.log(message);//my name is sarah.

message是一个模板字面量,里面又包含了name is${name}.这个模板字面量。

2.4 标签模板

在模板字面量中使用原始值

通过String.raw标签转换成原生字符串

let message = String.raw`Mul\nddd`;
console.log(message); //"Mul\\nddd"因为\n的原生是\\n.

总结

  • es6中codePointAt方法和String.fromCodePoint()方法实现码位和字符串之间的转化。

  • 在比较字符串的时候,normalize()方法可以将字符串标准化,标准化有四种形式,可以更准确地比较字符串

  • 还有新增的一些其他方法检测一个字符串里面的子串,复制字符串。

  • 在es6中的正则这一块,新加了通过构造函数来复制正则表达式的语法,正则表达式中新增的y修饰符,以及用反撇号来代替双,单引号的模板字面量。