【译】《Understanding ECMAScript6》- 第贰章-基础知识(1)

正则表明式的u标志

多多字符串操作必要正则表达式协助实现。前文提到,正则表明式也是在“每一种字符由单个1陆-bit编码单元构成”的前提下职业,那也是前文例子中相称单个字符的正则表明式不能够合作给定字符的原由。为化解这几个主题素材,ES陆在正则表达式中新添了u标明来拍卖Unicode。

带有u标明的正则表明式将依据字符相配,而不是基于编码单元。那种方式下,相称带有代理编码对的字符将会回来正确的预料结果。如下:

var text = "𠮷";

console.log(text.length);           // 2
console.log(/^.$/.test(text));      // false
console.log(/^.$/u.test(text));     // true

上例中包括u标记的相配单字符正则表明式再次来到了不易的结果。不幸的是,ES陆未有提供检验字符对应编码单元个数的方法,可是,大家能够用饱含u申明的正则表明式消除这一个主题材料:

function codePointLength(text) {
    var result = text.match(/[\s\S]/gu);
    return result ? result.length : 0;
}

console.log(codePointLength("abc"));    // 3
console.log(codePointLength("𠮷bc"));   // 3

上例中的正则表明式全局相配包含空格在内的具有字符,并且支持Unicode。result是三个含有全体相配结果的数组,它的length也正是给定字符串的编码单元个数。

固然上述方案得以消除须要,但实施功用并不高,尤其是对长字符串的处理。所以,请尽量减弱编码单元个数的检查测试。希望ES七能够带给我们得到编码单元个数越发使得的不二等秘书籍。

因为u申明的行使涉及语法的改观,所以在不协作的JavaScript运营条件中会抛出语法错误。能够接纳以下方法检验运营环境是不是支持u标志:

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

上述函数用RegExp构造函数注脚正则表明式,并且将u作为参数字传送入。这种语法包容低版本的JavaScript引擎,如若构造函数不支持u,将会抛错。

只要您的代码运维在低版本的JavaScript引擎,提议用RegExp构造函数来探测u标识的包容性。那种办法能够使得的检测,并且能够制止因语法错误导致的实行中断。

越来越多字符串相关退换

JavaScript对字符串处理的完备性完全比不上别的编制程序语言。直到ES5才引进了trim()艺术,ES陆在字符串处理上也增加了无数新章程。

目录:

ES陆在ES伍的基本功上做了大批量的改换,有1些较大的退换涉及到新的数据类型和语法,也有部分对语言原有功效做的壹对较小的改进。本章主要介绍那一个细节的退换,那个改动在料定程度上令有些现成难点得到较好的改革。

repeat()

ES6新增的repeat()办法接受一个代表重复次数的参数n,再次来到值是将给定字符串重复n次的新字符串。如下:

console.log("x".repeat(3));         // "xxx"
console.log("hello".repeat(2));     // "hellohello"
console.log("abc".repeat(4));       // "abcabcabcabc"

repeat()办法在有些场景下相当迅猛,越发是对文本的处理。一个很出色的例子,在代码格式化学工业具中拍卖缩进时,如下:

// indent using a specified number of spaces
var indent = " ".repeat(size),
    indentLevel = 0;

// whenever you increase the indent
var newIndent = indent.repeat(++indentLevel);

正则表明式的y标识

y标记开头作为正则表达式的一个扩张属性被Firefox完结,随后ES6将其条件。带有y标(也称为粘性标识)志的正则表明式从lastIndex属性钦赐的职分上马相配,若是此任务并未有科学相称的字符,正则表明式将结束对后边内容的合作。如下:

var text = "hello1 hello2 hello3",
    pattern = /hello\d\s?/,
    result = pattern.exec(text),
    globalPattern = /hello\d\s?/g,
    globalResult = globalPattern.exec(text),
    stickyPattern = /hello\d\s?/y,
    stickyResult = stickyPattern.exec(text);

console.log(result[0]);         // "hello1 "
console.log(globalResult[0]);   // "hello1 "
console.log(stickyResult[0]);   // "hello1 "

pattern.lastIndex = 1;
globalPattern.lastIndex = 1;
stickyPattern.lastIndex = 1;

result = pattern.exec(text);
globalResult = globalPattern.exec(text);
stickyResult = stickyPattern.exec(text);

console.log(result[0]);         // "hello1 "
console.log(globalResult[0]);   // "hello2 "
console.log(stickyResult[0]);   // Error! stickyResult is null

