es6

1.let 命令

let声明的变量只在当前代码块有效

let不存在变量提升,必须在声明后再使用

暂时性死区:暂时性死区的本质就是,只要一进入当前作用域,所要使用的变量就已经存在了,但是不可获取,只有等到声明变量的那一行代码出现,才可以获取和使用该变量。

let不允许在相同作用域内重复声明同一个变量

为什么需要块级作用域?

(1)内层变量可能会覆盖全局变量,变量提升导致内层变量覆盖外层变量

(2)用来计数的变量泄露为全局变量

块级作用域内部优先使用函数表达式,而不是函数声明

let只能出现在当前作用域的顶层,严格模式下,函数声明也是。

2.const命令

const声明一个只读的常量,一旦声明,必须马上赋值,且常量的值不能改变,只在声明的块内有用,同样存在暂时性死区,不可重复声明。

let、const、var区别

(1)let、const不存在变量声明提升,var存在

(2)let、const只能赋值一次,否则报错,var可以赋值多次,const在声明的时候必须赋值

(3)同一作用域let和const不能声明同名变量,var可以

(4)let、const存在块级作用域,var没有

(5)如果是复合数据类型,const的值可以修改

3.声明变量的6种方法

const  var  function  let  class  import

4.顶层对象

在浏览器环境指的是window对象,在 Node 指的是global对象。ES5 之中,顶层对象的属性与全局变量是等价的。从 ES6 开始,全局变量将逐步与顶层对象的属性脱钩。

5.变量的解构赋值

数组的解构赋值

对象的解构赋值

字符串的解构赋值

数值和布尔的解构赋值   等号右边不是对象或数组,先转换为对象。undefined和null无法转换成为对象,所以对它们解构赋值会报错。

函数参数的解构赋值

可以使用圆括号的情况只有一种:赋值语句的非模式部分

变量解构赋值的作用:

(1)交换变量的值

(2)从函数返回多个值

(3)函数参数的定义

(4)提取json对象中的数据

(5)函数参数的默认值

(6)遍历Map结构

(7)输入模块的指定方法

6.字符串的新增方法

String.fromCodePoint()用于从 Unicode 码点返回对应字符

ES6 还为原生的 String 对象,提供了一个raw()方法。该方法返回一个斜杠都被转义(即斜杠前面再加一个斜杠)的字符串,往往用于模板字符串的处理方法。

ES6 提供了codePointAt()方法,能够正确处理 4 个字节储存的字符,返回一个字符的码点。

ES6 提供字符串实例的normalize()方法,用来将字符的不同表示方法统一为同样的形式,这称为 Unicode 正规化。

javaScript 只有indexOf方法,可以用来确定一个字符串是否包含在另一个字符串中。ES6 又提供了三种新方法。这三个方法都支持第二个参数,表示开始搜索的位置,使用第二个参数n时,endsWith的行为与其他两个方法有所不同。它针对前n个字符,而其他两个方法针对从第n个位置直到字符串结束。

includes():返回布尔值,表示是否找到了参数字符串。

startsWith():返回布尔值,表示参数字符串是否在原字符串的头部。

endsWith():返回布尔值,表示参数字符串是否在原字符串的尾部。

repeat方法返回一个新字符串,表示将原字符串重复n次。

如果某个字符串不够指定长度,会在头部或尾部补全。padStart()用于头部补全,padEnd()用于尾部补全。padStart()和padEnd()一共接受两个参数,第一个参数是字符串补全生效的最大长度,第二个参数是用来补全的字符串。如果省略第二个参数,默认使用空格补全长度。

trimStart()消除字符串头部的空格,trimEnd()消除尾部的空格。它们返回的都是新字符串,不会修改原始字符串。

matchAll()方法返回一个正则表达式在当前字符串的所有匹配。

7.正则的扩展

var regex=new RegExp(/xyz/,'i');  //es6支持在第一个参数是正则对象时,第二个参数是修饰符

字符串的正则方法:

String.prototype.match 调用 RegExp.prototype[Symbol.match]

String.prototype.replace 调用 RegExp.prototype[Symbol.replace]

