字面量的扩展

 

数值

ES6提供了二进制和八进制数值的新的写法,分别用前缀0b和0o表示。

二进制表示法

0b111110111 === 503 // true

八进制表示法

0o767 === 503 // true

字符

Unicode表示法

JavaScript允许采用“\uxxxx”形式表示一个字符,其中“xxxx”表示字符的码点。

"\u0061"
// "a"

但是,这种表示法只限于\u0000——\uFFFF之间的字符。超出这个范围的字符,必须用两个双字节的形式表达。

"\uD842\uDFB7"
// "𠮷"
 
"\u20BB7"
// " 7"

上面代码表示,如果直接在“\u”后面跟上超过0xFFFF的数值(比如\u20BB7),JavaScript会理解成“\u20BB+7”。由于\u20BB是一个不可打印字符,所以只会显示一个空格,后面跟着一个7。

ES6对这一点做出了改进,只要将码点放入大括号,就能正确解读该字符。

"\u{20BB7}"
// "𠮷"
 
"\u{41}\u{42}\u{43}"
// "ABC"

正则表达式

u修饰符

ES6对正则表达式添加了u修饰符,用来正确处理大于\uFFFF的Unicode字符。

点字符

点(.)字符在正则表达式中,解释为除了换行以外的任意单个字符。对于码点大于0xFFFF的Unicode字符,点字符不能识别,必须加上u修饰符。

var s = "𠮷";
 
/^.$/.test(s) // false
/^.$/u.test(s) // true

上面代码表示,如果不添加u修饰符,正则表达式就会认为字符串为两个字符,从而匹配失败。

Unicode字符表示法

ES6新增了使用大括号表示Unicode字符,这种表示法在正则表达式中必须加上u修饰符,才能识别。

/\u{61}/.test('a') // false
/\u{61}/u.test('a') // true
/\u{20BB7}/u.test('𠮷') // true

上面代码表示,如果不加u修饰符,正则表达式无法识别\u{61}这种表示法,只会认为这匹配61个连续的u。

量词

使用u修饰符后,所有量词都会正确识别大于码点大于0xFFFF的Unicode字符。

/a{2}/.test('aa') // true
/a{2}/u.test('aa') // true
/𠮷{2}/.test('𠮷𠮷') // false
/𠮷{2}/u.test('𠮷𠮷') // true

预定义模式

u修饰符也影响到预定义模式,能否正确识别码点大于0xFFFF的Unicode字符。

/^\S$/.test('𠮷') // false
/^\S$/u.test('𠮷')

上面代码的\S是预定义模式,匹配所有不是空格的字符。只有加了u修饰符,它才能正确匹配码点大于0xFFFF的Unicode字符。

利用这一点,可以写出一个正确返回字符串长度的函数。

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

s.length // 4
codePointLength(s) // 2

i修饰符

有些Unicode字符的编码不同,但是字型很相近,比如,\u004B与\u212A都是大写的K。

/[a-z]/i.test('\u212A') // false
/[a-z]/iu.test('\u212A') // true

上面代码中,不加u修饰符,就无法识别非规范的K字符。

y修饰符

除了u修饰符,ES6还为正则表达式添加了y修饰符,叫做“粘连”(sticky)修饰符。它的作用与g修饰符类似,也是全局匹配,后一次匹配都从上一次匹配成功的下一个位置开始,不同之处在于,g修饰符只确保剩余位置中存在匹配,而y修饰符确保匹配必须从剩余的第一个位置开始,这也就是“粘连”的涵义。

var s = "aaa_aa_a";
var r1 = /a+/g;
var r2 = /a+/y;
 
r1.exec(s) // ["aaa"]
r2.exec(s) // ["aaa"]
 
r1.exec(s) // ["aa"]
r2.exec(s) // null

上面代码有两个正则表达式,一个使用g修饰符,另一个使用y修饰符。这两个正则表达式各执行了两次,第一次执行的时候,两者行为相同,剩余字符串都是“_aa_a”。由于g修饰没有位置要求,所以第二次执行会返回结果,而y修饰符要求匹配必须从头部开始,所以返回null。

如果改一下正则表达式,保证每次都能头部匹配,y修饰符就会返回结果了。

var s = "aaa_aa_a";
var r = /a+_/y;
 
r.exec(s) // ["aaa_"]
r.exec(s) // ["aa_"]

上面代码每次匹配,都是从剩余字符串的头部开始。

进一步说,y修饰符号隐含了头部匹配的标志ˆ。

/b/y.exec("aba")
// null

