ES6语法简介

#ES6
##一、简介

    ECMAScript 6.0(以下简称 ES6)是 JavaScript 语言的下一代标准,已经在 2015 年 6 月正式发布了。它的目标,是使得 JavaScript 语言可以用来编写复杂的大型应用程序,成为企业级开发语言。ECMAScript 和 JavaScript 的关系是,前者是后者的规格,后者是前者的一种实现。

** 学习参考网址:**

##二、语法
###1、 let 与 const

let 声明的变量只在 let 命令所在的代码块内有效。  const 声明一个只读的常量,一旦声明,常量的值就不能改变。

let 特征

代码块内有效,不能重复声明,不存在变量提升。

const 特征

 const 声明一个只读变量,声明之后不允许改变。意味着,一但声明必须初始化,否则会报错。

###2、解构赋值
解构赋值是对赋值运算符的扩展。

他是一种针对数组或者对象进行模式匹配,然后对其中的变量进行赋值。

在代码书写上简洁且易读,语义更加清晰明了;也方便了复杂对象中数据字段获取。

数组模型的解构(Array)

基本
let [a, b, c] = [1, 2, 3]; // a = 1 // b = 2 // c = 3[链接文字](http://example.com)

可嵌套
let [a, [[b], c]] = [1, [[2], 3]]; // a = 1 // b = 2 // c = 3

不完全解构
let [a = 1, b] = []; // a = 1, b = undefined

剩余运算符
let [a, ...b] = [1, 2, 3]; //a = 1 //b = [2, 3]

字符串
let [a, b, c, d, e] = 'hello'; // a = 'h' // b = 'e' // c = 'l' // d = 'l' // e = 'o'

解构默认值
let [a = 2] = [undefined]; // a = 2 let [a = 3, b = a] = []; // a = 3, b = 3 let [a = 3, b = a] = [1]; // a = 1, b = 1 let [a = 3, b = a] = [1, 2]; // a = 1, b = 2

对象模型的解构(Object)

基本
let { foo, bar } = { foo: 'aaa', bar: 'bbb' }; // foo = 'aaa' // bar = 'bbb' let { baz : foo } = { baz : 'ddd' }; // foo = 'ddd'

可嵌套可忽略
let obj = {p: ['hello', {y: 'world'}] }; let {p: [x, { y }] } = obj; // x = 'hello' // y = 'world' let obj = {p: ['hello', {y: 'world'}] }; let {p: [x, { }] } = obj; // x = 'hello'

不完全解构
let obj = {p: [{y: 'world'}] }; let {p: [{ y }, x ] } = obj; // x = undefined // y = 'world'

剩余运算符
let {a, b, ...rest} = {a: 10, b: 20, c: 30, d: 40}; // a = 10 // b = 20 // rest = {c: 30, d: 40}

解构默认值
let {a = 10, b = 5} = {a: 3}; // a = 3; b = 5; let {a: aa = 10, b: bb = 5} = {a: 3}; // aa = 3; bb = 5;

###3、Symbol 用法
ES6 引入了一种新的原始数据类型 Symbol ,表示独一无二的值,最大的用法是用来定义对象的唯一属性名。

ES6 数据类型除了 Number 、 String 、 Boolean 、 Objec t、 null 和 undefined ,还新增了 Symbol 。

**基本用法 **

Symbol 函数栈不能用 new 命令,因为 Symbol 是原始数据类型,不是对象。可以接受一个字符串作为参数,为新创建的 Symbol 提供描述,用来显示在控制台或者作为字符串的时候使用,便于区分。

使用场景

作为属性名

 由于每一个 Symbol 的值都是不相等的,所以 Symbol 作为对象的属性名,可以保证属性不重名。

let sy = Symbol("key1"); // 写法1 let syObject = {}; syObject[sy] = "kk"; console.log(syObject); // {Symbol(key1): "kk"} // 写法2 let syObject = { [sy]: "kk"}; console.log(syObject); // {Symbol(key1): "kk"} // 写法3 let syObject = {}; Object.defineProperty(syObject, sy, {value: "kk"}); console.log(syObject); // {Symbol(key1): "kk"}

Symbol 作为对象属性名时不能用.运算符,要用方括号。因为.运算符后面是字符串,所以取到的是字符串 sy 属性,而不是 Symbol 值 sy 属性。

let syObject = {}; syObject[sy] = "kk"; syObject[sy]; // "kk" syObject.sy; // undefined

注意点

Symbol 值作为属性名时,该属性是公有属性不是私有属性,可以在类的外部访问。但是不会出现在 for...in 、 for...of 的循环中,也不会被 Object.keys() 、 Object.getOwnPropertyNames() 返回。如果要读取到一个对象的 Symbol 属性,可以通过 Object.getOwnPropertySymbols() 和 Reflect.ownKeys() 取到。

定义常量

 Symbol 定义常量可以保证一组常量的值都不相等。

 以下为例,Symbol 的值是唯一的,所以不会出现相同值得常量,即可以保证 switch 按照代码预想的方式执行。