String.prototype.search 调用 RegExp.prototype[Symbol.search]

String.prototype.split 调用 RegExp.prototype[Symbol.split]

ES6 对正则表达式添加了u修饰符,含义为“Unicode 模式”,用来正确处理大于\uFFFF的 Unicode 字符。也就是说,会正确处理四个字节的 UTF-16 编码。

点(.)字符在正则表达式中,含义是除了换行符以外的任意单个字符。

\S是预定义模式,匹配所有非空白字符

有些 Unicode 字符的编码不同,但是字型很相近

除了u修饰符,ES6 还为正则表达式添加了y修饰符,叫做“粘连”(sticky)修饰符。

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

ES6 为正则表达式新增了flags属性,会返回正则表达式的修饰符。

8.数值表示方法

二进制和八进制表示方法:0b  0o  将0b和0o开头的字符串数值转换成十进制,用Number方法

Number.isFinite() 用来判断一个数值是否是有限的

Number.isNaN()用来检查一个值是否是NaN

Number.parseInt()  Number.parseFloat()

Number.isInteger() //用来判断一个数值是否为整数

Number.EPSILON的实质是一个可以接受的最小误差范围。

Number.isSafeInteger()则是用来判断一个整数是否落在这个范围之内,这个函数的实现很简单,就是跟安全整数的两个边界值比较一下。

Math.trunc方法用于去除一个数的小数部分,返回整数部分。对于非数值,Math.trunc内部使用Number方法将其先转为数值。对于空值和无法截取整数的值,返回NaN。

Math.sign方法用来判断一个数到底是正数、负数、还是零。对于非数值,会先将其转换为数值。

它会返回五种值。

参数为正数,返回+1;

参数为负数,返回-1;

参数为 0,返回0;

参数为-0,返回-0;

其他值,返回NaN。

Math.cbrt方法用于计算一个数的立方根。

Math.clz32()方法将参数转为 32 位无符号整数的形式,然后返回这个 32 位值里面有多少个前导 0。

Math.imul方法返回两个数以 32 位带符号整数形式相乘的结果,返回的也是一个 32 位的带符号整数。

Math.hypot方法返回所有参数的平方和的平方根。

Math.clz32()方法将参数转为 32 位无符号整数的形式,然后返回这个 32 位值里面有多少个前导 0。

Math.imul方法返回两个数以 32 位带符号整数形式相乘的结果,返回的也是一个 32 位的带符号整数。

Math.fround方法返回一个数的32位单精度浮点数形式

四个对数运算:

Math.expm1(x)返回 ex - 1  

 Math.log1p(x)方法返回1 + x的自然对数

Math.log10(x)返回以 10 为底的x的对数。如果x小于 0,则返回 NaN。

Math.log2(x)返回以 2 为底的x的对数。如果x小于 0,则返回 NaN。

指数运算符(**),特点右结合。

9.函数的扩展

为函数的参数设置默认值,主要修改es5的缺点,如果为函数的参数赋值的布尔值是false,则还是会用默认值,应该加一个判断typeOf()==='undefined'.es6作出了改进  ,ES6 允许为函数的参数设置默认值,即直接写在参数定义的后面。

function log(x,y='World')

{console.log(x,y);}

函数的length属性:指定了默认值以后,函数的length属性,将返回没有指定默认值的参数个数。也就是说,指定了默认值后,length属性将失真。如果设置了默认值的参数不是尾参数,那么length属性也不再计入后面的参数了。

可以将参数默认值设为undefined,表明这个参数是可以省略的

ES6 引入 rest 参数(形式为...变量名),用于获取函数的多余参数,这样就不需要使用arguments对象了。rest 参数搭配的变量是一个数组,该变量将多余的参数放入数组中。rest 参数之后不能再有其他参数(即只能是最后一个参数),否则会报错。函数的length属性,不包括 rest 参数。

Array.prototype.slice.call(arguments, 1)可以理解成是让arguments转换成一个数组对象。

ES2016 做了一点修改,规定只要函数参数使用了默认值、解构赋值、或者扩展运算符,那么函数内部就不能显式设定为严格模式,否则会报错。

