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

目录:

  • 双重好之Unicode编码支持
    • codePointAt()函数
    • String.fromCodePoint()
    • 用转义序列对Non-BMP字符编码
    • normalize()函数
    • 正则表达式的u标志
    • Unicode标识符
  • 又多字符串相关变更
    • includes(),startsWith(),endsWith()
    • repeat()
  • 重多的正则表达式改动
    • 正则表达式的y标志
    • 克隆正则表达式
    • flags属性
  • Object.is()

ES6于ES5的基本功及举行了汪洋的变更,有一部分较充分的更动涉及到新的数据类型和语法,也起部分针对语言原有功能做的片比较小之改良。本章主要介绍这些细节的转,这些改变在得水平达到使某些现存问题取得比较好之改善。

再度好的Unicode编码支持

每当ES6之前,JavaScript字符串完全依据16-bit的编码思想。所有字符串的性能和法,比如lengthcharAt(),都是白手起家以“每16-bit序列代表一个字符”的前提之下。ES5支持少数栽编码格式:UCS-2和UTF-16(两者都使用16-bit呢编码单元(code
units),得到的结果一致)。虽然至今为止所有的字符都得为此16号表示,但这种规模将未会见维持太老了。

持续利用16个最编码单元并无能够促成Unicode的“为世界上有所字符提供全局唯一标识符”的靶子。这些号称码点(code
points)
的全局唯一标识符是一个从0开始之数字(你也许用其知道吧字符编码,但事实上是发出细微差别的)。字符编码的打算是以码点编码为内部一致的编码单元。UCS-2编码对码点和编码单元开同样针对性一映射,UTF-16则发出重新多可能性。

UTF-16编码中,起始之2^16垛点于展现吧一个16-bit编码单元,这虽是所谓的基本多语言面(Basic
Multilingual
Plane,简称BMP)。任何超出这范围之码点都无可知显现呢16-bit,这些码点被认为处于上平面(supplementary
plane)中。为解决之题目,UTF-16引入了代理编码对(surrogate
pairs)的定义,用少独16-bit的编码单元代表一个码点。也就是说,字符串中的一个字符或是一个16-bit编码单元(处于BMP中,占各类16
bits),要么是有限独编码单元(处于上平面,占位32 bits)。

ES5确定字符串的保有操作以16-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将那个视为两只16-bit编码单元。所以,其length价为2,匹配单字符的正则表达式返回falsecharAt()艺术无能够获实惠的字符。charCodeAt()办法返回每个编码单元对应之的16-bit数字,这曾经是你得在ECMAScript
5蒙获取的卓绝相仿真实值的事物了。

ES6要挟行使UTF-16编码。字符编码的尺码意味着JavaScript可以正确处理包含代理编码对之字符了。

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要是休是2。对于索引0,charCodeAt()函数只收获到第一单编码单元,而codePointAt()函数获取了整合第一独字符的有所编码单元。两个函数对索引1(第一单字符的第二单编码单元)和索引2(第二只字符a)的运算结果同样。

利用codePointAt()函数可以死便宜地判断为定字符到底映射为一个或有数只码点:

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

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

出乎号右侧的16-bit字符0xFFFF意味着十六进制的FFFF,所以任何大于它的码点都出于少只编码单元构成。

String.fromCodePoint()

ECMAScript在供某种意义时,往往会又提供以及该倒的效果。比如你可以为此codePointAt()函数获取给定字符的码点,同时为可用String.fromCodePoint()函数获取给定码点对应之字符。如下:

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

若可简简单单的当String.fromCodePoint()函数是增强版的String.fromCharCode()。两者在拍卖BMP字符时的运算结果完全一致,区别在针对BMP范围外字符的拍卖。

因而转义序列对Non-BMP字符编码

ES5允许由转义序列意味着16-bit的字符。转义序列是由于\u暨四个十六迈入制值组成。比如转义序列\u0061意味着字符a

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

苟转义序列的十六上前制值超过了FFFF,它对应之字符就不止了BMP字符的上限,然后你晤面得到有匪夷所思之结果:

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

鉴于Unicode转义序列被严格定义为只能分包4单十六前进制值,ECMAScript在处理\u20BB7时拿其就是两个字符:\u20BB7。第一单字符是不足打印的,第二只字符时数字7.

