[TOC]
ES6新特性列表
本文转载整理自方方文章:https://fangyinghang.com/es-6-tutorials/
1. 作用域
1.1 块级作用域
块语句(或其他语言的复合语句)用于组合零个或多个语句。该块由一对大括号界定,可以是labelled
:
语法:
块声明:
{ StatementList }
,StatementList:在块语句中分组的语句。标记块声明:LabelIdentifier: { StatementList },LabelIdentifier:用于视觉识别的可选
label
或break
的目标
通过var
声明的变量,没有块级作用域,所设置变量的影响会在超出语句块本身之外持续存在。 换句话说,这种语句块不会引入一个作用域。
相比之下,使用 let
和const
声明的变量是有块级作用域的。
1.2 块级变量let
let 语句声明一个块级作用域的本地变量,并且可选的将其初始化为一个值。let
允许你声明一个作用域被限制在 块
级中的变量、语句或者表达式。与 var
关键字不同的是, var
声明的变量只能是全局或者整个函数块的。 var
和 let
的不同之处在于let
是在编译时才初始化(声明let前不可访问,暂存死区)。
就像const
一样,let
不会在全局声明时(在最顶部的范围)创建window
对象的属性。
let
声明的变量只在其声明的块或子块中可用,这一点,与var
相似。二者之间最主要的区别在于var
声明的变量的作用域是整个封闭函数。
// var声明的变量整个函数作用域内都可以访问
function varTest() {
var x = 1;
{
var x = 2; // 同样的变量!
console.log(x); // 2
}
console.log(x); // 2
}
// let声明的变量只能在所在的块区域内访问
function letTest() {
let x = 1;
{
let x = 2; // 不同的变量
console.log(x); // 2
}
console.log(x); // 1
}
// 位于函数或代码顶部的var声明会给全局对象新增属性, 而let不会。例如:
var x = 'global';
let y = 'global';
console.log(this.x); // "global"
console.log(this.y); // undefined
暂存死区
与通过
var
声明的有初始化值undefined
的变量不同,通过let
声明的变量直到它们的定义被执行时才初始化。在变量初始化前访问该变量会导致ReferenceError
。该变量处在一个自块顶部到初始化处理的“暂存死区”中。如果使用
typeof
检测在暂存死区中的变量, 会抛出ReferenceError
异常:
function do_something () {
console.log(bar); // undefined
// console.log(foo); // Uncaught ReferenceError: Cannot access 'foo' before initialization
console.log(typeof foo); // Uncaught ReferenceError: Cannot access 'foo' before initialization
var bar = 1;
let foo = 2;
}
do_something()
1.3 块级变量const
常量是块级作用域,很像使用 let 语句定义的变量。常量的值不能通过重新赋值来改变,并且不能重新声明。
const常量更let一样,只不过一旦声明就不能修改值。
- 暂存死区:与let一样
2. 箭头函数
sum = (a, b) => a + b
nums.forEach( v => { console.log(v) })
- 词法 this,
箭头函数和普通函数区别:
1、箭头函数没有this
2、箭头函数不能使用new调用
3、箭头函数没有arguments参数
3. 参数处理
3.1 默认参数值
函数默认参数允许在没有值或undefined
被传入时使用默认形参。
JavaScript 中函数的参数默认是undefined
。然而,在某些情况下可能需要设置一个不同的默认值。这是默认参数可以帮助的地方。以前,一般设置默认参数的方法是在函数体测试参数是否为undefined
,如果是的话就设置为默认的值。
function multiply(a, b = 1) {
return a * b;
}
console.log(multiply(5, 2)); // 10
console.log(multiply(5)); // 5
// 这个规则对于函数和变量也是适用的
function callSomething(thing = something()) {
return thing;
}
let numberOfTimesCalled = 0;
function something() {
numberOfTimesCalled += 1;
return numberOfTimesCalled;
}
callSomething(); // 1
callSomething(); // 2
3.2 剩余参数
剩余参数语法允许我们将一个不定数量的参数表示为一个数组。
语法:function(a, b, ...theArgs) {}
如果函数的最后一个命名参数以...
为前缀,则它将成为一个由剩余参数组成的数组,其中从0
(包括)到theArgs.length - 1
的元素由传递给函数的实际参数提供。在上面的语法中,theArgs将收集该函数的第三个参数(第一个参数被映射到a,而第二个参数映射到b)和所有后续参数。
在上面的语法中,theArgs
将收集该函数的第三个参数(第一个参数被映射到a
,而第二个参数映射到b
)和所有后续参数。
- 剩余参数和
arguments
对象的区别- 剩余参数只包含那些没有对应形参的实参,而
arguments
对象包含了传给函数的所有实参。 arguments
对象不是一个真正的数组,而剩余参数是真正的Array
实例,也就是说你能够在它上面直接使用所有的数组方法,比如sort
,map
,forEach
或pop
。arguments
对象还有一些附加的属性 (如callee
属性)。
- 剩余参数只包含那些没有对应形参的实参,而
function sum(...theArgs) {
return theArgs.reduce((previous, current) => { return previous + current; });
}
console.log(sum(1, 2, 3)); // 6
console.log(sum(1, 2, 3, 4)); // 10
// 可以解构剩余参数
function f(...[a, b, c]) { return a + b + c;}
f(1) // NaN (b and c are undefined)
f(1, 2, 3) // 6
f(1, 2, 3, 4) // 6 (the fourth parameter is not destructured)
3.3 展开运算符
展开语法(Spread syntax), 可以在函数调用/数组构造时, 将数组表达式或者string在语法层面展开;还可以在构造字面量对象时, 将对象表达式按key-value的方式展开。
语法:
函数调用:
myFunction(...iterableObj)
,等价于apply方式调用myFunction.apply(null, iterableObj)
,在new表达式中不能使用apply,扩展运算符就很好替换字面量数组构造或字符串:
[...iterableObj, '4', ...'hello', 6]
构造字面量对象时,进行克隆或者属性拷贝:
let objClone = { ...obj }
,作用跟Object.assign()
一样,都是浅拷贝,Object.assign()
函数会触发 setters,而展开语法则不会
注意:
在数组或函数参数中使用展开语法时, 该语法只能用于 可迭代对象:
在函数调用时使用展开语法,请注意不能超过 JavaScript 引擎限制的最大参数个数。更多详细信息,请参考:
apply()
。
function sum(x, y, z) { return x + y + z; }
const numbers = [1, 2, 3];
// 使用apply形式
console.log(sum.apply(null, numbers)); // 6
// 使用扩展运算符
console.log(sum(...numbers)); // 6
// 扩展运算符在 new中应用
new Date(...[1970, 0, 1])
// 数组拷贝,是浅拷贝,多维数组无法使用扩展运算符拷贝
var arr1 = [1, 2, 3];
var arr2 = [...arr1]; // 类似arr.slice()
arr2.push(4); // arr2 此时变成 [1, 2, 3, 4], arr 不受影响
// 扩展运算符还可以用来连接多个数组
var arr3 = [...arr1, ...arr2] // [1, 2, 3, 1, 2, 3, 4]
var obj1 = { foo: 'bar', x: 42 };
var obj2 = { foo: 'baz', y: 13 };
// 克隆对象
var clonedObj = { ...obj1 }; // { foo: "bar", x: 42 }
// 合并对象
var mergedObj = { ...obj1, ...obj2 }; // { foo: "baz", x: 42, y: 13 }
4. 模板字面量
模板字面量 是允许嵌入表达式的字符串字面量。你可以使用多行字符串和字符串插值功能。
模板字符串使用反引号 (``) 来代替普通字符串中的用双引号和单引号。模板字符串可以包含特定语法(${expression}
)的占位符。占位符中的表达式和周围的文本会一起传递给一个默认函数,该函数负责将所有的部分连接起来,如果一个模板字符串由表达式开头,则该字符串被称为带标签的模板字符串,该表达式通常是一个函数,它会在模板字符串处理后被调用,在输出最终结果前,你都可以通过该函数来对模板字符串进行操作处理。在模版字符串内使用反引号(`)时,需要在它前面加转义符(\)。
语法:
`string text`
// 多行
`string text line 1
string text line 2`
// 字符串拼接参数
`string text ${expression} string text`
4.1 多行字符串
在新行中插入的任何字符都是模板字符串中的一部分,使用普通字符串,你可以通过以下的方式获得多行字符串:
console.log('string text line 1\n' +
'string text line 2');
// "string text line 1
// string text line 2"
要获得同样效果的多行字符串,只需使用如下代码:
console.log(`string text line 1
string text line 2`);
// "string text line 1
// string text line 2"
4.2 字符串插值
普通字符串插入表达式,必须使用如下方式
var a = 5;
var b = 10;
console.log('Fifteen is ' + (a + b) + ' and\nnot ' + (2 * a + b) + '.');
// "Fifteen is 15 and
// not 20."
现在通过模板字符串,我们可以使用一种更优雅的方式来表示:
var a = 5;
var b = 10;
console.log(`Fifteen is ${a + b} and
not ${2 * a + b}.`);
// "Fifteen is 15 and
// not 20."
4.3 带标签的模板字面量
更高级的是带标签的模板字符串。标签使您可以用函数解析模板字符串。标签函数的第一个参数包含一个字符串值的数组。其余的参数与表达式相关。最后,你的函数可以返回处理好的的字符串(或者它可以返回完全不同的东西 , 如下个例子所述)。用于该标签的函数的名称可以被命名为任何名字。标签函数并不一定需要返回一个字符串.
function myTag(strings, personExp, ageExp) {
var str0 = strings[0]; // "that "
var str1 = strings[1]; // " is a "
// 其实还有一个表达式,但是他是空的,所以忽略
// var str2 = strings[2];
var ageStr;
if (ageExp > 99){ ageStr = 'centenarian'; }
else { ageStr = 'youngster'; }
return str0 + personExp + str1 + ageStr;
}
var person = 'Mike';
var age = 28;
var output = myTag`that ${ person } is a ${ age }`;
console.log(output);
4.4 原始字符串
在标签函数的第一个参数中,存在一个特殊的属性raw
,我们可以通过它来访问模板字符串的原始字符串,而不经过特殊字符的替换。
function tag(strings) { console.log(strings.raw[0]); }
tag`string text line 1 \n string text line 2`;
// logs "string text line 1 \n string text line 2" , 输出了需要特殊处理的字符'\' and 'n'
tag`hello ${2222} 你好`; // hello , 遇到插值就结束输出了
另外,使用String.raw()
方法创建原始字符串和使用默认模板函数和字符串连接创建是一样的。
var str = String.raw`Hi\n${2+3}!`;
// "Hi\n5!"
str.length;
// 6
str.split('').join(',');
// "H,i,\,n,5,!"
5. 原有字面量加强
- 更安全的二进制字面量(0b1111101)
- 更安全的八进制字面量(0o767)
- 字符串支持 Unicode
- 正则表达式字面量添加 Unicode 支持(u 标记)
- 正则表达式添加 y 标记,支持粘滞匹配
6. 对象属性加强
- 属性定义支持短语法
obj = { x, y }
- 属性名支持表达式
obj = {["baz" + quux() ]: 42}
- 添加
__proto__
属性,但不建议使用
7. 解构赋值
- 数组匹配
[ b, a ] = [ a, b ]
- 对象匹配
let { a, b, c } = objABC
- 参数匹配
function g ({ name: n, val: v }) {}
- 函数参数默认值
function f({size='big', radius=25} = {}) {}
解构赋值语法是一种 Javascript 表达式。通过解构赋值, 可以将属性/值从对象/数组中取出,赋值给其他变量。
// 解构数组
let a, b, rest;
[a, b] = [10, 20]; // a === 10, b === 20
[a, b, ...rest] = [10, 20, 30, 40, 50]; // 将剩余数组赋值给一个变量
console.log(rest); // [30, 40, 50]
// 默认值:为了防止从数组中取出一个值为undefined的对象,可以在表达式左边的数组中为任意对象预设默认值。
[a=5, b=7] = [1]; // a === 1, b === 7
// 解析一个从函数返回的数组, 还可以忽略某些返回值
function f() { return [1, 2, 3]; }
var [a, , b] = f();
// 解构对象
var o = {p: 42, q: true};
var {p, q} = o;
console.log(p); // 42
// 可以从一个对象中提取变量并赋值给和对象属性名不同的新的变量名。
var o = {p: 42, q: true};
var {p: foo, q: bar} = o;
console.log(foo); // 42
// 默认值:变量可以先赋予默认值。当要提取的对象没有对应的属性,变量就被赋予默认值。
var {a = 10, b = 5} = {a: 3};
console.log(b); // 5
// 给新的变量命名并提供默认值
var {a:aa = 10, b:bb = 5} = {a: 3};
console.log(bb); // 5
// 函数参数
// 从作为函数实参的对象中提取数据
function whois({id, name: showName, fullName: {firstName: name}}){
console.log('id: ', id, showName + " is " + name);
}
var user = { id: 42, name: "jdoe", fullName: { firstName: "John",} };
whois(user)
// 函数参数默认值
function f({size='big', radius=25} = {}) { console.log(size, radius); }
f({radius: 30})
8. 模块
8.1 导出export
在创建JavaScript模块时,export
语句用于从模块中导出函数、对象或原始值,以便其他程序可以通过 import
语句使用它们。无论您是否声明,导出的模块都处于严格模式
。 export语句不能用在嵌入式脚本中。
存在两种 exports 导出方式:
- 命名导出(每个模块包含任意数量)
- 默认导出(每个模块包含一个)
// 导出单个特性
export let name1, name2, …, nameN; // also var, const
export let name1 = …, name2 = …, …, nameN; // also var, const
export function FunctionName(){...}
export class ClassName {...}
// 导出列表
export { name1, name2, …, nameN };
// 重命名导出
export { variable1 as name1, variable2 as name2, …, nameN };
// 解构导出并重命名
export const { name1, name2: bar } = o;
// 默认导出
export default expression;
export default function (…) { … } // also class, function*
export default function name1(…) { … } // also class, function*
export { name1 as default, … };
使用:
// 使用命名导出
// module "my-module.js"
function cube(x) { return x * x * x; }
const foo = Math.PI + Math.SQRT2;
export { cube, foo };
// 在其他的文件中导入:
import { cube, foo, graph } from 'my-module.js';
// 使用默认导出
// module "my-module.js"
export default function cube(x) { return x * x * x; }
// 在其他的文件中导入:
import cube from 'my-module.js';
8.1 导入import
静态的import
语句用于导入由另一个模块导出的绑定。无论是否声明,导入的模块都运行在严格模式下。在浏览器中,import
语句只能在声明了 type="module"
的 script
的标签中使用。
在您希望按照一定的条件或者按需加载模块的时候,动态import()
是非常有用的。而静态型的 import
是初始化加载依赖项的最优选择,使用静态 import
更容易从代码静态分析工具和 tree shaking 中受益。
// 导出文件:/modules/my-module.js文件
export function cube(x) { return x * x * x; }
export const foo = Math.PI + Math.SQRT2;
const name = 'export'
export { name };
export default '我是默认导出的'
// 导入文件写法
// 导入整个模块的内容,这将myModule插入当前作用域,其中包含来自位于/modules/my-module.js文件中导出的所有接口。
import * as myModule from '/modules/my-module.js';
console.log(myModule)
// {cube: [Function: cube], default: '我是默认导出的', foo: 4.555, name: 'export'}
myModule.cube(5)
// 导入单个、多个
import {cube} from '/modules/my-module.js';
import {cube, foo} from '/modules/my-module.js';
// 导入时重命名接口
import {cube as cc , foo as ff} from '/modules/my-module.js';
console.log(ff) // 4.55580
// 为了执行模块文件,不导入模块任何接口:这将运行模块中的全局代码, 但实际上不导入任何值。
import '/modules/my-module.js';
// 导入默认,
// 单独导入默认,名字可以任意命名
import tt from '/modules/my-module.js';
console.log(tt) // '我是默认导出的'
// 默认和命名空间导入或命名导入一起使用
import tt, * as myModule from './b.mjs'
// 或者
import tt, {cube, foo} from './b.mjs'
动态导入import:关键字import可以像调用函数一样来动态的导入模块。以这种方式调用,将返回一个 promise
。
import('/modules/my-module.js')
.then((module) => {
// Do something with the module.
});
这种使用方式也支持 await
关键字。
let module = await import('/modules/my-module.js');
9. 类
10 迭代
11. 生成器Generator
12. Promise
13. 元编程
14. 新增数据类型
15. 原有内置对象 API 增强
- Object.assign
- Array.from
- Array.of
- Array.prototype.fill
- Array.prototype.copyWithin
- Array.prototype.entries
- Array.prototype.keys
- Array.prototype.values
- String.prototype.includes
- String.prototype.repeat
- String.prototype.startsWith
- String.prototype.endsWith
- Number.EPSILON
- Number.isInteger
- Number.isSafeInteger
- Number.isFinite
- Number.isNaN(‘NaN’) // false
- Math.acosh
- Math.hypot
- Math.imul
- Math.sign
- Math.trunc