字面量的扩展
数值
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