函数的name属性,返回该函数的函数名。如果将一个匿名函数赋值给一个变量,ES5 的name属性,会返回空字符串,而 ES6 的name属性会返回实际的函数名。bind返回的函数,name属性值会加上bound前缀

箭头函数:箭头函数根本没有自己的this,导致内部的this就是外层代码块的this。正是因为它没有this,所以也就不能用作构造函数。由于箭头函数没有自己的this,所以当然也就不能用call()、apply()、bind()这些方法去改变this的指向。

尾调用(Tail Call)是函数式编程的一个重要概念,本身非常简单,一句话就能说清楚,就是指某个函数的最后一步是调用另一个函数。

10.数组的扩展

扩展运算符:扩展运算符(spread)是三个点(...),它好比 rest 参数的逆运算,将一个数组转为用逗号分隔的参数序列。

扩展运算符的应用:

(1)复制数组:

const a1=[1,2];

// 写法一

const a2=[...a1];

// 写法二

const[...a2]=a1;

(2)合并数组

[...arr1,...arr2,...arr3]

(3)与解构赋值结合

[a,...rest]=list

如果将扩展运算符用于数组赋值,只能放在参数的最后一位,否则会报错。

(4)将字符串转换成真正的数组

[...'hello']

// [ "h", "e", "l", "l", "o" ]

(5)实现了Iterator接口的对象

(6)Map 和 Set 结构,Generator 函数

Array.from()用于将两类对象转换成数组:类似数组的对象和可遍历的对象

Array.of方法用于将一组值,转换为数组。

数组实例的copyWithin,在当前数组内部,将指定位置的成员复制到其他位置(会覆盖原有成员),然后返回当前数组。也就是说,使用这个方法,会修改当前数组。target(必需):从该位置开始替换数据。如果为负值,表示倒数。start(可选):从该位置开始读取数据,默认为 0。如果为负值,表示从末尾开始计算。end(可选):到该位置前停止读取数据,默认等于数组长度。如果为负值,表示从末尾开始计算。

Array.prototype.copyWithin(target,start=0,end=this.length)

数组实例的find方法,用于找出第一个符合条件的数组成员

数组实例的findIndex方法的用法与find方法非常类似,返回第一个符合条件的数组成员的位置,如果所有成员都不符合条件,则返回-1。

fill方法使用给定值,填充一个数组。

ES6 提供三个新的方法——entries(),keys()和values()——用于遍历数组。唯一的区别是keys()是对键名的遍历、values()是对键值的遍历,entries()是对键值对的遍历。

Array.prototype.includes方法返回一个布尔值,表示某个数组是否包含给定的值,与字符串的includes方法类似

Array.prototype.flat()用于将嵌套的数组“拉平”,变成一维的数组。该方法返回一个新数组,对原数据没有影响。

flatMap()只能展开一层数组。

[1,2,3,4].flatMap(x=>[[x*2]])// [[2], [4], [6], [8]]

数组的空位:

forEach(), filter(), reduce(), every() 和some()都会跳过空位。

map()会跳过空位,但会保留这个值

join()和toString()会将空位视为undefined,而undefined和null会被处理成空字符串

11.对象的扩展

属性的简洁表示法:ES6 允许直接写入变量和函数,作为对象的属性和方法。

const foo='bar';

const baz={foo};

const o={

method(){

return"Hello!";

}

};

定义对象的属性有两种方式:方法一是直接用标识符作为属性名,方法二是用表达式作为属性名,要将表达式放在方括号里面。

方法的name属性返回函数名,有两种特殊情况:bind方法创造的函数,name属性返回bound加上原函数的名字;Function构造函数创造的函数,name属性返回anonymous。如果对象的方法是一个 Symbol 值,那么name属性返回的是这个 Symbol 值的描述。

属性的遍历:遍历对象的键名,都遵守同样的属性遍历的次序规则。

首先遍历所有数值键,按照数值升序排列。

其次遍历所有字符串键,按照加入时间升序排列。

最后遍历所有 Symbol 键,按照加入时间升序排列。