为弥补上述缺陷,ES6引入了推而广之Unicode转义序列。扩展转义序列包含在花括号内,可以收到任意个数(理论及不超8个)的十六迈入制值来表示一个字符:

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

直达例被,利用扩展转义序列获取到了对的字符。

上述办法就会运作在支持ES6的条件下,其他条件会报语法错误。可以经以下函数判断运行条件是否支持扩大转义字符:

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

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()函数并无能够为您提供协助。但是了解它的留存与作用,当您相逢相关题材常常拿会大有效。

正则表达式的u标志

过多字符串操作需要正则表达式协助完成。前文提到,正则表达式也是以“每个字符由单个16-bit编码单元构成”的前提下办事,这为是前文例子中匹配配单个字符的正则表达式无法配合给定字符的因。为解决之题目,ES6在正则表达式中新增了u表明来拍卖Unicode。

带有u标明的正则表达式将根据字符匹配,而不是根据编码单元。这种模式下,匹配带有代理编码对之字符将会晤回去正确的预料结果。如下:

var text = "𠮷";

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

上例被涵盖u标志的匹配单字符正则表达式返回了无可非议的结果。不幸的是,ES6没有提供检测字符对许编码单元个数的道,但是,我们可以就此含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为即是被定字符串的编码单元个数。

尽管上述方案可以化解急需,但执行效率并无赛,尤其是对长字符串的处理。所以,请尽量减少编码单元个数的检测。希望ES7力所能及带来吃咱们沾编码单元个数更加管用的艺术。

因为u表明的用涉及语法的转,所以在不兼容的JavaScript运行环境面临见面弃来语法错误。可以行使以下办法检测运行环境是否支持u标志:

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

上述函数用RegExp构造函数声明正则表达式,并且将u当参数传入。这种语法兼容低版本的JavaScript引擎,如果构造函数不支持u,将见面抛错。

一旦您的代码运行在没有版本的JavaScript引擎,建议用RegExp构造函数来探测u标明的兼容性。这种艺术好有效之检测,并且会避免因语法错误导致的实行中断。

Unicode标识符

ES6针对Unicode的美支持表示当Unicode作为标识符声明变量时有些用法的转移。ES5已经同意Unicode转义序列作为标识符声明变量了,如下:

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

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

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

ES6惨遭,你吧足以为此Unicode编码单元的转义序列作为标识符:

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

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

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

另外,ES6的标识符声明语法遵循规范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)。

重多字符串相关变更

JavaScript对字符串处理的完备性完全无设另编程语言。直到ES5才引入了trim()法,ES6于字符串处理及吧扩展了森初章程。

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

自JavaScript面世以来,开发者一直下indexOf()措施处理子字符串。ES6新增加了三单处理子字符串的法子:

  • 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()不同,后两者会将正则表达式转化为字符串后进行拍卖。

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);

重新多正则表达式相关变更

正则表达式是处理字符串不可或缺的同一环,然而在ES6之前的几只本子升级中连没太要命改。ES6于提升字符串操作的又,也对正则表达式进行了改善。

正则表达式的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标明,另一个勿含有其他标志。第一蹩脚开展的老三涂鸦匹配都回来了千篇一律的结果hello1(请留意末尾的空格)。然后用三只正则表达式的lastIndex性能都安为1,作用是教三者从字符串的次字符开始匹配。不含有其他标志的表达式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

落得例被,粘性正则和大局正则第一不良exec()相当之后lastIndex值变为7,第二不善匹配后改为14。

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

  1. 仅仅出正则表达式自身的函数(比如exec()test())才见面指向粘性正则表达式的lastIndex来影响。如果正则表达式作为ECMAScript字符串函数(比如match())的参数,粘性正则表达式的lastIndex价值不见面让影响;
  2. 如果用^配合配字符串的前奏字符,粘性正则表达式会打字符串的胚胎字符,或者多做本第一尽的开局字符开始匹配。只要lastIndex为0,粘性正则表达式和正常的正则表达式行为完全一致。但是如果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首先单参数是正则表达式,设置第二只参数会报错。ES6改良了这种规则,第二个参数可以于安装,并且会蒙掉第一只参数正则的其他标志。如下:

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寓相同之表明。

flags属性

ES5备受,可以经过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性,可以一直提正则表达式的其它碎片,而无自然正则表达式转换为字符串操作。

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

当然,=====能够满足绝大多数的采用场景,如果生上述涉的少种独特状况,Object.is()函数会提高代码的逻辑性。