上例中的七个正则表达式多少个带有y注明,贰个包涵g注脚,另1个不包罗其余标志。第一次开始展览的三遍相称都回去了平等的结果hello1(请小心末尾的空格)。然后将七个正则表明式的lastIndex性子都设置为1,成效是令3者从字符串的第2字符起先相称。不分包别的标记的表达式pattern从不受影响,如故非常到了hello1;带有g表明的表明式globalPattern同盟到了hello2,因为它从第一字字符e往左相配向来到结尾;而富含y标记的表达式stickyPattern的万分结果为null,那是因为第叁个字符e不相符匹配内容,粘性正则表明式立刻结束了承继内容的相配。

与全局g表明的条条框框平等,粘性标记y在做到二遍相配之后,会将lastIndex安装为此番相配字符串最终三个字符的索引值加一。如若此次相称无对应结果,lastIndex值将被初叶化为0。如下:

var text = "hello1 hello2 hello3",
    pattern = /hello\d\s?/,
    result = pattern.exec(text),
    globalPattern = /hello\d\s?/g,
    globalResult = globalPattern.exec(text),
    stickyPattern = /hello\d\s?/y,
    stickyResult = stickyPattern.exec(text);

console.log(result[0]);         // "hello1 "
console.log(globalResult[0]);   // "hello1 "
console.log(stickyResult[0]);   // "hello1 "

console.log(pattern.lastIndex);         // 0
console.log(globalPattern.lastIndex);   // 7
console.log(stickyPattern.lastIndex);   // 7

result = pattern.exec(text);
globalResult = globalPattern.exec(text);
stickyResult = stickyPattern.exec(text);

console.log(result[0]);         // "hello1 "
console.log(globalResult[0]);   // "hello2 "
console.log(stickyResult[0]);   // "hello2 "

console.log(pattern.lastIndex);         // 0
console.log(globalPattern.lastIndex);   // 14
console.log(stickyPattern.lastIndex);   // 14

上例中,粘性正则和全局正则第2遍exec()相称之后lastIndex值变为柒,第一遍相称后变为14。

粘性标识还有以下细节须要专注:

  1. 只有正则表达式本人的函数(比如exec()test())才会对粘性正则表明式的lastIndex发出震慑。假如正则表明式作为字符串函数(比如match())的参数,粘性正则表明式的lastIndex值不会受影响;
  2. 如果用^相配字符串的发轫字符,粘性正则表明式会从字符串的早先字符,恐怕多创作本第①行的开首字符开端相称。只要lastIndex为0,粘性正则表明式和常规的正则表达式行为完全一致。然则1旦lastIndex不为0,粘性正则表明式将不会实行相称。

同其余标记同样,你能够用二本品质判别正则表明式是或不是包涵y标志。如果有,sticky将为true,否则为false。如下:

var pattern = /hello\d/y;

console.log(pattern.sticky);    // true

sticky为只读属性。

同上文提到的u标惠氏样,y标明涉及语法的改变,所以在低版本JavaScript引擎下会报错。你能够用来u标明类似的方法开始展览容错处理:

function hasRegExpY() {
    try {
        var pattern = new RegExp(".", "y");
        return true;
    } catch (ex) {
        return false;
    }
}

RegExp变迁正则表明式可防止止低版本JavaScript引擎的语法错误。

克隆正则表明式

ES5同意将正则表达式作为参数字传送入RegExp,以此情势来克隆一个正则说明式,如下:

var re1 = /ab/i,
    re2 = new RegExp(re1);

可是,即使设置RegExp的第叁个参数(代表正则表明式类型),ES5中将会报错:

var re1 = /ab/i,

    // throws an error in ES5, okay in ES6
    re2 = new RegExp(re1, "g");

ES5中,如果RegExp首先个参数是正则表明式,设置首个参数会报错。ES陆更始了那种规则,第3个参数能够被安装,并且会覆盖掉第三个参数正则的其它标识。如下:

var re1 = /ab/i,

    // throws an error in ES5, okay in ES6
    re2 = new RegExp(re1, "g");


console.log(re1.toString());            // "/ab/i"
console.log(re2.toString());            // "/ab/g"

console.log(re1.test("ab"));            // true
console.log(re2.test("ab"));            // true

console.log(re1.test("AB"));            // true
console.log(re2.test("AB"));            // false

上例中,re1含有大小写不敏感标记ire2只包涵全局标记gRegExp构造函数克隆了re1并且用g覆盖了i。假设不安装第一个参数,re2将会和re1含有一样的标志。

normalize()函数

