运算符的扩展
本章介绍 ES6 后续标准添加的一些运算符。
指数运算符
ES2016 新增了一个指数运算符(**)。
122 ** 2 // 42 ** 3 // 8
这个运算符的一个特点是右结合,而不是常见的左结合。多个指数运算符连用时,是从最右边开始计算的。
123// 相当于 2 ** (3 ** 2)2 ** 3 ** 2// 512
上面代码中,首先计算的是第二个指数运算符,而不是第一个。
指数运算符可以与等号结合,形成一个新的赋值运算符(**=)。
1234567let a = 1.5;a **= 2;// 等同于 a = a * a;let b = 4;b **= 3;// 等同于 b = b * b * b;
链判断运算符
编程实务中,如果读取对象内部的某个属性,往往需要判断一下,属性的上层对象是否存在。比如,读取message.body.user.firstName这个属性,安全的写法是写成下面这样。
12345678// 错误的写法const firstName = message.body.user.firstName || 'default ...
对象的新增方法
本章介绍 Object 对象的新增方法。
Object.is()
ES5
比较两个值是否相等,只有两个运算符:相等运算符(==)和严格相等运算符(===)。它们都有缺点,前者会自动转换数据类型,后者的NaN不等于自身,以及+0等于-0。JavaScript
缺乏一种运算,在所有环境中,只要两个值是一样的,它们就应该相等。
ES6 提出“Same-value
equality”(同值相等)算法,用来解决这个问题。Object.is就是部署这个算法的新方法。它用来比较两个值是否严格相等,与严格比较运算符(===)的行为基本一致。
1234Object.is('foo', 'foo')// trueObject.is({}, {})// false
不同之处只有两个:一是+0不等于-0,二是NaN等于自身。
12345+0 === -0 //trueNaN === NaN // falseObject.is(+0, -0) // falseObject.is(NaN, NaN) // true
...
对象的扩展
对象(object)是 JavaScript 最重要的数据结构。ES6
对它进行了重大升级,本章介绍数据结构本身的改变,下一章介绍Object对象的新增方法。
属性的简洁表示法
ES6
允许在大括号里面,直接写入变量和函数,作为对象的属性和方法。这样的书写更加简洁。
123456const foo = 'bar';const baz = {foo};baz // {foo: "bar"}// 等同于const baz = {foo: foo};
上面代码中,变量foo直接写在大括号里面。这时,属性名就是变量名,
属性值就是变量值。下面是另一个例子。
1234567891011function f(x, y) { return {x, y};}// 等同于function f(x, y) { return {x: x, y: y};}f(1, 2) // Object {x: 1, ...
数组的扩展
扩展运算符
含义
扩展运算符(spread)是三个点(...)。它好比 rest
参数的逆运算,将一个数组转为用逗号分隔的参数序列。
12345678console.log(...[1, 2, 3])// 1 2 3console.log(1, ...[2, 3, 4], 5)// 1 2 3 4 5[...document.querySelectorAll('div')]// [<div>, <div>, <div>]
该运算符主要用于函数调用。
12345678910function push(array, ...items) { array.push(...items);}function add(x, y) { return x + y;}const numbers = [4, 38];add(...numbers) // 42
上面代码中,array.push(...items)和add(...numbers)这两行,都是函数的调用,它们都使用了扩展运算符。该运 ...
函数的扩展
函数参数的默认值
基本用法
ES6 之前,不能直接为函数的参数指定默认值,只能采用变通的方法。
12345678function log(x, y) { y = y || 'World'; console.log(x, y);}log('Hello') // Hello Worldlog('Hello', 'China') // Hello Chinalog('Hello', '') // Hello World
上面代码检查函数log的参数y有没有赋值,如果没有,则指定默认值为World。这种写法的缺点在于,如果参数y赋值了,但是对应的布尔值为false,则该赋值不起作用。就像上面代码的最后一行,参数y等于空字符,结果被改为默认值。
为了避免这个问题,通常需要先判断一下参数y是否被赋值,如果没有,再等于默认值。
123if (typeof y === 'undefined') { y = ...
数值的扩展
二进制和八进制表示法
ES6
提供了二进制和八进制数值的新的写法,分别用前缀0b(或0B)和0o(或0O)表示。
120b111110111 === 503 // true0o767 === 503 // true
从 ES5
开始,在严格模式之中,八进制就不再允许使用前缀0表示,ES6
进一步明确,要使用前缀0o表示。
12345678910// 非严格模式(function(){ console.log(0o11 === 011);})() // true// 严格模式(function(){ 'use strict'; console.log(0o11 === 011);})() // Uncaught SyntaxError: Octal literals are not allowed in strict mode.
如果要将0b和0o前缀的字符串数值转为十进制,要使用Number方法。
12Number('0b111') // 7Number('0o10 ...
正则的扩展
RegExp 构造函数
在 ES5 中,RegExp构造函数的参数有两种情况。
第一种情况是,参数是字符串,这时第二个参数表示正则表达式的修饰符(flag)。
123var regex = new RegExp('xyz', 'i');// 等价于var regex = /xyz/i;
第二种情况是,参数是一个正则表示式,这时会返回一个原有正则表达式的拷贝。
123var regex = new RegExp(/xyz/i);// 等价于var regex = /xyz/i;
但是,ES5 不允许此时使用第二个参数添加修饰符,否则会报错。
12var regex = new RegExp(/xyz/, 'i');// Uncaught TypeError: Cannot supply flags when constructing one RegExp from another
ES6
改变了这种行为。如果RegExp构造函数第一个参数是一个正则对象,那么可以使用第二个参数指定修饰符。而且,返回的正则表达式会 ...
字符串的新增方法
本章介绍字符串对象的新增方法。
String.fromCodePoint()
ES5 提供String.fromCharCode()方法,用于从 Unicode
码点返回对应字符,但是这个方法不能识别码点大于0xFFFF的字符。
12String.fromCharCode(0x20BB7)// "ஷ"
上面代码中,String.fromCharCode()不能识别大于0xFFFF的码点,所以0x20BB7就发生了溢出,最高位2被舍弃了,最后返回码点U+0BB7对应的字符,而不是码点U+20BB7对应的字符。
ES6
提供了String.fromCodePoint()方法,可以识别大于0xFFFF的字符,弥补了String.fromCharCode()方法的不足。在作用上,正好与下面的codePointAt()方法相反。
1234String.fromCodePoint(0x20BB7)// "?"String.fromCodePoint(0x78, 0x1f680, 0x79) === 'x\uD83D\uDE80y ...
字符串的扩展
本章介绍 ES6
对字符串的改造和增强,下一章介绍字符串对象的新增方法。
字符的 Unicode 表示法
ES6 加强了对 Unicode
的支持,允许采用\uxxxx形式表示一个字符,其中xxxx表示字符的
Unicode 码点。
12"\u0061"// "a"
但是,这种表示法只限于码点在\u0000~\uFFFF之间的字符。超出这个范围的字符,必须用两个双字节的形式表示。
12345"\uD842\uDFB7"// "?""\u20BB7"// " 7"
上面代码表示,如果直接在\u后面跟上超过0xFFFF的数值(比如\u20BB7),JavaScript
会理解成\u20BB+7。由于\u20BB是一个不可打印字符,所以只会显示一个空格,后面跟着一个7。
ES6
对这一点做出了改进,只要将码点放入大括号,就能正确解读该字符。
1234567891011"\u{20BB7}"// "?&quo ...
变量的解构赋值
数组的解构赋值
基本用法
ES6
允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构(Destructuring)。
以前,为变量赋值,只能直接指定值。
123let a = 1;let b = 2;let c = 3;
ES6 允许写成下面这样。
1let [a, b, c] = [1, 2, 3];
上面代码表示,可以从数组中提取值,按照对应位置,对变量赋值。
本质上,这种写法属于“模式匹配”,只要等号两边的模式相同,左边的变量就会被赋予对应的值。下面是一些使用嵌套数组进行解构的例子。
1234567891011121314151617181920let [foo, [[bar], baz]] = [1, [[2], 3]];foo // 1bar // 2baz // 3let [ , , third] = ["foo", "bar", "baz"];third // "baz"let [x, , y] = [1, 2, 3];x // 1y // 3let [h ...