关键字super,指向当前对象的原型对象。

扩展运算符的解构赋值,不能复制继承自原型对象的属性

12.对象的新增方法

Object.is() 判断两个值是否相等

Object.assign方法用于对象的合并,将源对象(source)的所有可枚举属性,复制到目标对象(target)。

用途主要有以下几种:

(1)为对象添加属性

(2)为对象添加方法

(3)克隆对象

(4)合并多个对象

(5)为属性指定默认值

Object.getOwnPropertyDescriptor()方法会返回某个对象属性的描述对象

__proto__属性:用来读取或设置当前对象的prototype对象

Object.setPrototypeOf方法的作用与__proto__相同,用来设置一个对象的prototype对象,返回参数对象本身。

Object.getPrototypeOf()与Object.setPrototypeOf方法配套,用于读取一个对象的原型对象。

Object.keys方法,返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历(enumerable)属性的键名。

Object.values方法返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历(enumerable)属性的键值。

Object.entries()方法返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历(enumerable)属性的键值对数组。

Object.fromEntries()方法是Object.entries()的逆操作,用于将一个键值对数组转为对象。

13.Symbol

防止属性名的冲突就是 ES6 引入Symbol的原因。

Symbol函数的参数只是表示对当前 Symbol 值的描述,因此相同参数的Symbol函数的返回值是不相等的。

Symbol 值不能与其他类型的值进行运算,会报错。

提供了一个实例属性description,直接返回 Symbol 的描述。  const sym=Symbol('foo');sym.description // "foo"

Symbol 值作为对象属性名时,不能用点运算符。在对象的内部,使用 Symbol 值定义属性时,Symbol 值必须放在方括号之中。

Object.getOwnPropertySymbols方法返回一个数组,成员是当前对象的所有用作属性名的 Symbol 值。

Reflect.ownKeys方法可以返回所有类型的键名,包括常规键名和 Symbol 键名。

希望重新使用同一个 Symbol 值,Symbol.for方法可以做到这一点。

Symbol.keyFor方法返回一个已登记的 Symbol 类型值的key。

对象的Symbol.isConcatSpreadable属性等于一个布尔值,表示该对象用于Array.prototype.concat()时,是否可以展开。

14.set和map结构

一种新的数据结构 Set。它类似于数组,但是成员的值都是唯一的,没有重复的值。通过add()方法向 Set 结构加入成员,结果表明 Set 结构不会添加重复的值。

去除数组重复元素的方法:[...new Set(array)]  

在set数据类型看来  两个对象总是不相等的,而NaN和NaN是相等的。

set实例的操作方法:

add(value):添加某个值,返回 Set 结构本身。

delete(value):删除某个值,返回一个布尔值,表示删除是否成功。

has(value):返回一个布尔值,表示该值是否为Set的成员。

clear():清除所有成员,没有返回值。

WeakSet 结构与 Set 类似,也是不重复的值的集合。但是,它与 Set 有两个区别。

首先,WeakSet 的成员只能是对象,而不能是其他类型的值。其次,WeakSet 中的对象都是弱引用,即垃圾回收机制不考虑 WeakSet 对该对象的引用,也就是说,如果其他对象都不再引用该对象,那么垃圾回收机制会自动回收该对象所占用的内存,不考虑该对象还存在于 WeakSet 之中。

ES6 提供了 Map 数据结构。它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。

Map结构实例的属性和操作方法:

size属性。set方法设置键名key对应的键值为value,然后返回整个 Map 结构。如果key已经有值,则键值会被更新,否则就新生成该键。set方法返回的是当前的Map对象,因此可以采用链式写法。get方法读取key对应的键值,如果找不到key,返回undefined。has方法返回一个布尔值,表示某个键是否在当前 Map 对象之中。has方法返回一个布尔值,表示某个键是否在当前 Map 对象之中。delete方法删除某个键,返回true。如果删除失败,返回false。clear方法清除所有成员,没有返回值。

WeakMap结构与Map结构类似,也是用于生成键值对的集合。WeakMap与Map的区别有两点。首先,WeakMap只接受对象作为键名(null除外),不接受其他类型的值作为键名。