Unicode另三个有趣的上边是,有些分化的字符在进展排序或一些基于相比的操作中得以被感到是等价的。有二种方法来定义那个涉及。第①种,标准等价是指七个码点系列在具有方面都被感觉是可沟通的,甚至多个字符的组合也得以被正式等价为贰个字符。第三种,兼容性是指八个码点种类即使映射为泾渭明显分化的八个字符,不过在少数场景下得以交换。

是因为那种涉及的存在,八个完全分化的码点体系或者被映射为一个等同的字符串。比如,字符"æ"和字符串"ae"固然由不一致的码点连串组成,但两者在某个场景下能够相互取代。可是若是不将其基准,那三个字符串在JavaScript中是一点壹滴不等的。

ES6扶助字符串通过normalize()函数进行Unicode标准化。normalize()函数接收一个可选参数,代表Unicode标准化格式,参数值可选:NFC(默认值)、NFDNFKCNFKD。感兴趣的读者能够自行查阅相关知识。

亟需小心的是,在对字符串进行比较以前,要求将它们整个条件。如下:

var normalized = values.map(function(text) {
    return text.normalize();
});

normalized.sort(function(first, second) {
    if (first < second) {
        return -1;
    } else if (first === second) {
        return 0;
    } else {
        return 1;
    }
});

上述代码中,values数组中的全部字符串被转化为标准格式,以便数组的没有错排序。你也得以再比较函数内部采纳normalize()函数:

values.sort(function(first, second) {
    var firstNormalized = first.normalize(),
        secondNormalized = second.normalize();

    if (firstNormalized < secondNormalized) {
        return -1;
    } else if (firstNormalized === secondNormalized) {
        return 0;
    } else {
        return 1;
    }
});

除此以外部要求要小心的是,全体的字符串必须被行业内部成为同壹种格式。上例中接纳的暗中同意值NFC,你也能够动用其余格式:

values.sort(function(first, second) {
    var firstNormalized = first.normalize("NFD"),
        secondNormalized = second.normalize("NFD");

    if (firstNormalized < secondNormalized) {
        return -1;
    } else if (firstNormalized === secondNormalized) {
        return 0;
    } else {
        return 1;
    }
});

一经你的办事中不关乎Unicode标准化难点,normalize()函数并无法给你提供扶助。然则通晓它的留存和效率,当你蒙受相关主题素材时将会那二个有效。

flags属性

ES第55中学,能够经过source本性获取正则表明式的文件部分(即除标志以外的壹对),但是要想赢得标志一部分就须要将正则表明式转化为字符串再做如下处理:

function getFlags(re) {
    var text = re.toString();
    return text.substring(text.lastIndexOf("/") + 1, text.length);
}

// toString() is "/ab/g"
var re = /ab/g;

console.log(getFlags(re));          // "g"

ES6在保留source本性的还要,新扩展了flags质量,两者都是原型链的只读属性。flags属性可以令正则表达式的操作更为密切。

flags属性以字符串的形式再次来到应用张永琛则表明式的兼具标记。如下:

var re = /ab/g;

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

通过sourceflags质量,能够直接领取正则表明式的别样碎片,而不必然正则表达式调换为字符串操作。

用转义连串对Non-BMP字符编码

ES5允许由转义体系意味着1陆-bit的字符。转义连串是由\u与多个十6进制值组成。比如转义种类\u0061表示字符a

console.log("\u0061");      // "a"

假如转义体系的十六进制值抢先了FFFF,它对应的字符就不止了BMP字符的上限,然后你会收获部分匪夷所思的结果:

console.log("\u20BB7");     // "₻7"

鉴于Unicode转义类别被严酷定义为只好分包6个十陆进制值,ECMAScript在拍卖\u20BB7时将其正是八个字符:\u20BB7。第三个字符是不可打字与印刷的,第壹个字符时数字7.

为弥补上述缺陷,ES陆引进了恢宏Unicode转义类别。扩张转义类别包罗在花括号内,能够收到任意个数(理论上不当先几个)的十六进制值来表示2个字符:

console.log("\u{20BB7}");     // "𠮷"

上例中,利用扩大转义体系获取到了天经地义的字符。

上述措施只好运作在支撑ES陆的条件下,其他环境会报语法错误。能够通过以下函数推断运转条件是或不是协理扩充转义字符:

function supportsExtendedEscape() {
 try {
     "\u{00FF1}";
     return true;
 } catch (ex) {
     return false;
 }
}

Unicode标识符

