Loading... ## ECMAScript 6 (ES6新语法)- 第一篇章 ### 前言 ECMAScript 6 目前基本成为业界标准,它的普及速度比 ES5 要快很多,主要原因是现代浏览器对 ES6 的支持相当迅速,尤其是 Chrome 和 Firefox 浏览器,已经支持 ES6 中绝大多数的特性。 ### let 和 const 命令 **ES6**新增了 `let`命令,用来声明变量。它的语法类似于 `var`。但是 `let`所声明的变量,只有在 `let`命令所在的代码块中有效。 #### let **差异1:作用域不同** ```javascript //使用var { var i = 10; } console.logo(i); //输出打印结果为10 //使用let { let i = 10; } console.log(i); //执行错误:i is not defined ``` **差异2:let没有变量提升与暂时性死区** ```javascript console.log(temp); //代码执行到此处的时候,temp尚未声明,执行错误“temp is not defined” let temp = "hello"; //变量成名成功后,才可以使用temp变量 //如果使用var声明变量时 console.log(temp); //此处输入打印Undefined,并未引发异常 var temp = "Hello"; ``` **差异3:let变量不能重复声明** ```javascript let temp = "hello"; let temp = "csust"; //执行错误: Identifier 'temp' has already been declared ``` **面试题分享** ```javascript var a = 9; //全局变量a fun(); //fun()是函数,调用在前,定义在后(函数声明会提升到作用域的顶部优先声明) console.log(a); //此处的a为全局变量a,输出结果为9 function fun(){ console.log(a); //此处的a为fun()函数中变量,刚刚说到,函数声明会优先声明,此处的a为undefined var a = 10; //声明变量后,a的值为10,但是全局变量a的值不会发生变化 console.log(a); //正常输出10 } /* * 请写出此段代码的运行结果? * 程序输出: * undefined * 10 * 9 */ ``` #### const `const`声明一个只读的常量。一旦声明,常量的值就不能改变。 代码分析1: ```javascript const txt = "你好!"; txt = "Hello!"; //代码执行错误:Assignment to constant variable. console.log(txt); ``` 代码分析2: ```javascript const info = function(){ this.a = "10"; console.log(a); } info.a = "1000"; info(); //输出? [10] console.log(info.a); //输出? [1000] ``` **注意**: `const`声明的常量必须初始化 ### 变量的结构赋值 ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构。 #### 数组语法 - 声明: let [name1,name2...] = [value1,value3...]; - 使用: 直接使用名称即可 - 注意: 等号右边必须是一个数组 - 如何使用默认值`javascript let [name = '张三', age = 20] = [];` #### 对象语法 - 声明: `let { name, age } = { name: ‘jack', age: 20 };` - 使用: 直接使用名称即可 - 注意: 对象的解构与数组有一个重要的不同。数组的元素是按次序排列的,变量的取值由它的位置决定;而对象的属性没有次序,变量必须与属性同名,才能取到正确的值。 - 如果不想使用同名操作,则可以使用如下方法 ```javascript // 方式1 let {foo: baz } ={ foo: 'aaa',bar: 'bbb' }; // 方式2 let obj = { first: 'hello', last: 'world' }; let { first: f, last: l } = obj; ``` 对象也支持默认值 - **不管是数组还是对象,如果解构失败,则变量的值等于undefined。** #### 常见应用 **两数交换** ```javascript let x = 1,y = 2; //声明变量 [x, y] = [y, x]; //执行交换 console.log(`x:${x} y:${y}`); /* 输出结果为: * x:2 y:1 */ ``` **函数返回多个数据值** - **返回一个数组** ```javascript function example() { return [1, 2, 3]; //此处返回数组 } let [a, b, c] = example(); //此处将返回结果依次赋值给a、b、c三个变量 ``` - **返回一个对象** ```javascript function example() { return { foo: 1, bar: 2 }; } let { foo, bar } = example(); ``` - **函数参数的定义** - 参数是一组有次序的值 ```javascript function f([x, y, z]) { ... } //方法声明 f([1, 2, 3]); //调用方法 ``` - 参数是一组无次序的值 ```javascript function f({x, y, z}) { ... } //方法定义 f({z: 3, y: 2, x: 1}); //方法调用 ``` - **提取JSON数据** ```javascript let jsonData = { id: 42, status: "OK", data: [867, 5309] }; let { id, status, data: number } = jsonData; console.log(id, status, number); //42, "OK", [867, 5309] ``` - **遍历Map对象** ```javascript let map = new Map(); map.set('first', 'hello'); map.set('second', 'world'); for (let [key, value] of map) { console.log(key + " is " + value); } // 输出结果: // first is hello // second is world ``` ### 字符串的扩展 #### 模板字符串 - **语法** ```javascript let text = `普通文本${变量名}普通文本...`; //以往的字符串使用单引号('')或者双引号("") //此处的模板字符串使用反单引号(``) ``` - **案例** ```javascript let [name,age,className] = ['jack', 10, '理工30班']; //以往的字符串拼接输出 let sayHi = '你好,我叫:'+name+',我今年:'+age+"岁,来自于:"+className; console.log(sayHi); //模板字符串输出 let sayHello = `你好,我叫:${name},我今年:${age}岁,来自于:${className}`; console.log(sayHello); ``` - **注意**:模板字符串支持多行文本,能正常解析空格和回车 #### 扩展应用 - **支持函数调用** ```javascript function getText(){ return 1+1; } let text = `方法的返回值是:${getText()}`; console.log(text); // 输出结果 // 方法的返回值是:2 ``` - **注意**:如果模板字符串中的变量没有声明,将报错。 - **标签模板** - 案例1 ```javascript alert `hello` 等价于 alert('hello') ``` - 案例2 ```javascript function test(text,age1,age2){ console.log(text[0]); console.log(text[1]); console.log(text[2]); console.log(age1); console.log(age2); } let [a,b] = [10,20]; test `Hello world! ${a+b} ${a*b}`; //调用 /* 参数对应: * text: Hello world! * age1: a+b * age2: a*b * * 输出结果: * Hello world! * * * 30 * 200 */ ``` - 案例3 ```javascript function test(text,age1,age2){ console.log(text[0]); console.log(text[1]); console.log(text[2]); console.log(age1); console.log(age2); reutrn 'Home'; } let [a,b] = [10,20]; let result = test `Hello ${a+b} world! ${a*b} 你好!`; /* 参数对应: * text: Hello world! * age1: a+b * age2: a*b * * 输出结果: * Hello * world! * 你好! * 30 * 200 */} ``` ### 字符串新增的方法 #### `String.raw()` **作用:** 该方法返回一个斜杠都被转义(即斜杠前面再加一个斜杠)的字符串 ```javascript let txt = String.raw`在C#中的换行转义符是:\n`; console.log(txt); //方法返回结果: ‘在C#中的换行转义符是:\\n’ //页面显示结果: 在C#中的换行转义符是:\n ``` #### 实例方法:`includes()`,` startsWith()`, `endsWith()` - 传统上,JavaScript 只有indexOf方法,可以用来确定一个字符串是否包含在另一个字符串中。 - ES6 又提供了三种新方法。 - includes(字符串,开始搜索的位置):返回布尔值,表示是否找到了参数字符串。 - startsWith(字符串,开始搜索的位置):返回布尔值,表示参数字符串是否在原字符串的头部。 - endsWith(字符串,结束搜索的位置):返回布尔值,表示参数字符串是否在原字符串的尾部。 ```javascript let s = 'Hello world!'; s.startsWith('Hello') // true s.endsWith('lo',5) // true s.includes('o') // true ``` #### 实例方法:`repeat()` **作用:** repeat方法返回一个新字符串,表示将原字符串重复n次 ```javascript 'hello'.repeat(2) // "hellohello" 'hello'.repeat(0.9) // "" ``` - 注意 - 参数如果是小数,会被取整。 - 如果repeat的参数是负数或者**Infinity**,会报错。 - 但是: 如果参数是 0 到-1 之间的小数,则等同于 0 - 参数NaN等同于 0 - 如果repeat的参数是字符串,则会先转换成数字 #### 实例方法:`padStart()`,`padEnd()` 字符串补全长度的功能。如果某个字符串不够指定长度,会在头部或尾部补全。`padStart()`用于头部补全,`padEnd()`用于尾部补全。 ```javascript 'cf'.padEnd(6,'k'); // "cfkkkk" 'cf'.padStart(6,'k'); // "kkkkcf" ``` **注意:** - 如果省略第二个参数,默认使用空格补全长度。 - 如果用来补全的字符串与原字符串,两者的长度之和超过了最大长度,则会截去超出位数的补全字符串。 ```javascript 'abc'.padStart(10, '0123456789') // '0123456abc’ ``` #### 实例方法:`trimStart()`,`trimEnd()` `trimStart()`消除字符串头部的空格,`trimEnd()`消除尾部的空格。 ### 数值的扩展 #### `Number.isFinite()`, `Number.isNaN()` - `Number.isFinite()` 函数用于检查其参数是否是无穷大。 - 如果 number 是 NaN(非数字),或者是正、负无穷大的数,则返回 false - Number.isNaN() 函数用于检查一个其参数否为NaN(非数字) - 如果number是数字,则返回: false,否则返回true #### `Number.parseInt()`,`Number.parseFloat()` - ES6 将全局方法parseInt()和parseFloat(),移植到Number对象上面,行为完全保持不变。 #### `Number.isInteger()` - `Number.isInteger()`用来判断一个数值是否为整数。 - 如果参数不是数值,Number.isInteger返回false。 ```javascript Number.isInteger(25) == Number.isInteger(25.0) //整数和浮点数采用的是同样的储存方法,所以 25 和 25.0 被视为同一个值。 ``` #### Math对象的扩展 **`Math.trunc(number)`** 方法用于去除一个数的小数部分,返回整数部分。 **`Math.sign()`** 方法用来判断一个数到底是正数、负数、还是零。对于非数值,会先将其转换为数值。 - 参数为正数,返回+1; - 参数为负数,返回-1; - 参数为 0,返回0; - 参数为-0,返回-0; - 其他值,返回NaN。 **`Math.imul(num1,num2)`** 方法返回两个数以 32 位带符号整数形式相乘的结果,返回的也是一个 32 位的带符号整数。 ### 函数的扩展 #### 函数参数默认值 ES6 允许为函数的参数设置默认值,即直接写在参数定义的后面。 ```javascript function log(x, y = 'World') { console.log(x, y); } log(‘Hello’) // Hello World log('Hello', 'China') // Hello China log('Hello', '') // Hello ``` **注意:** - 在函数中,已经定义的参数不能再次被声明 ```javascript function foo(x = 5) { let x = 1; // error const x = 2; // error } ``` - 使用参数默认值时,函数不能有同名参数。 ```javascript // 不报错 function foo(x, x, y) { // ... } // 报错 function foo(x, x, y = 1) { // ... } ``` #### 函数默认值与解构赋值默认值结合使用 ```javascript //演示案例1 function foo({x, y = 5}) { console.log(x, y); } foo({}) // undefined 5 foo({x: 1}) // 1 5 foo({x: 1, y: 2}) // 1 2 foo() // TypeError: Cannot read property 'x' of undefined /* * 此代码只使用了对象的解构赋值默认值,没有使用函数参数的默认值。 * 只有当函数foo的参数是一个对象时,变量x和y才会通过解构赋值生成。 * 如果函数foo调用时没提供参数,变量x和y就不会生成,从而报错。 * 通过提供函数参数的默认值,就可以避免这种情况。 */ //演示案例2 function foo({x, y = 5} = {}) { console.log(x, y); } foo() // undefined 5 /*通常情况下,定义了默认值的参数,应该是函数的尾参数。*/ ``` #### rest参数 ES6 引入 rest 参数(形式为...变量名),用于获取函数的多余参数,这样就不需要使用arguments对象了。rest 参数搭配的变量是一个数组,该变量将多余的参数放入数组中。(**可以理解为可变参数**) ```javascript //演示案例1: function add(...nums) { let sum = 0; for (var val of nums) { sum += val; } return sum; } add(2, 5, 3) // 10 //注意,rest 参数之后不能再有其他参数(即只能是最后一个参数),否则会报错。 //演示案例2: const sortNumbers = (...numbers) => numbers.sort(); let result = sortNumbers(5,4,3,1,2); console.log(result); // [1,2,3,4,5] ``` #### 严格模式 ```javascript function doSomething(a, b) { 'use strict'; // 省略代码 } //注意:规定只要函数参数使用了默认值、解构赋值、或者扩展运算符,那么函数内部就不能显式设定为严格模式,否则会报错。 ``` #### name属性 函数的name属性,返回该函数的函数名。 #### 箭头函数 ```javascript var f = v => v; // 等同于 var f = function (v) { return v; }; //注意:在箭头函数中,this指向是无效的 ``` - 注意事项 - 如果箭头函数不需要参数或需要多个参数,就使用一个圆括号代表参数部分。 ```javascript var f = () => 5; // 等同于 var f = function () { return 5 }; /**********************************************/ var sum = (num1, num2) => num1 + num2; // 等同于 var sum = function(num1, num2) { return num1 + num2; }; ``` - 如果箭头函数的代码块部分多于一条语句,就要使用大括号将它们括起来,并且使用return语句返回。 - ```javascript var sum = (num1, num2) => { return num1 + num2; } ``` - 由于大括号被解释为代码块,所以如果箭头函数直接返回一个对象,必须在对象外面加上括号,否则会报错。 ```javascript // 报错 let getTempItem = id => { id: id, name: "Temp" }; // 不报错 let getTempItem = id => ({ id: id, name: "Temp" }); ``` #### 函数参数的尾逗号 ES2017 允许函数的最后一个参数有尾逗号。 #### `catch`命令的参省略 ```javascript try { // ... } catch { //省略参数 // ... } ``` ### 数组的扩展 #### 扩展运算符 扩展运算符是三个点(...)。将一个数组转为用逗号分隔的参数序列。 ```javascript //演示案例1:普通使用 console.log(...[1, 2, 3]) // 1 2 3 console.log(1, ...[2, 3, 4], 5) // 1 2 3 4 5 //演示案例2:方法调用 function add(x, y) { return x + y; } const numbers = [4, 38]; add(...numbers) // 42 //演示案例3:求取数组中的最大值 Math.max(...[14, 3, 77]) //演示案例4: 数组追加元素 let arr1 = [0, 1, 2]; let arr2 = [3, 4, 5]; arr1.push(...arr2); //0,1,2,3,4,5 //演示案例5:数组克隆 const a1 = [1, 2]; const a2 = [...a1]; //演示案例6:数组合并 const arr1 = ['a', 'b']; const arr2 = ['c']; const arr3 = ['d', 'e']; let newArr = [...arr1,...arr2,...arr3]; // ["a", "b", "c", "d", "e"] ``` ### 与解构赋值结合 ```javascript let [first, ...nums] = [1, 2, 3, 4, 5]; // first => 1 nums => [2, 3, 4, 5] const [first, ...nums] = []; // first => undefined nums => [] const [first, ...nums] = ["foo"]; // first => "foo" nums => [] ``` **注意:** 如果将扩展运算符用于数组赋值,只能放在参数的最后一位,否则会报错。 #### 扩展运算符 **字符串切割**:`let txt = [...‘hello’]; // ["h", "e", "l", "l", "o"]` **注意:** 如果将扩展运算符用于数组赋值,只能放在参数的最后一位,否则会报错。 #### Array.of() **Array.of方法用于将一组值,转换为数组。** ```javascript Array.of(3, 11, 8) // [3,11,8] Array.of(3) // [3] Array.of(3).length // 1 /* * 这个方法的主要目的,是弥补数组构造函数Array()的不足。 * 因为参数个数的不同,会导致Array()的行为有差异。 */ Array() // [] Array(3) // [, , ,] //声明一个数组,长度默认为3 【差异】 Array(3, 11, 8) // [3, 11, 8] ``` #### includes()方法 **作用:** Array.prototype.includes方法返回一个布尔值,表示某个数组是否包含给定的值,与字符串的includes方法类似。 ```javascript [1, 2, 3].includes(2) // true [1, 2, 3].includes(4) // false [1, 2, NaN].includes(NaN) // true //没有该方法之前,我们通常使用数组的indexOf方法,检查是否包含某个值。 ``` #### entries(),keys() 和 values() ES6 提供三个新的方法:**`entries()`,`keys()`和 `values()`** 用于遍历数组。 `keys()`是对键名的遍历、`values()`是对键值的遍历,`entries()`是对键值对的遍历。 --- ### 对象扩展 #### 属性的简介表示法 ```javascript let birth = ‘2020/08/28'; const Person = { name: '张三', //等同于birth: birth birth, // 等同于hello: function () { ... } hello() { console.log('我的名字是', this.name); } }; ``` #### null判断运算符 ```javascript const text = message.text || "hello world"; ``` #### 链判断运算符 ```javascript const firstName = message?.data?.user?.firstName || 'default’; ``` #### Object.is() * ES5 比较两个值是否相等,只有两个运算符:相等运算符(==)和严格相等运算符(===)。它们都有缺点,前者会自动转换数据类型,后者的NaN不等于自身,以及+0等于-0。JavaScript 缺乏一种运算,在所有环境中,只要两个值是一样的,它们就应该相等。 * ES6 提出(同值相等)算法,用来解决这个问题。Object.is就是部署这个算法的新方法。它用来比较两个值是否严格相等,与严格比较运算符(===)的行为基本一致。 ```javascript +0 === -0 //true Object.is(+0,-0) // false ``` #### Object.assign() **语法:**`Object.assign(target, source1, source2,...);` **作用:** 此方法用于对象的合并,将源对象(source1/source2...)的所有可枚举属性,复制到目标对象(target) **注意:** * 方法的第一个参数是目标对象,后面的参数都是源对象。 * 如果目标对象与源对象有同名属性,或多个源对象有同名属性,则后面的属性会覆盖前面的属性。 ```javascript const target = { a: 1, b: 1 }; const source1 = { b: 2, c: 2 }; const source2 = { c: 3 }; Object.assign(target, source1, source2); target // {a:1, b:2, c:3} ``` **常见用法:** * 为对象添加属性 ```javascript class demo { constructor(name, age) { Object.assign(this, { name, age }); } } let info = new demo("jack", 20); //{name: "jack", age: 20} ``` * 为对象添加方法 ```javascript Object.assign(demo.prototype, { doWork(arg1, arg2) { console.log(“你调用了方法”); }, anotherMethod() { //... } }); let info = new demo("jack", 20); //{name: "jack", age: 20} info.doWork() //你调用了方法 ``` #### Object.getOwnPropertyDescriptors() **作用:** 返回指定对象所有自身属性(非继承属性)的描述对象。 #### __proto__属性和Object.create()方法 ```javascript let farther = { sayHello: function() { console.log('父类方法') } } //ES5 const obj = { sayHi: function() { console.log('子类方法') } } obj.__proto__ = farther; obj.sayHello(); //父类方法 obj.sayHi(); //子类方法 /*****************************/ //ES6 const newObj = Object.create(farther); newObj.age = 10; //定义属性 newObj.sayHi = function() { //定义方法 console.log(`子类方法, Age: $ { this.age}`); } newObj.sayHi(); //子类方法,Age:10 newObj.sayHello(); //父类方法 ``` ### Symbol * ES6 引入了一种新的原始数据类型 `Symbol`,表示独一无二的值。它是 JavaScript 语言的第七种数据类型,前六种是:undefined、null、布尔值(`Boolean`)、字符串(`String`)、数值(`Number`)、对象(`Object`)。 * **注意:** `Symbol`函数前不能使用new命令,否则会报错。这是因为生成的 `Symbol `是一个原始类型的值,不是对象。也就是说,由于 `Symbol `值不是对象,所以不能添加属性。基本上,它是一种类似于字符串的数据类型。 ```javascript let s = Symbol(); typeof s // "symbol” ``` * `Symbol`函数的参数只是表示对当前 `Symbol`值的描述,因此相同参数的 `Symbol`函数的返回值是不相等的。 ```javascript // 没有参数的情况 let s1 = Symbol(); let s2 = Symbol(); s1 === s2 // false // 有参数的情况 let s1 = Symbol('foo'); let s2 = Symbol('foo'); s1 === s2 // false ``` * 获取描述信息: ```javascript let sym = Symbol('My symbol'); let txt = `你好,$ {sym.description}`; //`你好,${sym}`,则报错 console.log(txt); //你好,My symbol ``` * **将Symbol转String对象** ```javascript let sym = Symbol('My symbol’); String(sym) // 'Symbol(My symbol)' sym.toString() // 'Symbol(My symbol)’ ``` * **Symbol 值也可以转为布尔值,但是不能转为数值** ```javascript let sym = Symbol(); Boolean(sym) // true ! sym // false if (sym) { // ... } ``` ### Set和Map数据结构 #### SET * 提供了新的数据结构 Set。它类似于数组,但是成员的值都是唯一的,没有重复的值。 * Set本身是一个构造函数,用来生成 Set 数据结构 * ```javascript const s = new Set(); [2, 3, 5, 4, 5, 2, 2].forEach(x = >s.add(x)); for (let i of s) { console.log(i); } //2 3 5 4 ``` **Set 实例的属性和方法** 1. `Set.prototype.size`:返回Set实例的成员总数。 2. `Set.prototype.add(value)`:添加某个值,返回 Set 结构本身。 3. `Set.prototype.delete(value)`:删除某个值,返回一个布尔值,表示删除是否成功。 4. `Set.prototype.has(value)`:返回一个布尔值,表示该值是否为Set的成员。 5. `Set.prototype.clear()`:清除所有成员,没有返回值。 **案例** ```javascript let s = new Set(); s.add(1).add(2).add(2); // 注意2被加入了两次 s.size // 2 s.has(1) // true s.has(2) // true s.has(3) // false s.delete(2); s.has(2) // false //经典案例: 数组去重 function dedupe(array) { return Array.from(new Set(array)); //Array.from方法可以将 Set结构转为数组 } dedupe([1,1, 2, 3]) // [1, 2, 3] ``` **遍历操作** 1. Set.prototype.keys():返回键名的遍历器 [键=值] 2. Set.prototype.values():返回键值的遍历器 3. Set.prototype.entries():返回键值对的遍历器 4. Set.prototype.forEach():使用回调函数遍历每个成员 ```javascript //for of let set = new Set(['red','green','blue']); for (let x of set){ console.log(x); //输出:ren green blue } //forEach set.forEach({value,key} => console.log(key + ":" + value)); //依次输出键值对数据 ``` #### WeakSet * `WeakSet` 结构与`Set` 类似,也是不重复的值的集合。但是,它与`Set` 有两个区别。 * 首先,WeakSet 的成员只能是对象,而不能是其他类型的值。 * 其次,WeakSet 中的对象都是弱引用,即垃圾回收机制不考虑 WeakSet 对该对象的引用,也就是说,如果其他对象都不再引用该对象,那么垃圾回收机制会自动回收该对象所占用的内存,不考虑该对象还存在于 WeakSet 之中。 * **语法:**`const ws = new WeakSet();` #### Map ES6 提供了 Map 数据结构。它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。也就是说,Object 结构提供了“string:object”的对应,Map 结构提供了“object:object”的对应,是一种更完善的 Hash 结构实现。如果你需要“键值对”的数据结构,Map 比 Object 更合适。 **语法-1:** ```javascript //此段代码在新建 Map 实例时,就指定了两个键name和title。 const map = new Map([ ['name', '张三'], ['title', 'Author'] ]); ``` **语法-2:** ```javascript let map = new Map(); map.set(1, 'aaa') .set(1, 'bbb'); //此代码对键1连续赋值两次,后一次的值覆盖前一次的值 map.get(1) // "bbb" ``` **常见属性和方法** 1. `size `属性:返回 Map 结构的成员总数。 2. `Map.prototype.set(key, value)` : set方法设置键名key对应的键值为value,然后返回整个 Map 结构。如果key已经有值,则键值会被更新,否则就新生成该键 3. `Map.prototype.get(key) `: get方法读取key对应的键值,如果找不到key,返回undefined 4. `Map.prototype.has(key) `: has方法返回一个布尔值,表示某个键是否在当前 Map 对象之中 5. `Map.prototype.delete(key)` : delete方法删除某个键,返回true。如果删除失败,返回false 6. `Map.prototype.clear() `: clear方法清除所有成员,没有返回值 **遍历方法** 1. Map.prototype.keys():返回键名的遍历器。 2. Map.prototype.values():返回键值的遍历器。 3. Map.prototype.entries():返回所有成员的遍历器。 4. Map.prototype.forEach():遍历 Map 的所有成员。 ```javascript //此外,Map 还有一个forEach方法,与数组的forEach方法类似,也可以实现遍历。 map.forEach(function(value, key) { console.log("Key: %s, Value: %s", key, value); }); ``` **数组与Map对象互转** ```JavaScript //Map转数组 const map = new Map([ [1, 'one'], [2, 'two'], [3, 'three'], ]); let arr = [...map]; console.log(arr); //[[1, 'one'], [2, 'two'], [3, 'three’]] //数组转Map //直接将数组传入 Map 构造函数,就可以转为 Map。 ``` **Map与Object互转** ```javascript //Map转对象 function strMapToObj(strMap) { let obj = Object.create(null); for (let [k,v] of strMap) { obj[k] = v; } return obj; } //对象转Map let obj = {"a":1, "b":2}; let map = new Map(Object.entries(obj)); ``` **Map转JSON** ```javascript //状态1: Map 的键名都是字符串,这时可以选择转为对象 JSON。 function strMapToJson(strMap){ return JSON.stringify(strMapToObj(strMap)); } let json = strMapToJson(map); console.log(json); // {"1":"one","2":"two","3":"three"} //状态2: Map 的键名有非字符串,这时可以选择转为数组 JSON。 function mapToArrayJson(map) { return JSON.stringify([...map]); } let myMap = new Map().set(true, 7).set({foo: 3}, [‘abc’]); mapToArrayJson(myMap) ; // [[true,7],[{"foo":3},["abc"]]] ``` **JSON转Map** ```javascript //状态1: JSON 转为 Map,正常情况下,所有键名都是字符串。 let json = '{"yes": true, "no": false}'; function jsonToStrMap(jsonStr) { return new Map(Object.entries(JSON.parse(jsonStr)))); } let map = jsonToStrMap(json); //{"yes" => true, "no" => false} //状态2: JSON 就是一个数组,且每个数组成员本身,又是一个有两个成员的数组。这时,它可以一一对应地转为 Map。这往往是 Map 转为数组 JSON 的逆操作。 let json = '[[true,7],[{"foo":3},["abc"]]]'; function jsonToMap(jsonStr) { return new Map(JSON.parse(jsonStr)); } jsonToMap(json); //{true => 7, Object {foo: 3} => ['abc']} ``` #### WeakMap WeakMap结构与Map结构类似,也是用于生成键值对的集合。 WeakMap与Map的区别有两点。 * 首先,WeakMap只接受对象作为键名(null除外),不接受其他类型的值作为键名。 * 其次,WeakMap的键名所指向的对象,不计入垃圾回收机制。 ### Promise对象 含义与概念: * Promise 是异步编程的一种解决方案 * 对象的状态不受外界影响。Promise对象代表一个异步操作,有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。 * 一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pending变为rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果,这时就称为 resolved(已定型)。 Promise的不足: * 首先,无法取消Promise,一旦新建它就会立即执行,无法中途取消。 * 其次,如果不设置回调函数,Promise内部抛出的错误,不会反应到外部。 * 第三,当处于pending状态时,无法得知目前进展到哪一个阶段 **语法** ```Javascript const promise = new Promise(function(resolve, reject) { // ... 省略其他代码 if (/* 异步操作成功 */){ resolve(value); //将对象的状态从pending 变为 resolved [并将异步操作的结果,作为参数传递出去;] } else { reject(error); //将对象的状态从pending 变为 rejected [并将异步操作报出的错误,作为参数传递出去。] } }); //Promise构造函数接受一个函数作为参数,该函数的两个参数分别是resolve和reject。 ``` 案例: ```javascript var myFirstPromise = new Promise(function(resolve, reject){ //当异步代码执行成功时,我们才会调用resolve(...), 当异步代码失败时就会调用reject(...) //在本例中,我们使用setTimeout(...)来模拟异步代码,实际编码时可能是XHR请求或是HTML5的一些API方法. setTimeout(function(){ resolve(”OK!"); //代码正常执行! }, 2500); }); myFirstPromise.then(function(msg){ //successMessage的值是上面调用resolve(...)方法传入的值. //successMessage参数不一定非要是字符串类型,这里只是举个例子 console.log(”数据请求成功,数据信息: " + msg); }); //当我们调用resolve或reject并不会终结 Promise 的参数函数的执行。 new Promise((resolve, reject) => { resolve(1); console.log(2); }).then(r => { console.log(r); }); // 2 // 1 ``` ### async函数 **语法** ```javascript async function name([param[, param[, ... param]]]) { statements } // name: 函数名称。 // param: 要传递给函数的参数的名称。 // statements: 函数体语句。 //async 函数返回一个 Promise对象,可以使用then 方法添加回调函数。 async function sayHi(){ return “你好”; } console.log(sayHi()) // Promise {<resolved>: "你好"} sayHi().then(v =>{ console.log(v); // 你好 }) ``` **注意:** * async 函数中可能会有 await 表达式,async 函数执行时,如果遇到 await 就会先暂停执行 ,等到触发的异步操作完成后,恢复 async 函数的执行并返回解析值。 * await 关键字仅在 async function 中有效。如果在 async function 函数体外使用 await ,你只会得到一个语法错误。await 操作符用于等待一个 Promise 对象, 它只能在异步函数 async function 内部使用。 ```javascript function testAwait() { return new Promise((resolve) = >{ //开启一个新的异步请求 setTimeout(function() { console.log("方法1"); resolve(); //请求完成 }, 1000); }); } async function helloAsync() { await testAwait(); //等待方法执行完成 console.log("方法2"); } helloAsync(); // 方法1 // 方法2 ``` ### Class的基本语法 ES6 的class可以看作只是一个语法糖,它的绝大部分功能,ES5 都可以做到,新的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。 ```javascript /******声明语法-1******/ // 声明类 class Example { constructor(a) { this.a = a; } } // 匿名类 let demo = class { constructor(a) { this.a = a; } } console.log(demo.name); // demo [此处输出为变量名] // 命名类 let demo = class Example { constructor(a) { this.a = a; } } console.log(demo.name); // Example /******声明语法-2******/ class Example { static stuName = 'Jack'; //静态属性 stuAge = 10; //实例属性 constructor(a) { //构造函数 this.a = a; //构造属性 this.sayHello = () =>{ //实例方法 } } sayHi(){ //原型方法 } static doWork(){ //静态方法 } } ``` **类的继承** 通过 `extends` 实现类的继承 **语法:** `class Child extends Father { ... }` 注意: * 子类 constructor 方法中必须有 super ,且必须出现在 this 之前。 * 不可继承常规对象。 ```javascript class People { constructor(nickName,age) { this.nickName = nickName; this.age = age; } sayHi(){ console.log(`你好,我叫:${this.nickName},今年:${this.age}岁`); } } class Student extends People{ constructor(nickName,age,className) { super(nickName,age); //出现在this子句之前,只能出现在子类构造函数中 this.className = className; } sayHi(){ console.log(`你好,我叫:${this.nickName},今年:${this.age}岁,来自于:${this.className}`); } } let lisi = new Student(); // 或者使用: let list = new Student(’李四’,20,’理工20班’); lisi.className = "理工30班"; lisi.nickName = "李四"; lisi.age = 30; lisi.sayHi(); //你好,我叫:李四,今年:30岁,来自于:理工30班 ``` ### Module #### export与import * 模块支持导入导出各种类型的变量,如字符串,数值,函数,类。 * 导出的函数声明与类声明必须要有名称(export default 命令另外考虑)。 * 不仅能导出声明还能导出引用(例如函数)。 * export 命令可以出现在模块的任何位置,但必需处于模块顶层。 * import 命令会提升到整个模块的头部,首先执行。 * 建议使用大括号指定所要输出的一组变量写在文档尾部,明确导出的接口。 * 函数与类都需要有对应的名称,导出文档尾部也避免了无对应名称。 ```html <!--页面中导入脚本: --> <script src="js/Test.js" type="module" defer/async></script> <!--defer是“渲染完再执行”,async是“下载完就执行”。另外,如果有多个defer脚本,会按照它们在页面出现的顺序加载,而多个async脚本是不能保证加载顺序的。 [可以省略不写,默认: defer]--> ``` **案例** ```javascript // Home.js 文件内容 //定义变量 let [myName,myAge] = ['Tome',20]; //定义方法 export let sayHi = function(){ return `你好,我叫:${myName},今年:${myAge}岁`; } //定义类 let myClass = class myClass { static info = "yeah!"; } //导出数据 export { myName, myAge, myClass } //Test.js 文件内容 import { myName, myAge, sayHi, myClass } from “./Home.js”; //导入数据 //定义自己的属性 let className = '理工30班'; //调用导入进来的数据 console.log(myName); console.log(sayHi()); console.log(myClass.info); console.log(className); ``` #### as重命名 * export 命令导出的接口名称,须和模块内部的变量有一一对应关系。 * 导入的变量名,须和导出的接口名称相同,即顺序可以不一致。 * 可以在导出位置或导入位置进行重命名操作 #### import注意事项 import命令输入的变量都是只读的,因为它的本质是输入接口。也就是说,不允许在加载模块的脚本里面,改写接口。 --- > 总结完了,其实这个文档是课程升级BCSP的时候准备,当时还准备一个讲解ppt,只可惜后面没有用到了,很多东西讲解的不是特别细致,凑合凑合还过得去,哈哈哈哈~ 最后修改:2021 年 12 月 30 日 © 允许规范转载 赞 都滑到这里了,不点赞再走!?