15.Proxy

用于修改某些操作的默认行为,等同于在语言层面进行修改,相当于“元编程”,即对编程语言进行编程。

Proxy 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。Proxy 这个词的原意是代理,用在这里表示由它来“代理”某些操作,可以译为“代理器”。

get方法用于拦截某个属性的读取操作,可以接受三个参数,依次为目标对象、属性名和 proxy 实例本身(严格地说,是操作行为所针对的对象),其中最后一个参数可选。

set方法用来拦截某个属性的赋值操作,可以接受四个参数,依次为目标对象、属性名、属性值和 Proxy 实例本身,其中最后一个参数可选。

apply方法拦截函数的调用、call和apply操作。

has方法用来拦截HasProperty操作,即判断对象是否具有某个属性时,这个方法会生效。典型的操作就是in运算符。

construct方法用于拦截new命令

deleteProperty方法用于拦截delete操作,如果这个方法抛出错误或者返回false,当前属性就无法被delete命令删除。

defineProperty方法拦截了Object.defineProperty操作。

getOwnPropertyDescriptor方法拦截Object.getOwnPropertyDescriptor(),返回一个属性描述对象或者undefined。

getPrototypeOf方法主要用来拦截获取对象原型。

ownKeys方法用来拦截对象自身属性的读取操作。

web服务的客户端

varobj=newProxy({},{get:function(target,key,receiver){console.log(`getting ${key}!`);returnReflect.get(target,key,receiver);},set:function(target,key,value,receiver){console.log(`setting ${key}!`);returnReflect.set(target,key,value,receiver);}});

16.Reflect

也是es6为了操作对象而提供的新API,Reflect对象的设计目的有以下几个:

(1)将Object对象的一些明显属于语言内部的方法(比如Object.defineProperty),放到Reflect对象上。

(2)修改某些Object方法的返回结果,让其变得更合理。

(3)让Object操作都变成函数行为。

(4)Reflect对象的方法与Proxy对象的方法一一对应,只要是Proxy对象的方法,就能在Reflect对象上找到对应的方法。

静态方法:

(1)Reflect.get(target,name,receiver)  查找并返回target对象的name属性,如果没有该属性,则返回undefined。

(2)Reflect,set(target,name,value,receiver)设置target对象的name属性等于value

(3)Reflect.has(obj,name)对应 name in obj中的in运算符

(4)Reflect.deleteProperty方法等同于delete obj[name],用于删除对象的属性。

(5)Reflect.construct方法等同于new target(...args),这提供了一种不使用new,来调用构造函数的方法。

(6)Reflect.getPrototypeOf方法用于读取对象的__proto__属性,对应Object.getPrototypeOf(obj)。

(7)Reflect.setPrototypeOf方法用于设置目标对象的原型(prototype),对应Object.setPrototypeOf(obj, newProto)方法。它返回一个布尔值,表示是否设置成功。

(8)Reflect.setPrototypeOf方法用于设置目标对象的原型(prototype),对应Object.setPrototypeOf(obj, newProto)方法。它返回一个布尔值,表示是否设置成功。

(9)Reflect.apply方法等同于Function.prototype.apply.call(func, thisArg, args),用于绑定this对象后执行给定函数。。

(10)Reflect.defineProperty方法基本等同于Object.defineProperty,用来为对象定义属性。未来,后者会被逐渐废除,请从现在开始就使用Reflect.defineProperty代替它

(11)Reflect.getOwnPropertyDescriptor基本等同于Object.getOwnPropertyDescriptor,用于得到指定属性的描述对象,将来会替代掉后者。

(12)Reflect.isExtensible方法对应Object.isExtensible,返回一个布尔值,表示当前对象是否可扩展。

(13)Reflect.preventExtensions对应Object.preventExtensions方法,用于让一个对象变为不可扩展。它返回一个布尔值,表示是否操作成功。

(14)Reflect.ownKeys方法用于返回对象的所有属性,基本等同于Object.getOwnPropertyNames与Object.getOwnPropertySymbols之和。

(15)使用proxy实现观察者模式