ES六对Unicode的杰出帮助代表当Unicode作为标识符表明变量时部分用法的改观。ES五已经允许Unicode转义种类作为标志符注脚变量了,如下:

// Valid in ECMAScript 5 and 6
var \u0061 = "abc";

console.log(\u0061);        // "abc"

// equivalent to
// console.log(a);          // "abc"

ES陆中,你也能够用Unicode编码单元的转义系列作为标志符:

// Valid in ECMAScript 5 and 6
var \u{61} = "abc";

console.log(\u{61});        // "abc"

// equivalent to
// console.log(a);          // "abc"

其余,ES陆的标记符注明语法听从规范Unicode Standard Annex #31: Unicode
Identifier and Pattern Syntax
:

  1. 开局首字符必须是$_大概隐含ID_Start主导衍生属性的Unicode码;
  2. 首字符以外的各种字符必须是$_\u200c(ZWNJ)、\u200d(ZWJ)可能隐含ID_Continue中央衍生属性的Unicode码。

ID_StartID_Continue的中坚衍生属性由Unicode Identifier and Pattern
Syntax
规定,以便Unicode标记符作为变量名和域名使用(此标准并不仅只限于JavaScript)。

更加好的Unicode编码扶助

在ES6以前,JavaScript字符串完全依照1六-bit的编码理念。全数字符串的习性和章程,比如lengthcharAt(),都是创立在“每16-bit种类代表2个字符”的前提之下。ES5帮忙二种编码格式:UCS-二和UTF-1陆(两者均采用1陆-bit为编码单元(code
units),获得的结果一律)。即便于今结束全体的字符都足以用15个人代表,但那种范围将不会保持太久了。

几次三番行使十2个人最为编码单元并不能够促成Unicode的“为世界上存有字符提供全局唯①标志符”的对象。那么些号称码点(code
points)
的大局唯1标志符是三个从0初始的数字(你恐怕将其精晓为字符编码,但实在是有细微差别的)。字符编码的成效是将码点编码为在这之中一致的编码单元。UCS-二编码对码点和编码单元做1对1映射,UTF-16则有越来越多也许性。

UTF-1陆编码中,开首的二^1陆码点被展现为一个1陆-bit编码单元,那正是所谓的主题多语言面(Basic
Multilingual
Plane,简称BMP)。任谭龙出那么些界定的码点都不能够呈现为16-bit,那个码点被以为处于补充平面(supplementary
plane)中。为斩草除根那么些标题,UTF-1六引进了代办编码对(surrogate
pairs)的概念,用多少个1六-bit的编码单元表示3个码点。也正是说,字符串中的二个字符要么是多个1陆-bit编码单元(处于BMP中,占位16bits),要么是七个编码单元(处于补充平面,占位3二 bits)。

ES伍规定字符串的有所操作遵从1陆-bit编码单元规则,所以借使您操作的字符包括代理编码对,将会收获出人意料的结果。如下:

var text = "𠮷";

console.log(text.length);           // 2
console.log(/^.$/.test(text));      // false
console.log(text.charAt(0));        // ""
console.log(text.charAt(1));        // ""
console.log(text.charCodeAt(0));    // 55362
console.log(text.charCodeAt(1));    // 57271

上例中,八个Unicode字符包罗代理编码对,JavaScript将其视为多少个1陆-bit编码单元。所以,其length值为二,匹配单字符的正则表明式重临falsecharAt()办法不能够收获实惠的字符。charCodeAt()艺术再次回到每个编码单元对应的的1陆-bit数字,那已经是你能够在ECMAScript
5中拿走的最接近真实值的东西了。

ES六威胁行使UTF-1陆编码。字符编码的标准意味着JavaScript能够正确处理包涵代理编码对的字符了。

越来越多正则表达式相关更换

正则表达式是处理字符串不可缺少的1环,不过在ES六在此之前的多少个版本进级中并未有太大转移。ES6在晋级字符串操作的还要,也对正则表明式举行了考订。

codePointAt()函数

codePointAt()函数完全辅助UTF-16编码,能够啊你复苏获取给定字符的Unicode码点。codePointAt()函数接受码点地点(而不是字符地点)作为参数,并赶回二个整数值:

var text = "𠮷a";

console.log(text.charCodeAt(0));    // 55362
console.log(text.charCodeAt(1));    // 57271
console.log(text.charCodeAt(2));    // 97

console.log(text.codePointAt(0));   // 134071
console.log(text.codePointAt(1));   // 57271
console.log(text.codePointAt(2));   // 97