上面代码由于不能保证头部匹配,所以返回null。y修饰符的设计本意,就是让头部匹配的标志ˆ在全局匹配中都有效。

与y修饰符相匹配,ES6的正则对象多了sticky属性,表示是否设置了y修饰符。

var r = /hello\d/y;
r.sticky // true

数组

ES6提供简洁写法,允许直接通过现有数组生成新数组,这被称为数组推导(array comprehension)。

语法

[for (x of iterable) x]
[for (x of iterable) if (condition) x]
[for (x of iterable) for (y of iterable) x + y]

参数

概述

在数组推导式内部,可以使用下面两种子语句:

for…of if 每个 for-of 语句都放在与其配对的 if 语句(可以有多个,也可以完全省略)的左边,每个数组推导式中可以包含多组这样的配对,但最终选取的表达式值只能有一个,且这个值(也可以是个数组推导式,也就是说可以嵌套)只能放在推导式的最右边,紧靠着右中括号。

推导例子

基本用法

通过for…of结构,数组a2直接在a1的基础上生成。

var a1 = [1, 2, 3, 4];
var a2 = [i * 2 for (i of a1)];
  
a2 // [2, 4, 6, 8]

数组推导可以替代map和filter方法。模拟map功能只要单纯的for…of循环就行了,模拟filter功能除了for…of循环,还必须加上if语句。

[for (i of [1, 2, 3]) i * i];
// 等价于
[1, 2, 3].map(function (i) { return i * i });
  
[i for (i of [1,4,2,3,-8]) if (i < 3)];
// 等价于
[1,4,2,3,-8].filter(function(i) { return i < 3 });

多重推导

新引入的for…of结构,可以直接跟在表达式的前面或后面,甚至可以在一个数组推导中,使用多个for…of结构。

var a1 = ["x1", "y1"];
var a2 = ["x2", "y2"];
var a3 = ["x3", "y3"];
 
[(console.log(s + w + r)) for (s of a1) for (w of a2) for (r of a3)];
// x1x2x3
// x1x2y3
// x1y2x3
// x1y2y3
// y1x2x3
// y1x2y3
// y1y2x3
// y1y2y3

上面代码在一个数组推导之中,使用了三个for…of结构。

需要注意的是,数组推导的方括号构成了一个单独的作用域,在这个方括号中声明的变量类似于使用let语句声明的变量。

字符串推导

由于字符串可以视为数组,因此字符串也可以直接用于数组推导。

[c for (c of 'abcde') if (/[aeiou]/.test(c))].join('') // 'ae'
  
[c+'0' for (c of 'abcde')].join('') // 'a0b0c0d0e0'

上面代码使用了数组推导,对字符串进行处理。

上一部分的数组推导有一个缺点,就是新数组会立即在内存中生成。这时,如果原数组是一个很大的数组,将会非常耗费内存。

对象

属性的简洁表示法

ES6允许直接写入变量和函数,作为对象的属性和方法。这样的书写更加简洁。

var Person = {
   
  name: '张三',
   
  //等同于birth: birth
  birth,
    
  // 等同于hello: function ()...
  hello() { console.log('我的名字是', this.name); }
  
};

这种写法用于函数的返回值,将会非常方便。

function getPoint() {
  var x = 1;
  var y = 10;
  
  return {x, y};
}

getPoint()
// {x:1, y:10}

下面是一个类似的例子。

let x = 4;
let y = 1;

// 下行等同于 let obj = { x: x, y: y };
let obj = { x, y };

属性名表达式

JavaScript语言定义对象的属性,有两种方法。

// 方法一
obj.foo = true;
 
// 方法二
obj['a'+'bc'] = 123;

方法一是直接用标识符作为属性名,方法二是用表达式作为属性名,这时要将表达式放在方括号之内。

但是,如果使用字面量方式定义对象(使用大括号),在ES5中只能使用方法一(标识符)定义属性。

var obj = {
  foo: true,
  abc: 123
};

ES6允许字面量定义对象时,用方法二(表达式)作为对象的属性名,即把表达式放在方括号内。

let propKey = 'foo';
   
let obj = {
   [propKey]: true,
   ['a'+'bc']: 123
};

下面是另一个例子。

var lastWord = "last word";
 
var a = {
    "first word": "hello",
    [lastWord]: "world"
};
  
a["first word"] // "hello"
a[lastWord] // "world"
a["last word"] // "world"

表达式还可以用于定义方法名。

let obj = {
  ['h'+'ello']() {
    return 'hi';
  }
};
 
console.log(obj.hello()); // hi