const COLOR_RED = Symbol("red"); const COLOR_YELLOW = Symbol("yellow"); const COLOR_BLUE = Symbol("blue"); function getConstantName(color) { switch (color) { case COLOR_RED : return "COLOR_RED"; case COLOR_YELLOW : return "COLOR_YELLOW "; case COLOR_BLUE: return "COLOR_BLUE"; default: throw new Exception('Can't find this color'); } }

Symbol.for()

  Symbol.for() 类似单例模式,首先会在全局搜索被登记的 Symbol 中是否有该字符串参数作为名称的 Symbol 值,如果有即返回该 Symbol 值,若没有则新建并返回一个以该字符串参数为名称的 Symbol 值,并登记在全局环境中供搜索。

let yellow = Symbol("Yellow"); let yellow1 = Symbol.for("Yellow"); yellow === yellow1; // false let yellow2 = Symbol.for("Yellow"); yellow1 === yellow2; // true}

Symbol.keyFor()

    Symbol.keyFor() 返回一个已登记的 Symbol 类型值的 key ,用来检测该字符串参数作为名称的 Symbol 值是否已被登记。

let yellow1 = Symbol.for("Yellow"); Symbol.keyFor(yellow1); // "Yellow"

###4、Map 与 Set
Map 对象

Map 对象保存键值对。任何值(对象或者原始值) 都可以作为一个键或一个值。

Set 对象

 Set 对象允许你存储任何类型的唯一值,无论是原始值或者是对象引用。

###5、ES6 Reflect 与 Proxy

Proxy 与 Reflect 是 ES6 为了操作对象引入的 API 。Proxy 可以对目标对象的读取、函数调用等操作进行拦截,然后进行操作处理。它不直接操作对象,而是像代理模式,通过对象的代理对象进行操作,在进行这些操作时,可以添加一些需要的额外操作。Reflect 可以用于获取目标对象的行为,它与 Object 类似,但是更易读,为操作对象提供了一种更优雅的方式。它的方法与 Proxy 是对应的。

###6、字符串
ES6 之前判断字符串是否包含子串,用 indexOf 方法,ES6 新增了子串的识别方法。

  • includes():返回布尔值,判断是否找到参数字符串。
  • startsWith():返回布尔值,判断参数字符串是否在原字符串的头部。
  • endsWith():返回布尔值,判断参数字符串是否在原字符串的尾部。

let string = "apple,banana,orange"; string.includes("banana"); // true string.startsWith("apple"); // true string.endsWith("apple"); // false string.startsWith("banana",6) // true

字符串重复

    repeat():返回新的字符串,表示将字符串重复指定次数返回。

console.log("Hello,".repeat(2)); // "Hello,Hello,"

如果参数是小数,向下取整

console.log("Hello,".repeat(3.2)); // "Hello,Hello,Hello,"

 如果参数是 0 至 -1 之间的小数,会进行取整运算,0 至 -1 之间的小数取整得到 -0 ,等同于 repeat 零次

console.log("Hello,".repeat(-0.5)); // ""

 如果参数是 NaN,等同于 repeat 零次

console.log("Hello,".repeat(NaN)); // ""

 如果参数是负数或者 Infinity ,会报错:

console.log("Hello,".repeat(-1)); // RangeError: Invalid count value console.log("Hello,".repeat(Infinity)); // RangeError: Invalid count value

如果传入的参数是字符串,则会先将字符串转化为数字

console.log("Hello,".repeat("hh")); // "" console.log("Hello,".repeat("2")); // "Hello,Hello,"

字符串补全

  • padStart:返回新的字符串,表示用参数字符串从头部补全原字符串。

  • padEnd:返回新的字符串,表示用参数字符串从头部补全原字符串。

    以上两个方法接受两个参数,第一个参数是指定生成的字符串的最小长度,第二个参数是用来补全的字符串。如果没有指定第二个参数,默认用空格填充。
    

    console.log("h".padStart(5,"o")); // "ooooh" console.log("h".padEnd(5,"o")); // "hoooo" console.log("h".padStart(5)); // " h"

    如果指定的长度大于或者等于原字符串的长度,则返回原字符串: 
    

console.log("hello".padStart(5,"A")); // "hello"

  如果原字符串加上补全字符串长度大于指定长度,则截去超出位数的补全字符串:

console.log("hello".padEnd(10,",world!")); // "hello,worl"

  常用于补全位数:

console.log("123".padStart(10,"0")); // "0000000123"

**模板字符串**

   模板字符串相当于加强版的字符串,用反引号 `,除了作为普通字符串,还可以用来定义多行字符串,还可以在字符串中加入变量和表达式。

普通字符串
let string =Hello’\n’world; console.log(string); // "Hello' // 'world"

多行字符串:
let string1 = Hey,
can you stop angry now? ; console.log(string1); // Hey, // can you stop angry now?

字符串插入变量和表达式。
变量名写在 {} 中, {} 中可以放入 JavaScript 表达式。
let name = "Mike"; let age = 27; let info = ``My Name is ${name},I am a g e + 1 y e a r s o l d n e x t y e a r . c o n s o l e . l o g ( i n f o ) ; / / M y N a m e i s M i k e , I a m 28 y e a r s o l d n e x t y e a r . f u n c t i o n f ( ) r e t u r n " h a v e f u n ! " ; l e t s t r i n g 2 = G a m e s t a r t , {age+1} years old next year.` ` `console.log(info); // My Name is Mike,I am 28 years old next year.` 字符串中调用函数: `function f(){ return "have fun!"; } let string2= ` `Game start, {f()};
console.log(string2); // Game start,have fun!`

注意要点
模板字符串中的换行和空格都是会被保留的

标签模板

标签模板,是一个函数的调用,其中调用的参数是模板字符串。

  当模板字符串中带有变量,会将模板字符串参数处理成多个参数

###7、数值
数值的表示

二进制表示法新写法: 前缀 0b 或 0B 。

console.log(0b11 === 3); // true console.log(0B11 === 3); // true

八进制表示法新写法: 前缀 0o 或 0O 。

console.log(0o11 === 9); // true console.log(0O11 === 9); // true

常量

Number.EPSILON

Number.EPSILON 属性表示 1 与大于 1 的最小浮点数之间的差。

它的值接近于 2.2204460492503130808472633361816E-16,或者 2-52。

 测试数值是否在误差范围内:

0.1 + 0.2 === 0.3; // false // 在误差范围内即视为相等 equal = (Math.abs(0.1 - 0.3 + 0.2) < Number.EPSILON); // true

方法

Number 对象新方法

Number.isFinite()
用于检查一个数值是否为有限的( finite ),即不是 Infinity

从全局移植到 Number 对象的方法

逐步减少全局方法,用于全局变量的模块化。

方法的行为没有发生改变。

Number.parseInt()

Math 对象的扩展

Math.cbrt

用于计算一个数的立方根。

Math.imul

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

Math.hypot

用于计算所有参数的平方和的平方根。

Math.clz32

 用于返回数字的32 位无符号整数形式的前导0的个数。

** 数字处理 **

Math.trunc

用于返回数字的整数部分。

Math.fround

用于获取数字的32位单精度浮点数形式。

判断

Math.sign

判断数字的符号(正、负、0)

对数方法

Math.expm1()

用于计算 e 的 x 次方减 1 的结果,即 Math.exp(x) - 1 。

Math.log1p(x)

用于计算1 + x 的自然对数,即 Math.log(1 + x) 。

Math.log10(x)

用于计算以 10 为底的 x 的对数。

Math.log2()

 用于计算 2 为底的 x 的对数。

双曲函数方法

Math.sinh(x): 用于计算双曲正弦。 Math.cosh(x): 用于计算双曲余弦。 Math.tanh(x): 用于计算双曲正切。 Math.asinh(x): 用于计算反双曲正弦。 Math.acosh(x): 用于计算反双曲余弦。 Math.atanh(x): 用于计算反双曲正切。

指数运算符

1 ** 2; // 1 // 右结合,从右至左计算 2 ** 2 ** 3; // 256 // **= let exam = 2; exam ** = 2; // 4

###8、 对象

对象字面量

属性的简洁表示法
ES6允许对象的属性直接写变量,这时候属性名是变量名,属性值是变量值。
const age = 12; const name = "Amy"; const person = {age, name}; person //{age: 12, name: "Amy"} //等同于 const person = {age: age, name: name}
方法名也可以简写
const person = { sayHi(){ console.log("Hi"); } } person.sayHi(); //"Hi" //等同于 const person = { sayHi:function(){ console.log("Hi"); } } person.sayHi();//"Hi"
如果是Generator 函数,则要在前面加一个星号:
const obj = { *myGenerator() { yield 'hello world'; } }; //等同于 const obj = { myGenerator: function* () { yield 'hello world'; } };

属性名表达式
ES6允许用表达式作为属性名,但是一定要将表达式放在方括号内。
const obj = { ["he"+"llo"](){ return "Hi"; } } obj.hello(); //"Hi"
注意点:属性的简洁表示法和属性名表达式不能同时使用,否则会报错。
const hello = "Hello"; const obj = { [hello] }; obj //SyntaxError: Unexpected token } const hello = "Hello"; const obj = { [hello+"2"]:"world"};obj //{Hello2: "world"}

对象的拓展运算符

拓展运算符(...)用于取出参数对象所有可遍历属性然后拷贝到当前对象。

基本用法
let person = {name: "Amy", age: 15}; let someone = { ...person }; someone; //{name: "Amy", age: 15}
可用于合并两个对象
let age = {age: 15}; let name = {name: "Amy"}; let person = {...age, ...name}; person; //{age: 15, name: "Amy"}
注意点
自定义的属性和拓展运算符对象里面属性的相同的时候:自定义的属性在拓展运算符后面,则拓展运算符对象内部同名的属性将被覆盖掉。
let person = {name: "Amy", age: 15}; let someone = { ...person, name: "Mike", age: 17}; someone; //{name: "Mike", age: 17}
自定义的属性在拓展运算度前面,则变成设置新对象默认属性值。
let person = {name: "Amy", age: 15}; let someone = {name: "Mike", age: 17, ...person}; someone; //{name: "Amy", age: 15}
拓展运算符后面是空对象,没有任何效果也不会报错。
let a = {...{}, a: 1, b: 2}; a; //{a: 1, b: 2}
拓展运算符后面是null或者undefined,没有效果也不会报错。
let b = {...null, ...undefined, a: 1, b: 2}; b; //{a: 1, b: 2}

对象的新方法

    用于将源对象的所有可枚举属性复制到目标对象中

基本用法
let target = {a: 1}; let object2 = {b: 2}; let object3 = {c: 3}; Object.assign(target,object2,object3); // 第一个参数是目标对象,后面的参数是源对象 target; // {a: 1, b: 2, c: 3
如果目标对象和源对象有同名属性,或者多个源对象有同名属性,则后面的属性会覆盖前面的属性。
如果该函数只有一个参数,当参数为对象时,直接返回该对象;当参数不是对象时,会先将参数转为对象然后返回。
注意点
assign 的属性拷贝是浅拷贝:
let sourceObj = { a: { b: 1}}; let targetObj = {c: 3}; Object.assign(targetObj, sourceObj); targetObj.a.b = 2; sourceObj.a.b; // 2
Object.is(value1, value2)
用来比较两个值是否严格相等,与(===)基本类似。

###9、数组
数组创建

1、Array.of()
将参数中所有值作为元素形成数组。
console.log(Array.of(1, 2, 3, 4)); // [1, 2, 3, 4] // 参数值可为不同类型 console.log(Array.of(1, '2', true)); // [1, '2', true] // 参数为空时返回空数组 console.log(Array.of()); // []
2、Array.from()
将类数组对象或可迭代对象转化为数组。
// 参数为数组,返回与原数组一样的数组 console.log(Array.from([1, 2])); // [1, 2] // 参数含空位 console.log(Array.from([1, , 3])); // [1, undefined, 3]
3、mapFn
可选,map 函数,用于对每个元素进行处理,放入数组的是处理后的元素。
console.log(Array.from([1, 2, 3], (n) => n * 2)); // [2, 4, 6]
4、thisArg
可选,用于指定 map 函数执行时的 this 对象。
let map = { do: function(n) { return n * 2; } } let arrayLike = [1, 2, 3]; console.log(Array.from(arrayLike, function (n){ return this.do(n); }, map)); // [2, 4, 6]

类数组对象

1、一个类数组对象必须含有 length 属性,且元素属性名必须是数值或者可转换为数值的字符
let arr = Array.from({ 0: '1', 1: '2', 2: 3, length: 3 }); console.log(); // ['1', '2', 3] // 没有 length 属性,则返回空数组 let array = Array.from({ 0: '1', 1: '2', 2: 3, }); console.log(array); // [] // 元素属性名不为数值且无法转换为数值,返回长度为 length 元素值为 undefined 的数组 let array1 = Array.from({ a: 1, b: 2, length: 2 }); console.log(array1); // [undefined, undefined]
2、转换可迭代对象
转换 map
let map = new Map(); map.set('key0', 'value0'); map.set('key1', 'value1'); console.log(Array.from(map)); // [['key0', 'value0'],['key1', // 'value1']]
3、转换 set
let arr = [1, 2, 3]; let set = new Set(arr); console.log(Array.from(set)); // [1, 2, 3]
4、转换字符串
let str = 'abc'; console.log(Array.from(str)); // ["a", "b", "c"]

扩展的方法

1、查找
find()
查找数组中符合条件的元素,若有多个符合条件的元素,则返回第一个元素。
let arr = Array.of(1, 2, 3, 4); console.log(arr.find(item => item > 2)); // 3 // 数组空位处理为 undefined console.log([, 1].find(n => true)); // undefined
findIndex()
查找数组中符合条件的元素索引,若有多个符合条件的元素,则返回第一个元素索引。
let arr = Array.of(1, 2, 1, 3); // 参数1:回调函数 // 参数2(可选):指定回调函数中的 this 值 console.log(arr.findIndex(item => item = 1)); // 0 // 数组空位处理为 undefined console.log([, 1].findIndex(n => true)); //0
2、填充
fill()
将一定范围索引的数组元素内容填充为单个指定的值。
let arr = Array.of(1, 2, 3, 4); // 参数1:用来填充的值 // 参数2:被填充的起始索引 // 参数3(可选):被填充的结束索引,默认为数组末尾 console.log(arr.fill(0,1,2)); // [1, 0, 3, 4]
copyWithin()
将一定范围索引的数组元素修改为此数组另一指定范围索引的元素。
// 参数1:被修改的起始索引 // 参数2:被用来覆盖的数据的起始索引 // 参数3(可选):被用来覆盖的数据的结束索引,默认为数组末尾 console.log([1, 2, 3, 4].copyWithin(0,2,4)); // [3, 4, 3, 4] // 参数1为负数表示倒数 console.log([1, 2, 3, 4].copyWithin(-2, 0)); // [1, 2, 1, 2] console.log([1, 2, ,4].copyWithin(0, 2, 4)); // [, 4, , 4]
3、遍历
entries()
遍历键值对。
for(let [key, value] of ['a', 'b'].entries()){ console.log(key, value); } // 0 "a" // 1 "b" // 不使用 for... of 循环 let entries = ['a', 'b'].entries(); console.log(entries.next().value); // [0, "a"] console.log(entries.next().value); // [1, "b"] // 数组含空位 console.log([...[,'a'].entries()]); // [[0, undefined], [1, "a"]]
keys()
遍历键名。
for(let key of ['a', 'b'].keys()){ console.log(key); } // 0 // 1 // 数组含空位 console.log([...[,'a'].keys()]); // [0, 1] values()
遍历键值。
for(let value of ['a', 'b'].values()){ console.log(value); } // "a" // "b" // 数组含空位 console.log([...[,'a'].values()]); // [undefined, "a"]
4、包含
includes()
数组是否包含指定值。
注意:与 Set 和 Map 的 has 方法区分;Set 的 has 方法用于查找值;Map 的 has 方法用于查找键名。
// 参数1:包含的指定值 [1, 2, 3].includes(1); // true // 参数2:可选,搜索的起始索引,默认为0 [1, 2, 3].includes(1, 2); // false // NaN 的包含判断 [1, NaN, 3].includes(NaN); // true

定型数组

    数组缓冲区的特定类型的视图。
可以强制使用特定的数据类型,而不是使用通用的 DataView 对象来操作数组缓冲区。

扩展运算符

复制数组
let arr = [1, 2], arr1 = [...arr]; console.log(arr1); // [1, 2] // 数组含空位 let arr2 = [1, , 3], arr3 = [...arr2]; console.log(arr3); [1, undefined, 3]
合并数组
console.log([...[1, 2],...[3, 4]]); // [1, 2, 3, 4]

###10、 函数

函数参数的扩展

默认参数

基本用法
function fn(name,age=17){ console.log(name+","+age); } fn("Amy",18); // Amy,18 fn("Amy",""); // Amy, fn("Amy"); // Amy,17
注意点:使用函数默认参数时,不允许有同名参数。
// 不报错 function fn(name,name){ console.log(name); } // 报错 //SyntaxError: Duplicate parameter name not allowed in this context function fn(name,name,age=17){ console.log(name+","+age); }
只有在未传递参数,或者参数为 undefined 时,才会使用默认参数,null 值被认为是有效的值传递。
function fn(name,age=17){ console.log(name+","+age); } fn("Amy",null); // Amy,null
函数参数默认值存在暂时性死区,在函数参数默认值表达式中,还未初始化赋值的参数值无法作为其他参数的默认值。
function f(x,y=x){ console.log(x,y); } f(1); // 1 1 function f(x=y){ console.log(x); } f(); // ReferenceError: y is not defined

不定参数

不定参数用来表示不确定参数个数,形如,…变量名,由…加上一个具名参数标识符组成。具名参数只能放在参数组的最后,并且有且只有一个不定参数。
基本用法
function f(...values){ console.log(values.length); } f(1,2); //2 f(1,2,3,4); //4

箭头函数

箭头函数提供了一种更加简洁的函数书写方式。基本语法是:
参数 => 函数体

1、基本用法:
var f = v => v; //等价于 var f = function(a){ return a; } f(1); //1
2、当箭头函数没有参数或者有多个参数,要用 () 括起来。
var f = (a,b) => a+b; f(6,2); //8
3、当箭头函数函数体有多行语句,用 {} 包裹起来,表示代码块,当只有一行语句,并且需要返回结果时,可以省略 {} , 结果会自动返回。
var f = (a,b) => { let result = a+b; return result; } f(6,2); // 8
4、当箭头函数要返回对象的时候,为了区分于代码块,要用 () 将对象包裹起来
// 报错 var f = (id,name) => {id: id, name: name}; f(6,2); // SyntaxError: Unexpected token : // 不报错 var f = (id,name) => ({id: id, name: name}); f(6,2); // {id: 6, name: 2}
5、注意点:没有 this、super、arguments 和 new.target 绑定。
var func = () => { // 箭头函数里面没有 this 对象, // 此时的 this 是外层的 this 对象,即 Window console.log(this) } func(55) // Window var func = () => { console.log(arguments) } func(55); // ReferenceError: arguments is not defined
6、箭头函数体中的 this 对象,是定义函数时的对象,而不是使用函数时的对象。
function fn(){ setTimeout(()=>{ // 定义时,this 绑定的是 fn 中的 this 对象 console.log(this.a); },0) } var a = 20; // fn 的 this 对象为 {a: 19} fn.call({a: 18}); // 18
7、不可以作为构造函数,也就是不能使用 new 命令,否则会报错
适合使用的场景
ES6 之前,JavaScript 的 this 对象一直很令人头大,回调函数,经常看到 var self = this 这样的代码,为了将外部 this 传递到回调函数中,那么有了箭头函数,就不需要这样做了,直接使用 this 就行。所以,当我们需要维护一个 this 上下文的时候,就可以使用箭头函数。
不适合使用的场景
定义函数的方法,且该方法中包含 this;
需要动态 this 的时候。

###11、迭代器

** Iterator**

Iterator 是 ES6 引入的一种新的遍历机制,迭代器有两个核心概念:
1、 迭代器是一个统一的接口,它的作用是使各种数据结构可被便捷的访问,它是通过一个键为Symbol.iterator 的方法来实现。
2、 迭代器是用于遍历数据结构元素的指针(如数据库中的游标)。

迭代过程

 迭代的过程如下:
通过 Symbol.iterator 创建一个迭代器,指向当前数据结构的起始位置
随后通过 next 方法进行向下迭代指向下一个位置, next 方法会返回当前位置的对象,对象包含了 value 和 done 两个属性, value 是当前属性的值, done 用于判断是否遍历结束
 当 done 为 true 时则遍历结束

下面通过一个简单的例子进行说明:
const items = ["zero", "one", "two"]; const it = items[Symbol.iterator](); it.next(); {value: "zero", done: false} it.next(); {value: "one", done: false} it.next(); {value: "two", done: false} it.next(); {value: undefined, done: true}

可迭代的数据结构

以下是可迭代的值:

  • Array
    for (let item of ["zero", "one", "two"]) { console.log(item); } // output: // zero // one // two
  • String
    for (const c of 'z\uD83D\uDC0A') { console.log(c); } // output: // z // \uD83D\uDC0A
  • Map
    const map = new Map(); map.set(0, "zero"); map.set(1, "one"); for (let item of map) { console.log(item); } // output: // [0, "zero"] // [1, "one"]
    注意: WeakMaps 不可迭代
  • Set
    const set = new Set(); set.add("zero"); set.add("one"); for (let item of set) { console.log(item); } // output: // zero // one
    注意: WeakSets 不可迭代
  • Dom元素(正在进行中)
    arguments
    arguments 目前在 ES6 中使用越来越少,但也是可遍历的
    function args() { for (let item of arguments) { console.log(item); } } args("zero", "one"); // output: // zero // one

普通对象不可迭代

普通对象是由 object 创建的,不可迭代:

// TypeError for (let item of {}) { console.log(item); }
###12、Class 类

在ES6中,class (类)作为对象的模板被引入,可以通过 class 关键字定义类。

class 的本质是 function。

它可以看作一个语法糖,让对象原型的写法更加清晰、更像面向对象编程的语法。

基础用法

1、类定义
类表达式可以为匿名或命名。
// 匿名类
let Example = class { constructor(a) { this.a = a; } } // 命名类 let Example = class Example { constructor(a) { this.a = a; } }
2、类声明
class Example { constructor(a) { this.a = a; } } 注意要点:不可重复声明。 class Example{} class Example{} // Uncaught SyntaxError: Identifier 'Example' has already been // declared let Example1 = class{} class Example{} // Uncaught SyntaxError: Identifier 'Example' has already been // declared
3、注意要点
类定义不会被提升,这意味着,必须在访问前对类进行定义,否则就会报错。
类中方法不需要 function 关键字。
方法间不能加分号。
new Example();
class Example {}
4、类的主体
属性 prototype
ES6 中,prototype 仍旧存在,虽然可以直接自类中定义方法,但是其实方法还是定义在 prototype 上的。
覆盖方法 / 初始化时 添加方法 Example.prototype={ //methods } 添加方法 Object.assign(Example.prototype,{ //methods })
a、静态属性
静态属性:class 本身的属性,即直接定义在类内部的属性( Class.propname ),不需要实例化。 ES6 中规定,Class 内部只有静态方法,没有静态属性。
class Example { // 新提案 static a = 2; } // 目前可行写法 Example.b = 2;
b、公共属性
class Example{} Example.prototype.a = 2;
c、实例属性
实例属性:定义在实例对象( this )上的属性。
class Example { a = 2; constructor () { console.log(this.a); } }
d、name 属性
返回跟在 class 后的类名(存在时)。
let Example=class Exam { constructor(a) { this.a = a; } } console.log(Example.name); // Exam let Example=class { constructor(a) { this.a = a; } } console.log(Example.name); // Example
5、方法
a、constructor 方法
constructor 方法是类的默认方法,创建类的实例化对象时被调用。
class Example{ constructor(){ console.log('我是constructor'); } } new Example(); // 我是constructor
b、静态方法
class Example{ static sum(a, b) { console.log(a+b); } } Example.sum(1, 2); // 3
c、原型方法
class Example { sum(a, b) { console.log(a + b); } } let exam = new Example(); exam.sum(1, 2); // 3
d、实例方法
class Example { constructor() { this.sum = (a, b) => { console.log(a + b); } } }

类的实例化

new
class 的实例化必须通过 new 关键字。

class Example {} let exam1 = Example(); // Class constructor Example cannot be invoked without 'new'

实例化对象

class Example { constructor(a, b) { this.a = a; this.b = b; console.log('Example'); } sum() { return this.a + this.b; } } let exam1 = new Example(2, 1); let exam2 = new Example(3, 1); console.log(exam1._proto_ == exam2._proto_); // true exam1._proto_.sub = function() { return this.a - this.b; } console.log(exam1.sub()); // 1 console.log(exam2.sub()); // 2

decorator

decorator 是一个函数,用来修改类的行为,在代码编译时产生作用。

类修饰

一个参数

第一个参数 target,指向类本身。

function testable(target) { target.isTestable = true; } @testable class Example {} Example.isTestable; // true 多个参数——嵌套实现 function testable(isTestable) { return function(target) { target.isTestable=isTestable; } } @testable(true) class Example {} Example.isTestable; // true

 方法修饰
3个参数:target(类的原型对象)、name(修饰的属性名)、 
 descriptor(该属性的描述对象)。

class Example { @writable sum(a, b) { return a + b; } } function writable(target, name, descriptor) { descriptor.writable = false; return descriptor; // 必须返回 } 修饰器执行顺序 由外向内进入,由内向外执行。 class Example { @logMethod(1) @logMthod(2) sum(a, b){ return a + b; } } function logMethod(id) { console.log('evaluated logMethod'+id); return (target, name, desctiptor) => console.log('excuted logMethod '+id); } // evaluated logMethod 1 // evaluated logMethod 2 // excuted logMethod 2 // excuted logMethod 1

封装与继承

getter / setter
getter 与 setter 必须同级出现

class Example{ constructor(a, b) { this.a = a; // 实例化时调用 set 方法 this.b = b; } get a(){ console.log('getter'); return this.a; } set a(a){ console.log('setter'); this.a = a; // 自身递归调用 } } let exam = new Example(1,2); // 不断输出 setter ,最终导致 RangeError class Example1{ constructor(a, b) { this.a = a; this.b = b; } get a(){ console.log('getter'); return this._a; } set a(a){ console.log('setter'); this._a = a; } } let exam1 = new Example1(1,2); // 只输出 setter , 不会调用 getter 方法 console.log(exam._a); // 1, 可以直接访问

extends
通过 extends 实现类的继承。
class Child extends Father { ... }

super
子类 constructor 方法中必须有 super ,且必须出现在 this 之前。
class Father { constructor() {} } class Child extends Father { constructor() {} // or // constructor(a) { // this.a = a; // super(); // } } let test = new Child(); // Uncaught ReferenceError: Must call super // constructor in derived class before accessing 'this' or returning // from derived constructor

调用父类构造函数,只能出现在子类的构造函数。

class Father { test(){ return 0; } static test1(){ return 1; } } class Child extends Father { constructor(){ super(); } } class Child1 extends Father { test2() { super(); // Uncaught SyntaxError: 'super' keyword unexpected // here } }
调用父类方法, super 作为对象,在普通方法中,指向父类的原型对象,在静态方法中,指向父类

class Child2 extends Father { constructor(){ super(); // 调用父类普通方法 console.log(super.test()); // 0 } static test3(){ // 调用父类静态方法 return super.test1+2; } } Child2.test3(); // 3

注意要点
不可继承常规对象。
###13、模块

 在 ES6 前, 实现模块化使用的是 RequireJS 或者 seaJS(分别是基于 AMD 规范的模块化库,  和基于 CMD 规范的模块化库)。

ES6 引入了模块化,其设计思想是在编译时就能确定模块的依赖关系,以及输入和输出的变量。

ES6 的模块化分为导出(export) @与导入(import)两个模块。

特点

  • ES6 的模块自动开启严格模式,不管你有没有在模块头部加上 use strict;。

  • 模块中可以导入和导出各种类型的变量,如函数,对象,字符串,数字,布尔值,类等。

  • 每个模块都有自己的上下文,每一个模块内声明的变量都是局部变量,不会污染全局作用域。

  • 每一个模块只加载一次(是单例的), 若再去加载同目录下同文件,直接从内存中读取。

export 与 import

a、基本用法

模块导入导出各种类型的变量,如字符串,数值,函数,类。
导出的函数声明与类声明必须要有名称(export default 命令另外考虑)。 
不仅能导出声明还能导出引用(例如函数)。
export 命令可以出现在模块的任何位置,但必需处于模块顶层。
import 命令会提升到整个模块的头部,首先执行。

/*-----export [test.js]-----*/ let myName = "Tom"; let myAge = 20; let myfn = function(){ return "My name is" + myName + "! I'm '" + myAge + "years old." } let myClass = class myClass { static a = "yeah!"; } export { myName, myAge, myfn, myClass } /*-----import [xxx.js]-----*/ import { myName, myAge, myfn, myClass } from "./test.js"; console.log(myfn());// My name is Tom! I'm 20 years old. console.log(myAge);// 20 console.log(myName);// Tom console.log(myClass.a );// yeah!

b、as 的用法

export 命令导出的接口名称,须和模块内部的变量有一一对应关系。
导入的变量名,须和导出的接口名称相同,即顺序可以不一致。

/*-----export [test.js]-----*/ let myName = "Tom"; export { myName as exportName } /*-----import [xxx.js]-----*/ import { exportName } from "./test.js"; console.log(exportName);// Tom 使用 as 重新定义导出的接口名称,隐藏模块内部的变量 /*-----export [test1.js]-----*/ let myName = "Tom"; export { myName } /*-----export [test2.js]-----*/ let myName = "Jerry"; export { myName } /*-----import [xxx.js]-----*/ import { myName as name1 } from "./test1.js"; import { myName as name2 } from "./test2.js"; console.log(name1);// Tom console.log(name2);// Jerry

c、import 命令的特点

  • 只读属性:不允许在加载模块的脚本里面,改写接口的引用指向,即可以改写 import 变量类型为对象的属性值,不能改写 import 变量类型为基本类型的值。
    import {a} from "./xxx.js" a = {}; // error import {a} from "./xxx.js" a.foo = "hello"; // a = { foo : 'hello' }
  • 单例模式:多次重复执行同一句 import 语句,那么只会执行一次,而不会执行多次。import 同一模块,声明不同接口引用,会声明对应变量,但只执行一次 import 单例模式:多次重复执行同一句 import 语句,那么只会执行一次,而不会执行多次。import 同一模块,声明不同接口引用,会声明对应变量,但只执行一次 import
    import { a } "./xxx.js"; import { a } "./xxx.js"; // 相当于 import { a } "./xxx.js"; import { a } from "./xxx.js"; import { b } from "./xxx.js"; // 相当于 import { a, b } from "./xxx.js";
  • 静态执行特性:import 是静态执行,所以不能使用表达式和变量。
    import { "f" + "oo" } from "methods"; // error let module = "methods"; import { foo } from module; // error if (true) { import { foo } from "method1"; } else { import { foo } from "method2"; } // error

export default 命令

在一个文件或模块中,export、import 可以有多个,export default 仅有一个。
export default 中的 default 是对应的导出接口变量。
通过 export 方式导出,在导入时要加{ },export default 则不需要。
export default 向外暴露的成员,可以使用任意变量来接收。

var a = "My name is Tom!"; export default a; // 仅有一个 export default var c = "error"; // error,default 已经是对应的导出变量,不能跟着变量声明语句

复合使用

注:import() 是提案,这边暂时不延伸讲解。

export 与 import 可以在同一模块使用,使用特点:

 1、可以将导出接口改名,包括 default。
2、复合使用 export 与 import ,也可以导出全部,当前模块导出的接口会覆盖继承导出的。

export { foo, bar } from "methods"; // 约等于下面两段语句,不过上面导入导出方式该模块没有导入 foo 与 bar import { foo, bar } from "methods"; export { foo, bar }; /* ------- 特点 1 --------*/ // 普通改名 export { foo as bar } from "methods"; // 将 foo 转导成 default export { foo as default } from "methods"; // 将 default 转导成 foo export { default as foo } from "methods"; /* ------- 特点 2 --------*/ export * from "methods"; import b from "./xxx.js"; // 不需要加{}, 使用任意变量接收

###14、Promise 对象

是异步编程的一种解决方案。

从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。

Promise 状态

状态的特点

   Promise 异步操作有三种状态:pending(进行中)、fulfilled(已成功)和 rejected(已失败)。除了异步操作的结果,任何其他操作都无法改变这个状态。

Promise 对象只有:从 pending 变为 fulfilled 和从 pending 变为 rejected 的状态改变。只要处于 fulfilled 和 rejected ,状态就不会再变了即 resolved(已定型)。

const p1 = new Promise(function(resolve,reject){ resolve('success1'); resolve('success2'); }); const p2 = new Promise(function(){ resolve('success3'); reject('reject'); }); p1.then(function(value){ console.log(value); // success1 }); p2.then(function(value){ console.log(value); // success3 });

状态的缺点

无法取消 Promise ,一旦新建它就会立即执行,无法中途取消。

如果不设置回调函数,Promise 内部抛出的错误,不会反应到外部。

当处于 pending 状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。

then 方法

   then 方法接收两个函数作为参数,第一个参数是 Promise 执行成功时的回调,第二个参数是 Promise 执行失败时的回调,两个函数只会有一个被调用。

then 方法的特点

  • 在 JavaScript 事件队列的当前运行完成之前,回调函数永远不会被调用。
    const p = new Promise(function(resolve,reject){ resolve('success'); }); p.then(function(value){ console.log(value); }); onsole.log('first'); // first // success

  • 通过 .then 形式添加的回调函数,不论什么时候,都会被调用。
    通过多次调用
    .then
    可以添加多个回调函数,它们会按照插入顺序并且独立运行。
    const p = new Promise(function(resolve,reject){ resolve(1); }).then(function(value){ // 第一个then // 1 console.log(value); return value * 2; }).then(function(value){ // 第二个then // 2 console.log(value); }).then(function(value){ // 第三个then // undefined console.log(value); return Promise.resolve('resolve'); }).then(function(value){ // 第四个then // resolve console.log(value); return Promise.reject('reject'); }).then(function(value){ // 第五个then //reject:reject console.log('resolve:' + value); }, function(err) { console.log('reject:' + err); });

  • then 方法将返回一个 resolved 或 rejected 状态的 Promise 对象用于链式调用,且 Promise 对象的值就是这个返回值。

简便的 Promise 链式编程最好保持扁平化,不要嵌套 Promise。

####15、Generator 函数

ES6 新引入了 Generator 函数,可以通过 yield 关键字,把函数的执行流挂起,为改变执行流程提供了可能,从而为异步编程提供解决方案。 

Generator 函数组成

Generator 有两个区分于普通函数的部分:
  • 在 function 后面,函数名之前有个 * ;

  • 函数内部有 yield 表达式。

function* func(){ console.log("one"); yield '1'; console.log("two"); yield '2'; console.log("three"); return '3'; }

执行机制

调用 Generator 函数和调用普通函数一样,在函数名后面加上()即可,但是 Generator 函数不会像普通函数一样立即执行,而是返回一个指向内部状态对象的指针,所以要调用遍历器对象Iterator 的 next 方法,指针就会从函数头部或者上一次停下来的地方开始执行。

f.next(); // one // {value: "1", done: false} f.next(); // two // {value: "2", done: false} f.next(); // three // {value: "3", done: true} f.next(); // {value: undefined, done: true}

函数返回的遍历器对象的方法

next 方法

一般情况下,next 方法不传入参数的时候,yield 表达式的返回值是 undefined 。当 next 传入参数的时候,该参数会作为上一步yield的返回值。

function* sendParameter(){ console.log("strat"); var x = yield '2'; console.log("one:" + x); var y = yield '3'; console.log("two:" + y); console.log("total:" + (x + y)); }

next不传参

var sendp1 = sendParameter(); sendp1.next(); // strat // {value: "2", done: false} sendp1.next(); // one:undefined // {value: "3", done: false} sendp1.next(); // two:undefined // total:NaN // {value: undefined, done: true} next传参 var sendp2 = sendParameter(); sendp2.next(10); // strat // {value: "2", done: false} sendp2.next(20); // one:20 // {value: "3", done: false} sendp2.next(30); // two:30 // total:50 // {value: undefined, done: true}

return 方法

return 方法返回给定值,并结束遍历 Generator 函数。

 return 方法提供参数时,返回该参数;不提供参数时,返回 undefined 。

function* foo(){ yield 1; yield 2; yield 3; } var f = foo(); f.next(); // {value: 1, done: false} f.return("foo"); // {value: "foo", done: true} f.next(); // {value: undefined, done: true} throw 方法 throw 方法可以再 Generator 函数体外面抛出异常,再函数体内部捕获。 var g = function* () { try { yield; } catch (e) { console.log('catch inner', e); } }; var i = g(); i.next(); try { i.throw('a'); i.throw('b'); } catch (e) { console.log('catch outside', e); } // catch inner a // catch outside b

yield* 表达式

yield* 表达式表示 yield 返回一个遍历器对象,用于在 Generator 函数内部,调用另一个 Generator 函数。

function* callee() { console.log('callee: ' + (yield)); } function* caller() { while (true) { yield* callee(); } } const callerObj = caller(); callerObj.next(); // {value: undefined, done: false} callerObj.next("a"); // callee: a // {value: undefined, done: false} callerObj.next("b"); // callee: b // {value: undefined, done: false} // 等同于 function* caller() { while (true) { for (var value of callee) { yield value; } } }

使用场景

实现 Iterator

为不具备 Iterator 接口的对象提供遍历方法。

###16、async 函数

async 是 ES7 才有的与异步操作有关的关键字,和 Promise , Generator 有很大关联的。

语法

async function name([param[, param[, ... param]]]) { statements }

  • name: 函数名称。
  • param: 要传递给函数的参数的名称。
  • statements: 函数体语句。

返回值

  async 函数返回一个 Promise 对象,可以使用 then 方法添加回调函数。

async function helloAsync(){ return "helloAsync"; } console.log(helloAsync()) // Promise {<resolved>: "helloAsync"} helloAsync().then(v=>{ console.log(v); // helloAsync })

   async 函数中可能会有 await 表达式,async 函数执行时,如果遇到 await 就会先暂停执行 ,等到触发的异步操作完成后,恢复 async 函数的执行并返回解析值。

  await 关键字仅在 async function 中有效。如果在 async function 函数体外使用 await ,你只会得到一个语法错误

function testAwait(){ return new Promise((resolve) => { setTimeout(function(){ console.log("testAwait"); resolve(); }, 1000); }); } async function helloAsync(){ await testAwait(); console.log("helloAsync"); } helloAsync(); // testAwait // helloAsync

await

    await 操作符用于等待一个 Promise 对象, 它只能在异步函数 async function 内部使用。

a、语法
[return_value] = await expression;
expression: 一个 Promise 对象或者任何要等待的值。
b、返回值
返回 Promise 对象的处理结果。如果等待的不是 Promise 对象,则返回该值本身。
如果一个 Promise 被传递给一个 await 操作符,await 将等待 Promise 正常处理完成并返回其处理结果。
function testAwait (x) { return new Promise(resolve => { setTimeout(() => { resolve(x); }, 2000); }); } async function helloAsync() { var x = await testAwait ("hello world"); console.log(x); } helloAsync (); // hello world
正常情况下,await 命令后面是一个 Promise 对象,它也可以跟其他值,如字符串,布尔值,数值以及普通函数。
function testAwait(){ console.log("testAwait"); } async function helloAsync(){ await testAwait(); console.log("helloAsync"); } helloAsync(); // testAwait // helloAsync
c、await针对所跟不同表达式的处理方式:
Promise 对象:await 会暂停执行,等待 Promise 对象 resolve,然后恢复 async 函数的执行并返回解析值。
非 Promise 对象:直接返回对应的值

猜你喜欢

转载自blog.csdn.net/qq_34484157/article/details/88867634