# ES6 参考: 阮一峰es6 https://wangdoc.com/es6/ ## let const 推荐使用let const 关键字替代 var关键字声明变量,因为 var存在诸多问题 * 越域 ```js { var a = 1; let b = 2; } console.log(a); // 1 console.log(b); // ReferenceError: b is not defined ``` * var 可以声明多次 * var 会变量提升 ## 解构 > 把复合数据类型(对象居多, 数组较少)快速解析成单个变量,而无需使用arr[x]或者obj[key]等传统方式进行赋值 ### 数组解构赋值 ```js var arr = ['this is a string', 2, 3]; //传统方式 var a = arr[0], b = arr[1], c = arr[2]; //解构赋值,是不是简洁很多? var [a, b, c] = arr; console.log(a);//this is a string console.log(b);//2 console.log(c);//3 // 如果想跳过某个元素, 只需多写一个逗号 ``` ### 嵌套数组解构 ```js var arr = [[1, 2, [3, 4]], 5, 6]; var [[d, e, [f, g]], h, i] = arr; console.log(d);//1 console.log(f);//3 console.log(i);//6 ``` ### 函数传参解构 ```js var arr = ['this is a string', 2, 3]; function fn1([a, b, c]) { console.log(a); console.log(b); console.log(c); } fn1(arr); //this is a string //2 //3 ``` ### for循环解构 ```js var arr = [[11, 12], [21, 22], [31, 32]]; for (let [a, b] of arr) { console.log(a); console.log(b); } //11 //12 //21 //22 //31 //32 ``` ### 对象赋值解构 对象的解构必须要用属性名, 不是根据顺序, 而是属性名对应 ```js var obj = { name: 'chris', sex: 'male', age: 26, son: { sonname: '大熊', sonsex: 'male', sonage: 1 } }; var {name, sex, age, son} = obj; console.log(name + ' ' + sex + ' ' + age); //chris male 26 console.log(son); // { sonname: '大熊', sonsex: 'male', sonage: 1 } ``` ### 对象传参解构 ```js var obj = { name: 'chris', sex: 'male', age: 26, son: { sonname: '大熊', sonsex: 'male', sonage: 1 } }; function fn2({sex, age, name}) { console.log(name + ' ' + sex + ' ' + age); } fn2(obj); //chris male 26 ``` ### 解构重命名变量 ```js var obj = { name: 'chris', sex: 'male', age: 26 }; var {name: nickname, age: howold} = obj; console.log(nickname + ' ' + howold); //chris 26 ``` 对象的解构赋值是下面形式的简写 ```js let { foo: foo, bar: bar } = { foo: 'aaa', bar: 'bbb' } ``` 对象的解构赋值的内部机制,是先找到同名属性,然后再赋给对应的变量。真正被赋值的是后者,而不是前者。 ```js let { foo: baz } = { foo: 'aaa', bar: 'bbb' } baz // "aaa" foo // error: foo is not defined ``` ```js const node = { loc: { start: { line: 1, column: 5 } } } let { loc, loc: { start }, loc: { start: { line } } } = node line // 1 loc // Object {start: Object} start // Object {line: 1, column: 5} ``` 上面代码有三次解构赋值,分别是对 loc、start、line 三个属性的解构赋值。注意,最后一次对 line 属性的解构赋值之中,只有 line 是变量,loc 和 start 不是变量。 ### 嵌套对象解构 ```js var obj = { name: 'chris', sex: 'male', age: 26, son: { sonname: '大熊', sonsex: 'male', sonage: 1 } }; var {name, sex, age, son: {sonname, sonsex, sonage}} = obj; console.log(sonname + ' ' + sonsex + ' ' + sonage); //大熊 male 1 //Babel暂不支持这种嵌套解构 obj = { name: 'chris', sex: 'male', age: [1, 2, 3] } {name, sex, age: [a, b, c]} = obj; console.log(c); ``` ### 对象属性重名 需要更改变量名: ```js var obj = { name: 'chris', sex: 'male', age: 26, son: { name: '大熊', sex: 'male', age: 1 } }; //赋值解构 var {name: fathername, son: {name, sex, age}} = obj; console.log(fathername); //chris console.log(name); //大熊 //传参解构 function fn3({sex, age, name, son: {name: sonname}}) { console.log(name + ' ' + sex + ' ' + age + ' ' + sonname); } fn3(obj); //chris male 26 大熊 ``` ### 循环解构对象 ```js var arr = [{name: 'chris', age: 26}, {name: 'jack', age: 27}, {name: 'peter',age: 28}]; for (let {age, name} of arr) { console.log(name + ' ' + age); } //chris 26 //jack 27 //peter 28 ``` ### 定义默认值 > 在解构过程中,如果某个路径上的属性不存在,可以使用默认值。 ```js const user = { id: 123, name: 'hehe' }; const { education: { school: { name } } = { school: { name: 'NB' } } } = user; console.log(name); //prints: NB ``` ### 解构的特殊应用场景 ```js //变量互换 var x = 1, y = 2; var [x, y] = [y, x]; console.log(x); //2 console.log(y); //1 //字符串解构 var str = 'love'; var [a, b, c, d] = str; console.log(a);//l console.log(b);//o console.log(c);//v console.log(d);//e ``` ## 展开运算符 展开运算符用三个点号表示,功能是把数组或类数组对象展开成一系列用逗号隔开的值 ```js var foo = function(a, b, c) { console.log(a); console.log(b); console.log(c); } var arr = [1, 2, 3]; //传统写法 foo(arr[0], arr[1], arr[2]); //使用展开运算符 foo(...arr); //1 //2 //3 ``` 特殊应用场景: ```js //数组深拷贝 var arr2 = arr; var arr3 = [...arr]; console.log(arr===arr2); //true, 说明arr和arr2指向同一个数组 console.log(arr===arr3); //false, 说明arr3和arr指向不同数组 //把一个数组插入另一个数组字面量 var arr4 = [...arr, 4, 5, 6]; console.log(arr4);//[1, 2, 3, 4, 5, 6] //字符串转数组 var str = 'love'; var arr5 = [...str]; console.log(arr5);//[ 'l', 'o', 'v', 'e' ] ``` ## 剩余运算符 剩余运算符也是三个点号,不过其功能与展开运算符恰好相反,把逗号隔开的值序列组合成一个数组 ```js //主要用于不定参数,所以ES6开始可以不再使用arguments对象 var bar = function(...args) { for (let el of args) { console.log(el); } } bar(1, 2, 3, 4); //1 //2 //3 //4 bar = function(a, ...args) { console.log(a); console.log(args); } bar(1, 2, 3, 4); //1 //[ 2, 3, 4 ] ``` 剩余运算符配合解构使用: ```js var [a, ...rest] = [1, 2, 3, 4]; console.log(a);//1 console.log(rest);//[2, 3, 4] ``` ## 结构展开运算符小结 等号表达式是典型的赋值形式,函数传参和for循环的变量都是特殊形式的赋值。解构的原理是赋值的两边具有相同的解构,就可以正确取出数组或对象里面的元素或属性值,省略了使用下标逐个赋值的麻烦。 ==对于三个点号,三点放在形参或者等号左边为剩余运算符; 放在实参或者等号右边为展开运算符。== ## 结构展开运算符经验 * 在等号赋值或for循环中,如果需要从数组或对象中取值,尽量使用解构。 * 在自己定义函数的时候,如果调用者传来的是数组或对象,形参尽量使用解构方式,优先使用对象解构,其次是数组解构。代码可读性会很好。 * 在调用第三方函数的时候,如果该函数接受多个参数,并且你要传入的实参为数组,则使用展开运算符。可以避免使用下标形式传入参数。也可以避免很多人习惯的使用apply方法传入数组。 * 剩余运算符使用场景应该稍少一些,主要是处理不定数量参数,可以避免arguments对象的使用。 ## 可选链操作符 > 读取对象内部的某个属性,往往需要判断一下,属性的上层对象是否存在,当其中一链为null或者undefined时就返回undefined,这样即使中间缺少一个属性也不会报错 ```js const firstName = message?.body?.user?.firstName || 'default'; ``` ## 空值合并操作符 > 空值合并操作符(`??`)是一个逻辑操作符,当左侧的操作数为null或者undefined时,返回其右侧操作数,否则返回左侧操作数。 如果使用 `||` 来为某些变量设置默认值,可能会遇到意料之外的行为。比如为假值(例如,`''` 或 `0`)时。 ```js const value1 = 0 || 'default'; // 'default'(因为 0 是假值) const value2 = 0 ?? 'default'; // 0(因为 0 不是 null 或 undefined) const value3 = '' || 'default'; // 'default'(因为空字符串是假值) const value4 = '' ?? 'default'; // ''(因为空字符串不是 null 或 undefined) const value5 = null ?? 'default'; // 'default'(因为 null 是空值) const value6 = undefined ?? 'default'; // 'default'(因为 undefined 是空值) ``` ## 函数的默认值 ```js function show(msg = "...") { console.log(msg) } show() show('test') ``` * 如果一个函数有多个形参也有默认值: 默认值一定要在后面 ```js function test(a, b, c =1){} // 不能是 function test(a=1, b, c){} ``` ## 箭头函数/lamda表示式 ```js // 两个参数的情况: let sum = function (a, b) { return a + b; } // 简写为: //当只有一行语句,并且需要返回结果时,可以省略 {} , 结果会自动返回。 let sum2 = (a, b) => a + b; //测试调用 console.log(sum2(10, 10));//20 // 代码不止一行,可以用`{}`括起来 let sum3 = (a, b) => { c = a + b; return c; }; //测试调用 console.log(sum3(10, 20));//30 ``` ## 模板字符串 ```js `${变量}` let info = "你好,我的名字是:【"+name+"】,年龄是:【"+age+"】,邮箱是:【】" console.log(info); # 模板字符串的写法 let info = `你好,我的名字是:${name},年龄是:${person.age},邮箱是:${person.email}` console.log(info); ``` ## fetch api 另建笔记 ## Promise 另建笔记 ## async 另建笔记 ## Set > 可以理解为不重复的数组 ```js const set = new Set([1, 5, 3, 4]) set.add(5) // 不能重复 set.delete(3) // 按照内容删除, 没有下标概念, 遍历获取不了index set.has(4) // true console.log(set) ``` 使用: 数组去重 ## Map > 键值对的集合 Map和Object的区别 * Map 的键可以是任意值,包括函数、对象或任意基本类型 * Object 的键必须是一个 String 或是 Symbol * Map 的键值对个数可以轻易地通过 size 属性获取 * Object 的键值对个数只能手动计算 * Map 是 可迭代的 的,所以可以直接被迭代 * Object 没有实现迭代协议, 所以不能用 for...of 表达式 * Map 在频繁增删键值对的场景下性能更好 * Map 没有元素的序列化和解析的支持 ```js const map = new Map() map.set('a', 1) // 添加 map.set('b', 2) map.has('a') // 判断是否存在 map.get('a') // 获取值 map.size // 获取长度 map.keys() // 获取keys map.forEach((value,key)=>{console.log(`${key}=${value}`)}) // 遍历 map.delete('a') // 删除 map.clear() // 清空 ``` ## Symbol > 表示唯一的不可变的值, 通常用于创建对象的唯一属性键 * 每个 `symbol` 的值都是唯一的, 即使他们的描述相同 * 可以用于对象的键名, 避免冲突 ```js const sym1 = Symbol('key') const sym2 = Symbol('key') console.log(sym1 === sym2) // false const obj = { [sym1]: 1, [sym2]: 2} ``` ## BigInt > 表示任意精度的整数, 可以表示大于 `Number.MAX_SAFE_INTEGER` (9007199254740991) 的整数, 或者小于 `Number.MIN_SAFE_INTEGER` (-9007199254740991) 的负整数 * 在数字后面加 `n` 来创建 * 不能和 `Number` 混合运算 ```js const big = 9007199254740992n console.log(big) console.log(big + 1n) ``` ## Class 另建笔记 ## Proxy > 创建一个对象的代理,从而实现基本操作的拦截和自定义(如属性查找、赋值、枚举、函数调用等) ```js const p = new Proxy(target, handler) ``` ```js let target = { name: "Alice", age: 25 }; let handler = { get(target, prop) { console.log(`读取属性:${prop}`); return target[prop]; }, set(target, prop, value) { console.log(`设置属性:${prop} = ${value}`); target[prop] = value; return true; // 表示设置成功 } }; let proxy = new Proxy(target, handler); proxy.name; // 读取属性:name proxy.age = 30; // 设置属性:age = 30 ``` ## for...of > 执行一个循环,该循环处理来自可迭代对象的值序列。可迭代对象包括内置对象的实例,例如 Array、String、TypedArray、Map、Set、NodeList(以及其他 DOM 集合),还包括 arguments 对象、由生成器函数生成的生成器,以及用户定义的可迭代对象 ```js const array1 = ["a", "b", "c"]; for (const element of array1) { console.log(element); } ``` * **支持所有可迭代对象**:`for...of` 不仅可以遍历数组,还可以遍历其他可迭代对象,如 `Map`、`Set`、字符串等。 * **支持 `break` 和 `continue`**:在 `for...of` 循环中,你可以使用 `break` 来提前终止循环,或者使用 `continue` 跳过当前迭代。 * **支持异步操作**:在 `for...of` 循环中,你可以使用 `await` 来处理异步操作,而 `forEach` 不支持。 `for .. in` 是 es5 就有的, 专门用来遍历对象的. 也可间接通过 `Object.keys(obj)` 来遍历 ```js const object = { a: 1, b: 2, c: 3 }; for (const property in object) { console.log(`${property}: ${object[property]}`); } ``` ## import export ```js // import * as from 路径 import * as dayjs from 'dayjs' // export * form 路径 export * from "./demandConfirmRecord" ``` ## 模块化 `type="module"` 将 JavaScript 程序拆分为可按需导入的单独模块的机制 ```html