对此BMP字符而言,codePointAt()函数与charCodeAt()函数的回来结果完全同样。上例中,text的首先个字符𠮷不是BMP字符,它由五个编码单元构成,也正是说,textlength3而不是二。对于索引0,charCodeAt()函数只收获到第二个编码单元,而codePointAt()函数获取了组合第三个字符的具有编码单元。四个函数对索引1(第3个字符的第3个编码单元)和索引贰(第四个字符a)的演算结果一律。

利用codePointAt()函数能够1贰分方便地认清给定字符到底映射为几个要么七个码点:

function is32Bit(c) {
    return c.codePointAt(0) > 0xFFFF;
}

console.log(is32Bit("𠮷"));         // true
console.log(is32Bit("a"));          // false

过量号左侧的1六-bit字符0xFFFF表示十陆进制的FFFF,所以任何大于它的码点都由八个编码单元构成。

String.fromCodePoint()

ECMAScript在提供某种意义时,往往会同时提供与其相反的作用。比如你能够用codePointAt()函数获取给定字符的码点,同时也可用String.fromCodePoint()函数获取给定码点对应的字符。如下:

console.log(String.fromCodePoint(134071));  // "𠮷"

你能够省略的感到String.fromCodePoint()函数是巩固版的String.fromCharCode()。两者在处理BMP字符时的演算结果完全一致,区别在于对BMP范围外字符的拍卖。

`includes()`,`startsWith()`,`endsWith()`

自JavaScript面世以来,开拓者一向利用indexOf()措施处理子字符串。ES陆新增加了四个处理子字符串的法子:

  • includes()
    假如字符串中蕴藏给定的子字符串,重临true,不然再次回到false
  • startsWith()
    倘诺给定的子字符串位于字符串的开局地方,再次来到true,不然重临false
  • endsWith()
    假如给定的子字符串位于字符串的最终,重临true,不然再次回到false

上述八个方式均可承受多少个参数:待检索的子字符串(必选)以及待检索父字符串的序幕检索地点(可选,默以为0)。借使传入了首个参数,includes()startsWith()主意将搜索父字符串自给定地点之后的始末,而endsWith()则将追寻父字符串自检索地点以前的内容。也正是说,第三个参数缩短了父字符串的索求范围。如下:

var msg = "Hello world!";

console.log(msg.startsWith("Hello"));       // true
console.log(msg.endsWith("!"));             // true
console.log(msg.includes("o"));             // true

console.log(msg.startsWith("o"));           // false
console.log(msg.endsWith("world!"));        // true
console.log(msg.includes("x"));             // false

console.log(msg.startsWith("o", 4));        // true
console.log(msg.endsWith("o", 8));          // true
console.log(msg.includes("o", 8));          // false

如上多少个主意能够更实惠的处理子字符串难题,而不要关注它们的现实性索引地方。

上述五个方法均重返叁个Boolean值,假若你的想要获取子字符串的目录地点,请使用indexOflastIndexOf()

比方将正则表明式作为参数字传送入includes()startsWith()endsWith()将会报错,这一点与indexOflastIndexOf()不等,后两者会将正则表明式转化为字符串后张开处理。

Object.is()

JavaScript开荒者习惯于选择双等操作符==要么严刻相等操作符===对多少个值实行比较。大多数人倾向于采纳后者以幸免相比进度中的强制类型转换。但是,尽管是严苛相等操作符也并不是全然规范。比如+0-0在JavaScript中是一心两样的多少个值,不过用===正如时会以为双方是相等的。其它,NaN === NaN的运作结果是false,这也是isNaN()函数不能缺少的来头之一。

为弥补===的缺陷,ES6新增了Object.is()函数。它接受多个参数,即使八个参数是等价的就回来true。这里的等价意味着比较两者的数据类型和值完全相等。大繁多景观下,Object.is()函数与===的运算结果同样,唯一的分别是Object.is()函数认为+0-0是不等价的,并且NaN等价于NaN。如下:

console.log(+0 == -0);              // true
console.log(+0 === -0);             // true
console.log(Object.is(+0, -0));     // false

console.log(NaN == NaN);            // false
console.log(NaN === NaN);           // false
console.log(Object.is(NaN, NaN));   // true

console.log(5 == 5);                // true
console.log(5 == "5");              // true
console.log(5 === 5);               // true
console.log(5 === "5");             // false
console.log(Object.is(5, 5));       // true
console.log(Object.is(5, "5"));     // false

当然,=====能够知足绝大很多的使用场景,假使有上述提到的两种13分情况,Object.is()函数会提升代码的逻辑性。