17.Promise对象

promise是异步编程的一种解决方案,比传统的解决方案回调函数和事件更合理更强大。

所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。

promise对象有以下两个特点:

(1)对象的状态不受外界的影响,promise对象代表一个异步操作,有三种状态:pending(进行中)、fulfilled(已成功)、rejected(已失败)。只有异步操作的结果可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。

(2)一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pending变为rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果,这时就称为 resolved(已定型)。

Promise 实例具有then方法,也就是说,then方法是定义在原型对象Promise.prototype上的。它的作用是为 Promise 实例添加状态改变时的回调函数。前面说过,then方法的第一个参数是resolved状态的回调函数,第二个参数(可选)是rejected状态的回调函数。

Promise.prototype.catch方法是.then(null, rejection)或.then(undefined, rejection)的别名,用于指定发生错误时的回调函数。

finally方法用于指定不管 Promise 对象最后状态如何,都会执行的操作。不管promise最后的状态,在执行完then或catch指定的回调函数以后,都会执行finally方法指定的回调函数。

Promise.all方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。

Promise.race方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例。只要p1、p2、p3之中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给p的回调函数。如果指定时间内没有获得结果,就将 Promise 的状态变为reject,否则变为resolve。

Promise.resolve,有时需要将现有对象转为 Promise 对象,Promise.resolve方法就起到这个作用。

Promise.reject(reason)方法也会返回一个新的 Promise 实例,该实例的状态为rejected。

18.Iterator遍历器

它是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署 Iterator 接口,就可以完成遍历操作(即依次处理该数据结构的所有成员)。

每一次调用next方法,都会返回数据结构的当前成员的信息。具体来说,就是返回一个包含value和done两个属性的对象。其中,value属性是当前成员的值,done属性是一个布尔值,表示遍历是否结束。

for...in循环读取键名,for...of循环读取键值

遍历 Set 结构和 Map 结构。值得注意的地方有两个,首先,遍历的顺序是按照各个成员被添加进数据结构的顺序。其次,Set 结构遍历时,返回的是一个值,而 Map 结构遍历时,返回的是一个数组,该数组的两个成员分别为当前 Map 成员的键名和键值。

并不是所有类似数组的对象都具有 Iterator 接口,一个简便的解决方法,就是使用Array.from方法将其转为数组。

19.Generator是es6提供的一种异步编程解决方案,语法行为与传统函数完全不同。

Generator函数有两个特征:function与函数名之间有一个星号;函数体内部使用yield表达式,定义不同的内部状态

由于 Generator 函数返回的遍历器对象,只有调用next方法才会遍历下一个内部状态,所以其实提供了一种可以暂停执行的函数。yield表达式就是暂停标志。yield表达式只能用在 Generator 函数里面,用在其他地方都会报错。yield表达式如果用在另一个表达式之中,必须放在圆括号里面。

20.class类

类和模块的内部默认是严格模式,不存在变量提升。

class的继承可以通过extends关键字实现

在子类的构造函数中,只有调用super之后,才可以使用this关键字,否则会报错。这是因为子类实例的构建,基于父类实例,只有super方法才能调用父类实例。

super()只能用在子类的构造函数之中,用在其他地方就会报错。

(1)子类的__proto__属性,表示构造函数的继承,总是指向父类。

(2)子类prototype属性的__proto__属性,表示方法的继承,总是指向父类的prototype属性。

21.模块加载方案

以前的模块加载方案主要有:commonJS和AMD两种方案,前者用于服务器,后者用于浏览器

从fs模块加载 3 个方法,其他方法不加载。这种加载称为“编译时加载”或者静态加载,即 ES6 可以在编译时就完成模块加载,效率要比 CommonJS 模块的加载方式高。

ES6 的模块自动采用严格模式

21.es6的新特性有哪些

(1)箭头函数

简化了函数的编写,修改了this的指带问题

(2)块级作用域

(3)数组的扩展

(4)rest参数

(5)展开运算符

(6)解构赋值

(7)模板字符串

(8)class

(9)promise

(10)iterator

(11)模块化

你可能感兴趣的:(es6)