ES6
一. 模板字符串
(1). 问题: 旧js中,拼接字符串,只能用+,但是+极容易和算术计算的+冲突!
(2). 解决: 只要动态拼接字符串,都可用模板字符串
(3). 什么是: 支持动态拼接内容,支持换行的特殊的字符串。
(4). 如何: 3句话:
a. 整个字符串要用一对儿反引号` `(键盘左上角esc键正下方)
b. 在` `内支持换行,支持单引号,支持双引号
c. 如果在字符串中有动态拼接的内容,必须放在${}中
(5). ${}中,到底能干什么
a. 能放什么: 变量,三目,运算,有返回值的函数调用,创建对象,访问数组下标——一切有返回值的合法的js表达式,都能放!
b. 不能放什么: 分支(if else if else switch case) 和 循环(for while do while) 和其它没有返回值的js表达式。
(6). 示例: 使用模板字符串动态拼接各种字符串
1 <!DOCTYPE html>
2 <html lang="en">
3 <head>
4 <meta charset="UTF-8">
5 <meta name="viewport" content="width=device-width, initial-scale=1.0">
6 <title>Document</title>
7 </head>
8 <body>
9 <script>
10 var uname="dingding";
11 console.log(`Welcome ${uname}`);
12 var price=12.5,count=5;
13 console.log(`
14 单价:¥${price.toFixed(2)}
15 数量:${count}
16 ===============================
17 小计:¥${(price*count).toFixed(2)}
18 `)
19 var sex=1;
20 console.log(`性别:${sex==1?"男":"女"}`)
21 var orderTime=1612341525799;//new Date().getTime()
22 console.log(`下单时间: ${new Date(orderTime).toLocaleString()}`);
23 var day=new Date().getDay();//今天星期几
24 // 3 0 1 2 3 4 5 6
25 var arr=["日","一","二","三","四","五","六"];
26 // 0 1 2 3 4 5 6
27 console.log(`今天星期${arr[day]}`)
28 </script>
29 </body>
30 </html>
31 运行结果:
32 Welcome dingding
33 单价:¥12.50
34 数量:5
35 ===============================
36 小计:¥62.50
37
38 性别:男
39 下单时间: 2021/2/3 下午4:38:45
40 今天星期三
二.新增的 let声明变量方式
(1). 问题: 旧js中,都用var来声明变量,但是var有两个广受诟病的缺陷
a. 声明提前: 破坏了程序正常的执行顺序
b. 不受代码块{}的限制:分支和循环结构的{}内的变量,出了{},依然可以使用!代码块内的变量,就有可能干扰代码块外的变量!
c. 示例: var声明提前和不受代码块{}限制共同造成的问题:
1 <!DOCTYPE html>
2 <html lang="en">
3 <head>
4 <meta charset="UTF-8">
5 <meta name="viewport" content="width=device-width, initial-scale=1.0">
6 <title>Document</title>
7 </head>
8 <body>
9 <script>
10 var t=0;//准备累加每个函数执行所需的总时间
11 function fun1(){
12 console.log(`执行fun1耗时0.3s`);
13 t+=0.3;
14 }
15 function fun2(){//墙
16 //var t;//fun2中从此有了局部变量t
17 //从此fun2中所有t,优先使用局部变量t,与全局再无关系!
18 //因为所有局部变量在函数调用后,立刻释放!所以函数什么也留不下来!
19 console.log(`执行fun2耗时0.8s`);
20 t+=0.8;
21
22 //过了一段时间,系统升级,加了如下代码
23 //因为条件写死为false,所以if一定不执行!
24 if(false){//不是墙,仅是很矮的栅栏,拦不住内部的var
25 var t=new Date();//获得当前系统时间
26 console.log(`当前系统时间时:${t.toLocaleString()}`);
27 }
28 }
29 fun1();
30 fun2();
31 console.log(`整段程序共耗时${t}s`);//1.1s
32 </script>
33 </body>
34 </html>
35 运行结果:
36 执行fun1耗时0.3s
37 执行fun2耗时0.8s
38 整段程序共耗时0.3s
(2). 解决: 今后所有的变量声明,尽量用let 代替 var。
(3). 优点: 2个
a. let声明的变量不会被声明提前
b. let声明的变量只在最近的{}内有效!出了{}就不能用了!——let让if, else等分支和循环结构的{},也变成作用域——块级作用域。
强调: 假块级作用域,只防let,防不住var。
c. 示例: 使用let解决声明提前和没有块级作用域的问题:
1 <!DOCTYPE html>
2 <html lang="en">
3 <head>
4 <meta charset="UTF-8">
5 <meta name="viewport" content="width=device-width, initial-scale=1.0">
6 <title>Document</title>
7 </head>
8 <body>
9 <script>
10 var t=0;//准备累加每个函数执行所需的总时间
11 function fun1(){
12 console.log(`执行fun1耗时0.3s`);
13 t+=0.3;
14 }
15 function fun2(){//墙
16 console.log(`执行fun2耗时0.8s`);
17 t+=0.8;
18
19 //过了一段时间,系统升级,加了如下代码
20 //条件写为true,所以if一定执行!
21 if(true){//不是墙,仅是很矮的栅栏,拦不住内部的var,但是可以拦住let
22 (function(){
23 let t=new Date();//获得当前系统时间
24 //var t=new Date();
25 console.log(`当前系统时间时:${t. toLocaleString()}`);
26 })();
27 }
28 }
29 fun1();
30 fun2();
31 console.log(`整段程序共耗时${t}s`);//1.1s
32 </script>
33 </body>
34 </html>
35 运行结果:
36 执行fun1耗时0.3s
37 执行fun2耗时0.8s
38 当前系统时间时:2021/2/3 下午5:51:56
39 整段程序共耗时1.1s
(4). let的特点: 3个
a. 禁止在首次初始化变量之前,提前使用该变量!
b. 相同作用域内,禁止重复let声明相同名称的变量!
c. 即使在全局创建的let变量,在window中也找不到!
d. 示例: 测试let的特点:
1 <!DOCTYPE html>
2 <html lang="en">
3 <head>
4 <meta charset="UTF-8">
5 <meta name="viewport" content="width=device-width, initial-scale=1.0">
6 <title>Document</title>
7 </head>
8 <body>
9 <script>
10 console.log(a);//不报错,会返回undefined
11 var a=10;
12
13 // console.log(b);//报错:
14 //Cannot access 'b' before initialization
15 // 不能 访问 b 在 初始化 之前
16 let b=10;
17
18 var a; //声明 declare
19 a=10; //赋值 assign
20 var a=10; //初始化 initialization
21
22 var c=10;
23 var c=100; //不会报错!后一个会覆盖前一个
24
25 let d=10;
26 //let d=100;//报错:
27 //Identifier 'd' has already been declared
28 //标识符/变量名 d 已经 被 声明!
29
30 var a1=10;
31 let a2=10;
32 // (function(){
33 // var a2=10;
34 // //还包裹之后所有和这个变量有关的代码!
35 // })();
36 console.log(window);
37 </script>
38 </body>
39 </html>
40 运行结果:
41 undefined
42
43 Window {…}
44 a: 10
45 a1: 10
46 ...
47 c: 100
(5). let的本质: let底层会被翻译为匿名函数自调!并且加下划线_
三. 箭头函数:
1. 问题: 旧的js中,频繁使用函数。但是每次使用函数时,都要写function。
2. 解决: 今后,定义一个函数,多数情况下,都可用箭头函数代替。
3. 如何: 3句话:
(1). 先去掉function,在()和{}之间加=>
(2). 如果只有一个形参变量,可省略()
(3). 如果函数体只有一句话,可省略{}
如果今后的一句话,还是return,则必须省略return!
4. 示例: 修改旧式的function为箭头函数
修改前
1 <!DOCTYPE html>
2 <html lang="en">
3 <head>
4 <meta charset="UTF-8">
5 <meta name="viewport" content="width=device-width, initial-scale=1.0">
6 <title>Document</title>
7 </head>
8 <body>
9 <script>
10 function add(a,b){
11 return a+b;
12 }
13 console.log(add(3,5));//8
14
15 var arr=[12,123,23,1,3,2];
16 arr.sort(function(a,b){return a-b});
17 console.log(arr);
18
19 var arr=["亮亮","然然","东东"];
20 arr.forEach(function(elem){
21 console.log(`${elem} - 到!`)
22 })
23
24 var arr=[1,2,3];
25 var arr2=arr.map(function(elem){
26 return elem*2;
27 })
28 console.log(arr2);
29
30 var arr=[1,2,3,4,5];
31 var sum=arr.reduce(function(box,elem){
32 return box+elem;
33 },0)
34 console.log(sum);
35
36 (function(){
37 var t=new Date();
38 console.log(`页面内容加载完成,at:${t.toLocaleString()}`)
39 })();
40
41 var t=5;
42 var timer=setInterval(function(){
43 t--;
44 console.log(t);
45 if(t==0){
46 console.log("boom!!!")
47 clearInterval(timer);
48 }
49 },1000)
50 </script>
51 </body>
52 </html>
53 运行结果:
54 8
55 (6) [1, 2, 3, 12, 23, 123]
56 亮亮-到!
57 然然-到!
58 东东-到!
59 (3) [2, 4, 6]
60 15
61 页面内容加载完成,at:2021/2/4 上午9:35:11
62 4
63 3
64 2
65 1
66 0
67 boom!!!
修改后
1 <!DOCTYPE html>
2 <html lang="en">
3 <head>
4 <meta charset="UTF-8">
5 <meta name="viewport" content="width=device-width, initial-scale=1.0">
6 <title>Document</title>
7 </head>
8 <body>
9 <script>
10 var add=(a,b)=>a+b;
11 console.log(add(3,5));//8
12
13 var arr=[12,123,23,1,3,2];
14 arr.sort((a,b)=>a-b);
15 console.log(arr);
16
17 var arr=["亮亮","然然","东东"];
18 arr.forEach(elem=>console.log(`${elem}-到!`))
19
20 var arr=[1,2,3];
21 var arr2=arr.map(elem=>elem*2)
22 console.log(arr2);
23
24 var arr=[1,2,3,4,5];
25 var sum=arr.reduce((box,elem)=>box+elem,0)
26 console.log(sum);
27
28 (()=>{
29 var t=new Date();
30 console.log(`页面内容加载完成,at:${t.toLocaleString()}`)
31 })();
32
33 var t=5;
34 var timer=setInterval(()=>{
35 t--;
36 console.log(t);
37 if(t==0){
38 console.log("boom!!!")
39 clearInterval(timer);
40 }
41 },1000)
42 </script>
43 </body>
44 </html>
45 运行结果:
46 8
47 (6) [1, 2, 3, 12, 23, 123]
48 亮亮-到!
49 然然-到!
50 东东-到!
51 (3) [2, 4, 6]
52 15
53 页面内容加载完成,at:2021/2/4 上午9:35:11
54 4
55 3
56 2
57 1
58 0
59 boom!!!
5. 问题: 箭头函数最大特点!!!
(1). 箭头函数内部的this->外部的this,内外this保持一致!
(2). 但是,有时,我们不希望内外this相通,比如:
a. 对象中的方法,就不希望方法中的this指向对象外的window
解决: 今后对象中的方法,都可省略":function",但是不要加=>
好处: 既省略了function,又不会影响作用域和this!
b. 构造函数!也不希望内部的this->外层的window!
解决: ES6 class(待后期介绍...)
c. 事件处理函数!也不希望内部的this->外层的window!
解决:待 Vue框架中介绍(待续...)
(3)箭头函数中不支持arguments参数了
6. 总结:
(1). 如果函数中没有this!或希望内外this相同时,就用箭头函数
(2). 如果函数中有this,且不希望内外this相同时,就不能用箭头函数!
7. 示例: 使用箭头函数简写对象中的方法和回调函数:
1 <!DOCTYPE html>
2 <html lang="en">
3 <head>
4 <meta charset="UTF-8">
5 <meta name="viewport" content="width=device-width, initial-scale=1.0">
6 <title>Document</title>
7 </head>
8 <body>
9 <script>
10 /*function*/
11 // var lilei={
12 // sname:"Li Lei",
13 // friends:["亮亮","然然","东东"],
14 // intr:function(){
15 // this.friends.forEach(
16 // function(fname){
17 // //this.sname 中的this会指向window最后会得到undefined....所以此方法不可取
18 // console.log(`${this.sname} 认识 ${fname}`)
19 // }
20 // )
21 // }
22 // }
23 // lilei.intr();
24
25 /*箭头函数*/
26 // var lilei={
27 // sname:"Li Lei",
28 // friends:["亮亮","然然","东东"],
29 // intr:function(){//墙
30 // this.friends.forEach(
31 // fname=>{//墙,拆了!内外的this相通了都指向调用函数的对象lilei了 但还可以简写 看下面
32 // console.log(`${this.sname} 认识 ${fname}`)
33 // }
34 // )
35 // }
36 // }
37 // lilei.intr();
38
39 /*对象方法简写*/
40 var lilei={//只是new Object的简写,不是作用域,不是墙
41 sname:"Li Lei",
42 friends:["亮亮","然然","东东"],
43 //正确: ES6,专门为对象的方法定义了一种简写
44 intr(){//还是墙,不等于箭头函数 其实还=intr:function(){
45 //优点: 单纯简写!不影响作用域和this!
46 //错误
47 // intr:()=>{//墙,拆了
48 //报错Cannot read property 'forEach' of undefined
49 this.friends.forEach(
50 fname=>{//墙,拆了!只影响this,不影响变量!
51 // var a=10;//局部变量!
52 console.log(`${this.sname} 认识 ${fname}`)
53 }
54 )
55 // console.log(a);//不能用!报错!a is not defined
56 }
57 }
58 lilei.intr();
59 </script>
60 </body>
61 </html>
8. 其它重要问题:
(1). 普通函数改为箭头函数,只影响this,不影响局部变量!局部变量不会变成全局变量!依然只在函数内部有效!
(2). 箭头函数底层的本质: 相当于.bind(),永久将函数内的this与外部this绑定!今后,即使用call,或apply,也无法更换箭头函数中的this!
四 for of 的应用
. 问题: 遍历索引数组:
(1). 普通for循环的问题: 语法固定,没有可简化的空间
(2). for in的问题: in不仅遍历数字下标,而且遍历原型对象中的其它共有属性——超范围
(3). forEach的问题: 是数组家的函数,只能数组家的对象使用。其它类型的对象,比如同为数字下标的类数组对象,就用不了!
2. 解决: 今后,只要想遍历数字下标的索引数组和类数组对象,都可用for of代替之前所有循环!
3. 优点:
(1). 既能遍历索引数组,又能遍历类数组对象——可遍历一切数字下标的东西。
(2). 语法比之前的都简洁!
4. 如何: for(var 元素值 of 索引数组/类数组对象){
对当前元素值执行操作
}
5. 原理: of会自动依次取出右边数组中的每个元素值,保存到of前的变量中
和for in对比,in取出的是下标,不是元素值。
6. for of的不足:
(1). 只能从前向后,一个挨一个的遍历。无法改变遍历的顺序和步调!
(2). 只能获得元素的内容,无法获得元素的位置!
7. 但是,因为今后绝大多数的遍历,都是从头到尾,挨着遍历,所以今后,for of用的绝对多!
8. 示例: 定义函数,计算任意多个数字的和
1 <!DOCTYPE html>
2 <html lang="en">
3 <head>
4 <meta charset="UTF-8">
5 <meta name="viewport" content="width=device-width, initial-scale=1.0">
6 <title>Document</title>
7 </head>
8 <body>
9 <script>
10 //索引数组:
11 var arr=["亮亮","然然","东东"];
12 //想点名
13 //forEach
14 arr.forEach(n=>console.log(`${n}-到!`));
15 //for of
16 for(var 元素值 of arr){
17 console.log(`${元素值}-到!`)
18 }
19
20 //想定义一个函数,可以计算任意多个数字的和
21 function add( ){
22 // arugments[ ].length
23 // 0 1 2 3 4 5 6 7 ...
24 // 类数组对象,下标也是数字
25 //求和的固定套路
26 var result=0;
27 //遍历arguments中接收到的每个实参值
28 //累加到result变量上
29 //for
30 // for(var i=0;i<arguments.length;i++){
31 // result+=arguments[i]
32 // }
33 //不能用forEach,因为arguments不是数组家孩子
34 //for of
35 for(var n of arguments){
36 result+=n;
37 }
38 return result;
39 }
40
41 console.log(add(1,2,3));//6
42 console.log(add(1,2,3,4,5));//15
43 </script>
44 </body>
45 </html>
46 运行结果:
47 亮亮-到!
48 然然-到!
49 东东-到!
50 亮亮-到!
51 然然-到!
52 东东-到!
53 6
54 15
五,参数增强
1. 参数默认值:
(1). 何时: 今后,只要希望一个形参变量,即使将来没有传入对应的实参时,也能有一个默认值使用时,就可以用参数默认值。
(2). 如何: 定义函数时:
function 函数名(形参1=默认值1, 形参2=默认值2, ... ){
...
}
(3). 问题: 只支持前边参数提供,后边参数保持默认的情况
不支持前边参数默认,后边参数提供新值 即提供的参数只会影响最前面的默认参数
(4). 解决: 参数解构(下面介绍...)
(5). 示例: 定义肯德基点套餐的函数,希望支持各种换菜品的情况
1 <!DOCTYPE html>
2 <html lang="en">
3 <head>
4 <meta charset="UTF-8">
5 <meta name="viewport" content="width=device-width, initial-scale=1.0">
6 <title>Document</title>
7 </head>
8 <body>
9 <script>
10 function intr(str="主人很懒,什么也没留下"){
11 console.log(`我的自我介绍是:${str}`)
12 }
13 intr("you can you up!");
14 intr();
15
16 //定义一个函数,可以帮用户点肯德基套餐
17 function order(zhushi="香辣鸡腿堡",xiaochi="薯条",yinliao="可乐"){
18 console.log(`
19 你本次点的套餐是:
20 主食:${zhushi},
21 小吃:${xiaochi},
22 饮料:${yinliao}
23 祝您用餐愉快!
24 =================
25 `)
26 }
27
28 //第一个用户愿意挨个选择挨个点
29 order(`香辣鸡腿堡`,`薯条`,`可乐`)
30 //第二个用户着急赶火车,就想要默认套餐!
31 order();
32 //第三个用户只想换汉堡!其它保持默认
33 order("奥尔良烤腿堡")
34 //第四个用户只想换小吃!其它保持默认!
35 //错误:
36 // order("辣翅") // 默认 默认
37 // zhushi, xiaochi,yinliao
38 //错误: 函数调用的语法,不支持空的,
39 // order( ,"辣翅")
40 //错误: ""就会把zhushi覆盖
41 // order("","辣翅")
42 //不好: 用户不懂什么是undefined!
43 // order(undefined, "辣翅")
44 </script>
45 </body>
46 </html>
47 运行结果:
48 我的自我介绍是:you can you up!
49 我的自我介绍是:主人很懒,什么也没留下
50 你本次点的套餐是:
51 主食:香辣鸡腿堡,
52 小吃:薯条,
53 饮料:可乐
54 祝您用餐愉快!
55 =================
56
57 你本次点的套餐是:
58 主食:香辣鸡腿堡,
59 小吃:薯条,
60 饮料:可乐
61 祝您用餐愉快!
62 =================
63
64 你本次点的套餐是:
65 主食:奥尔良烤腿堡,
66 小吃:薯条,
67 饮料:可乐
68 祝您用餐愉快!
69 =================
2. 剩余参数(rest)
(1). 问题: 箭头函数不支持arguments了!那么,将来在js中如果遇到不确定参数个数的情况时?怎么办?
(2). 解决: 在新的js中,只要参数个数不确定,都可用剩余参数语法!
(3). 如何: 定义函数时 收集所有实参值
var 函数名=(...自定义数组名)=>{ //其中...三个点必须要有
//...就会自动将所有的实参值,放入一个自动创建好的数组中保存!
}
(4). 问题: 如果有的形参有值,有的形参内容和个数都不确定
(5). 解决: 其实: ...数组名,还可以和其它固定的形参,共同使用!
第1个实参, 第2个实参, 除1、2实参外所有剩余实参
↓ ↓ \↓↓↓↓↓↓↓/
var 函数名=(固定形参1, 固定形参2, ...自定义数组名)=>{
}
(6). 强调: ...自定义数组所获得的数组是一个纯正的数组,可以使用数组家所有的函数!
(7). 示例: 定义函数计算多个数字的和
1 <!DOCTYPE html>
2 <html lang="en">
3 <head>
4 <meta charset="UTF-8">
5 <meta name="viewport" content="width=device-width, initial-scale=1.0">
6 <title>Document</title>
7 </head>
8 <body>
9 <script>
10 //想定义一个函数,可以计算任意多个数字的和
11 var add=(...arr)=>{
12 var result=0;
13 //报错:arguments is not defined
14 // for(var n of arguments){//类数组对象
15 for(var n of arr){//纯正的数组
16 result+=n;
17 }
18 return result;
19 }
20
21 console.log(add(1,2,3));//6
22 console.log(add(1,2,3,4,5));//15
23
24 //定义函数计算一个员工的薪资
25 //第一个实参值注定都会传入员工姓名
26 //但是,每个员工的工资由几部分组成,不确定!
27 function jisuan(ename,...arr){
28 console.log(`${ename}的总工资是: ${
29 arr.reduce(function(box,elem){
30 return box+elem;
31 },0)
32 }`)
33 }
34 //lilei想计算自己的总工资
35 jisuan("Li Lei",10000,1000,2000);
36 // ename \ ...arr /
37
38 //hmm也想计算自己的总工资
39 jisuan("Han Meimei",4000,1000,2000,3000,4000)
40 // ename \ ...arr /
41 </script>
42 </body>
43 </html>
44 运行结果:
45 6
46 15
47 Li Lei的总工资是: 13000
48 Han Meimei的总工资是: 14000
3. 打散数组(spread):
(1). 问题: apply虽然具有打散数组的功能,但是
a. apply的本职工作是替换this的,只是顺便能够打散数组。
b. 所以,如果需求和替换this无关,要使用apply,也被迫随意写第一个实参值,占位!——别扭!
(2). 解决: ES6,就专门定义了一个只打散数组的方法!
(3). 如何: 调用函数:
函数名(...数组)
(4). 原理: ...会先将数组拆散为多个独立的值,再依次传给函数的形参变量。
(5). 总结: 同样是"..."
a. 如果在定义函数时的形参列表中用...,意为收集
b. 如果在调用函数时的实参值列表中用...,意为打散
(6). 示例: 使用Math.max和Math.min查找数组中的最大值和最小值
1 <!DOCTYPE html>
2 <html lang="en">
3 <head>
4 <meta charset="UTF-8">
5 <meta name="viewport" content="width=device-width, initial-scale=1.0">
6 <title>Document</title>
7 </head>
8 <body>
9 <script>
10 console.log(Math.max(1,7,2,5));//7
11 console.log(Math.min(1,7,2,5));//1
12
13 var arr=[1,7,2,5];
14 //max和min不支持查找数组中的最大值和最小值
15 console.log(Math.max(arr));//NaN
16 console.log(Math.min(arr));//NaN
17 //旧js
18 console.log(
19 //因为当前要做的事儿与this无关,无需替换this
20 //但是,第一个实参值又不能不写
21 //所以,可以用任何合法的东西,放在第一个参数位置
22 Math.max.apply([], arr)
23 );
24 console.log(
25 Math.min.apply(0,arr)
26 );
27
28 //ES6:
29 console.log(Math.max(...arr));
30 console.log(Math.min(...arr));
31 </script>
32 </body>
33 </html>
34 运行结果:
35 7
36 1
37 NaN
38 NaN
39 7
40 1
41 7
42 1
(7). 语法糖:
a. 复制一个数组: var arr2=[...arr1];
1). []是创建一个新的空数组的意思
2). ...arr1打散原数组,将打散后的所有元素值,放入新数组中
b. 拼接多个数组和元素: var arr3=[...arr1, 元素值, ....arr2, 元素值 ]
1). []是创建一个新的空数组等待
2). 先将...arr1和...arr2打散为多个元素值,再和个别写死的元素值,按顺序放入新数组中
c. 克隆一个对象: var obj2={...obj1}
1). {}是创建一个新对象的意思
2). ...obj1是先将一个对象,打散为多个属性,再将所有属性,放入新对象中。
d. 拼接多个对象和属性: var obj3={ ...obj1, 属性:值, ...obj2, 属性:值 }
1). {}是创建一个新的空对象等待
2). 先将...obj1和...obj2打散为多个属性,再和个别写死的属性,放入新对象中保存。——对象中的属性从来不关心前后顺序!只要对象中有这个属性就行!
(8). 使用...语法糖简写实现常用操作:
1 <!DOCTYPE html>
2 <html lang="en">
3 <head>
4 <meta charset="UTF-8">
5 <meta name="viewport" content="width=device-width, initial-scale=1.0">
6 <title>Document</title>
7 </head>
8 <body>
9 <script>
10 var arr1=[1,2,3];
11 //复制arr1数组,获得一个新数组
12 var arr2=[...arr1];
13 console.log(arr2);
14 console.log(arr1==arr2);//false
15 //拼接数组和元素
16 var arr1=[1,2,3];
17 var arr2=[5,6,7];
18 var arr3=[...arr1,4,...arr2,8];
19 console.log(arr3);//1,2,3,4,5,6,7,8
20 //克隆对象
21 var lilei={
22 sname:"Li Lei",
23 sage:11
24 };
25 var lilei2={...lilei};
26 console.log(lilei2);
27 console.log(lilei==lilei2);//false
28 //拼接对象
29 var obj1={a:1,b:2};
30 var obj2={d:4,e:5};
31 var obj3={...obj1, c:3, ...obj2, f:6};
32 console.log(obj3);//{a:1,b:2,c:3,d:4,e:5,f:6}
33 </script>
34 </body>
35 </html>
36 运行结果:
37 (3) [1, 2, 3]
38 false
39 (8) [1, 2, 3, 4, 5, 6, 7, 8]
40 {sname: "Li Lei", sage: 11}
41 false
42 {a: 1, b: 2, c: 3, d: 4, e: 5, …}
43 a: 1
44 b: 2
45 c: 3
46 d: 4
47 e: 5
48 f: 6
六解构 (destruct): 从一个大的数组或对象中提取出个别成员单独使用!
1. 数组解构
(1). 问题: 很多时候,虽然数据放在一个数组中,而我们使用时,经常需要只使用数组中个别位置的数据而已!
a. 如果每次使用数组中的元素都要"数组名[下标]",太麻烦!
b. 时间长了,数组下标0,1,2又没有意义,极其不便于将来程序的维护
(2). 解决: 今后,只要只想用数组中个别元素时,都用数组解构,只提取出个别元素,单独使用!
(3). 如何: 2步,写在一句话里:
a. 装扮: 将准备接受元素值的一个一个的变量,放在一个数组的样式中
[变量1, 变量2,...]
b. 赋值: =右边,将实际保存数据的数组赋值给左边的数组结构
=数组
c. 总结: [变量1, 变量2,...] =数组
0 1 ... 0 1...
(4). 结果: =右边的数组中相同下标位置的元素值,会自动赋值给=左边的数组中相同下标位置的变量
变量1=数组[0];
变量2=数组[1];
... ...
(5). 示例: 解构出数组中的年,月,日,单独使用
1 <!DOCTYPE html>
2 <html lang="en">
3 <head>
4 <meta charset="UTF-8">
5 <meta name="viewport" content="width=device-width, initial-scale=1.0">
6 <title>Document</title>
7 </head>
8 <body>
9 <script>
10 // 年 月日 时 分
11 var arr=[2021,2,4,15,47]
12 // 0 1 2 3 4
13 //想使用arr中的年,月,日
14 //先将数组中的元素值,解构给=左边的对应变量
15 // var [y,m,d]=arr;
16 // 0 1 2
17
18 //如果只想用月和日,不想用年
19 var [ ,m,d]=arr;
20 // 0 1 2
21 //然后再单独使用变量
22 // console.log(`今年是${y}年`);
23 console.log(`今天是${m}月${d}日`);
24 </script>
25 </body>
26 </html>
27 运行结果:
28 今天是2月4日
2. 对象解构
(1). 问题: 使用对象中的属性和方法时,总要带着"对象名."前缀,非常麻烦!
(2). 解决: 如果只是想使用一个大的对象中的个别属性和方法,就可以用对象解构方式提取出个别属性和方法单独使用
(3). 如何: 还是2步写在一句话中
a. 先将变量们装扮成对象的样式,且要和实际保存数据的对象结构相同
var {属性名1: 变量1, 属性名2: 变量2, ... }
b. 赋值:
=对象
c. 总结: var {属性名1: 变量1, 属性名2: 变量2, ... }=对象
(4). 结果: =右边的对象中相同属性名的属性值,会自动赋值给=左边的对象结构中相同属性名的变量
变量1=对象.属性1;
变量2=对象.属性2;
... ...
(5). 问题: 如果原对象中属性名已经起的很好了,不想改变对象中原属性名和方法名,又不想在解构时把属性名或方法名重复写2遍
(6). 解决: 其实=左边的{}中,每个属性可以只写一个名字:
var {属性名1, 属性名2, ...}=对象
(7). 原理: 一个名字2用,既当属性名,用于配对;又当变量,用作接值
(8). 示例: 解构出对象中个别属性和方法单独使用
1 <!DOCTYPE html>
2 <html lang="en">
3 <head>
4 <meta charset="UTF-8">
5 <meta name="viewport" content="width=device-width, initial-scale=1.0">
6 <title>Document</title>
7 </head>
8 <body>
9 <script>
10 var user={
11 uname:"丁丁",
12 sex:1,
13 phone:18301092802,
14 login(){
15 console.log("登录...");
16 },
17 logout(){
18 console.log("注销...");
19 }
20 };
21 //当前页面中只会用到uname和logout
22 // 属性名 变量名 属性名:变量名
23 // 配对 接值 配对 接值
24 // var {uname:uname, logout:logout}=user;
25 //语法糖:
26 //对象解构时,如果不打算修改原对象中的属性名和方法名,又不想把属性名写两遍!其实,只需要写一遍即可!
27 //一个名字两用!
28 // 既属性名
29 // 配对
30 // 又变量名
31 // 接值
32 var {uname, logout}=user;
33
34 console.log(`Welcome ${uname}`);
35 logout();
36 </script>
37 </body>
38 </html>
39 运行结果:
40 Welcome 丁丁
41 注销...
3. 参数解构
(1). 问题: 单靠参数默认值,只支持前边的形参更换,后边的形参保持默认的情况。不支持,更换任意形参,任意形参保持默认的情况——不灵活!
(2). 解决: 在定义函数时,如果发现将来参数个数不确定,但是又要求实参值必须传给指定的形参变量——有对应关系时,只能用参数解构!
(3). 如何: 2步:
a. 定义函数时,要把形参变量们装扮为对象的结构:
function 函数名({
属性名1: 形参1,
属性名2: 形参2,
... : ...
}){
}
不想把属性名和形参名起的不一样,其实也可以只写一个名字:
function 函数名({
//一个名字2用:
//既属性名
// 配对
//又形参名
// 接值
形参1=默认值,
形参2=默认值,
...
}){
}
b. 调用函数时:
1). 想传哪些参数就只写哪些参数,不传的参数,一个都不用多写!
2). 但是,传参时必须也要放在一个对象结构中,且:左边的属性名,应该和定义函数时的形参变量名找到对应才行
函数({
形参1: 实参1,
形参2: 实参2,
... : ...
})
(4). 结果:
a. 如果形参在实参对象中能找到配对儿的属性值,形参就可得到实参值
b. 如果形参在实参对象中未能找到配对儿的属性值,也不会报错!打不了获得undefined,回来可以用默认值!
(5). 示例: 定义函数,可以点套餐,并可更换菜品
1 <!DOCTYPE html>
2 <html lang="en">
3 <head>
4 <meta charset="UTF-8">
5 <meta name="viewport" content="width=device-width, initial-scale=1.0">
6 <title>Document</title>
7 </head>
8 <body>
9 <script>
10 //定义一个函数,可以帮用户点肯德基套餐
11 function order({
12 zhushi="香辣鸡腿堡",
13 xiaochi="薯条",
14 yinliao="可乐"
15 }){
16 console.log(`
17 你本次点的套餐是:
18 主食:${zhushi},
19 小吃:${xiaochi},
20 饮料:${yinliao}
21 祝您用餐愉快!
22 =================
23 `)
24 }
25
26 //第一个用户愿意挨个选择挨个点
27 order({
28 zhushi:"深海鳕鱼堡",
29 xiaochi:"土豆泥",
30 yinliao:"奶茶"
31 });
32 //第二个用户着急赶火车,就想要默认套餐!
33 order({});
34 //第三个用户只想换汉堡!其它保持默认
35 order({
36 zhushi: "奥尔良烤腿堡"
37 })
38 //第四个用户只想换中间的小吃!其它保持默认!
39 //错误:
40 // order("辣翅") // 默认 默认
41 // zhushi, xiaochi,yinliao
42 //错误: 函数调用的语法,不支持空的,
43 // order( ,"辣翅")
44 //错误: ""就会把zhushi覆盖
45 // order("","辣翅")
46 //不好: 用户不懂什么是undefined!
47 // order(undefined, "辣翅")
48 //好的:
49 order({
50 xiaochi:"辣翅"
51 })
52 </script>
53 </body>
54 </html>
55 运行结果:
56 你本次点的套餐是:
57 主食:深海鳕鱼堡,
58 小吃:土豆泥,
59 饮料:奶茶
60 祝您用餐愉快!
61 =================
62
63 你本次点的套餐是:
64 主食:香辣鸡腿堡,
65 小吃:薯条,
66 饮料:可乐
67 祝您用餐愉快!
68 =================
69
70 你本次点的套餐是:
71 主食:奥尔良烤腿堡,
72 小吃:薯条,
73 饮料:可乐
74 祝您用餐愉快!
75 =================
76
77 你本次点的套餐是:
78 主食:香辣鸡腿堡,
79 小吃:辣翅,
80 饮料:可乐
81 祝您用餐愉快!
82 =================
七 class
1. 问题: 旧js中,要想创建一种新的类型,都要创建构造函数和原型对象方法。但是,旧js中构造函数和原型对象方法,是分开写的!不像一家人!——不符合封装的要求
2. 解决: 今后,只要想创建一种新的类型时,都用class包裹原来的构造函数和原型对象
3. 如何定义一个class: 3句话:
(1). 用class{}包裹原来的构造函数和原型对象方法
(2). 构造函数名/类型名要提升为整个class的名字。原函数统一更名为constructor关键字
(3). 今后只要放在class中的方法,默认就是保存到原型对象中的方法,且只需要写方法名()即可!
4. 如何使用class: 和之前使用原型对象和构造函数用法完全一样!
(1). 先用new创建该类型的子对象
(2). 用子对象可以访问原型对象中的方法。
5. 其实,class就是新瓶装旧酒,换汤不换药:
(1). 除了定义class的步骤稍微有变化之外,创建子对象的方法完全没变!——还是用new 类型名()创建
(2). 创建出的子对象结构和继承的原理,与旧js中使用构造函数方式创建的子对象,几乎完全一样!——还是__proto__继承原型对象。
6. 示例: 使用class创建学生类型:
1 <!DOCTYPE html>
2 <html lang="en">
3 <head>
4 <meta charset="UTF-8">
5 <meta name="viewport" content="width=device-width, initial-scale=1.0">
6 <title>Document</title>
7 </head>
8 <body>
9 <script>
10 //旧js中: 定义一个学生类型
11 //所有学生都要有姓名和年龄
12 //所有学生都要会做自我介绍
13 //2步:
14 //1. 先创建构造函数
15 // function Student(sname, sage){
16 // this.sname=sname;
17 // this.sage=sage;
18 // }
19 // //2. 向原型对象中添加共有方法
20 // Student.prototype.intr=function(){
21 // console.log(`I'm ${this.sname},I'm ${this.sage}`)
22 // }
23
24 //ES6中:
25 //1. class{}包裹
26 class Student{
27 //2. 构造函数名/类型名要提升为整个class的名字。原函数统一更名为constructor关键字
28 constructor(sname, sage){
29 this.sname=sname;
30 this.sage=sage;
31 }
32 //3. 今后只要放在class中的方法,默认就是保存到原型对象中的方法,且只需要写方法名()即可!
33 intr(){//=Student.prototype.intr=function(){
34 console.log(`I'm ${this.sname},I'm ${this.sage}`)
35 }
36 }
37
38 //反复创建两个学生对象:
39 var lilei=new Student("Li Lei",11);
40 var hmm=new Student("Han Meimei",12);
41 console.log(lilei);
42 console.log(hmm);
43 lilei.intr();
44 hmm.intr();
45 </script>
46 </body>
47 </html>
48 运行结果:
49 Student {sname: "Li Lei", sage: 11}
50 sage: 11
51 sname: "Li Lei"
52 __proto__:
53 constructor: class Student
54 intr: ƒ intr()
55 __proto__: Object
56 Student {sname: "Han Meimei", sage: 12}
57 sage: 12
58 sname: "Han Meimei"
59 __proto__:
60 constructor: class Student
61 intr: ƒ intr()
62 __proto__: Object
63 I'm Li Lei,I'm 11
64 I'm Han Meimei,I'm 12
7. 两种类型间的继承:
(1). 问题: 多个class之中包含部分相同的属性结构和方法定义
(2). 解决: 使用两种类型间的继承
(3). 如何: 2大步:
a. 先定义一个父类型,集中保存所有子类型相同部分的属性和方法。
1). 父类型的构造函数里包含相同部分的属性结构
子类型构造函数中就不要再包含重复内容了!
2). 父类型的原型对象中包含相同部分的方法定义
子类型原型对象中就不要再包含重复的方法了!
b. 让子类型class继承父类型class: 2步:
1). 让子类型class继承父类型class:
i. class 子类型 extends 父类型{ ... }
ii. 类似于setPrototypeOf(),设置子类型的原型对象,继承父类型的原型对象
2). 在子类型构造函数开头,使用super()关键字,请父类型构造函数过来帮忙创建子对象。
i. super: 父类型的意思,是extends附赠的专指父类型构造函数的关键词。
ii. 为什么: 自从改了class后,构造函数没有自己专门的名字了!靠函数名,已经调用不到构造函数了。
iii. 何时: 今后,只要在子类型中,想使用父类型的构造函数,唯一的办法,只有super()
iv. 问题: super()调用时,前边即没有点,又没有new,this难道不指window吗?
v. 答: super()会自动调整this与当前构造函数中的this保持一致!
(4). 示例: 使用两种类型间的继承,避免多个class中重复代码
1 <!DOCTYPE html>
2 <html lang="en">
3 <head>
4 <meta charset="UTF-8">
5 <meta name="viewport" content="width=device-width, initial-scale=1.0">
6 <title>Document</title>
7 </head>
8 <body>
9 <script>
10 class Enemy{
11 constructor(x,y){
12 this.x=x;
13 this.y=y;
14 }
15 fly(){
16 console.log(`飞到x=${this.x},y=${this.y}位置`)
17 }
18 }
19 class Plane extends Enemy{
20 constructor(x,y,score){
21 super(x,y);
22 this.score=score;
23 }
24 getScore(){
25 console.log(`击落敌机,得${this.score}分`)
26 }
27 }
28 var p1=new Plane(50,100,5);
29 console.log(p1);
30 p1.fly();
31 p1.getScore();
32 </script>
33 </body>
34 </html>
35 运行结果:
36 Plane {x: 50, y: 100, score: 5}
37 score: 5
38 x: 50
39 y: 100
40 __proto__: Enemy
41 constructor: class Plane
42 getScore: ƒ getScore()
43 __proto__:
44 constructor: class Enemy
45 fly: ƒ fly()
46 __proto__: Object
47 飞到x=50,y=100位置
48 击落敌机,得5分
八. promise
1. 问题: 在实际开发中,有时需要保证多个异步任务必须顺序执行!
2. 错误的解决: 单纯先后调用两个异步函数,无法保证必须先后执行。因为异步函数的特点就是谁也不等谁!
3. 不好的解决: 使用回调函数来解决: 2步:
(1). 定义回调函数:
a. 为前一个异步函数,定义一个形参变量,准备保存下一项任务函数
b. 在前一项异步任务函数中,异步任务最后一句话执行完时,调用形参变量,自动执行提前保存的下一项任务函数
(2). 调用前一项任务函数时,提前将下一项任务的调用语句,放在一个匿名函数function(){}中,提前传给前一个函数的形参变量保存起来——仅保存,暂不执行。
4. 示例: 使用回调函数保证两个异步任务必须顺序执行:
1 <!DOCTYPE html>
2 <html lang="en">
3 <head>
4 <meta charset="UTF-8">
5 <meta name="viewport" content="width=device-width, initial-scale=1.0">
6 <title>Document</title>
7 </head>
8 <body>
9 <script>
10 //提前给亮准备一个小车,准备装下一项任务
11 function liang(che){
12 console.log(`亮起跑...`)
13 setTimeout(function(){//等6s后自动执行,异步
14 console.log(`亮到达终点!`);
15 //当亮结束之后,才调用小车里的内容
16 che();//小车里放的内容才开始执行
17 },6000)
18 }
19 function ran(){
20 console.log(`然起跑...`)
21 setTimeout(function(){//等4s后自动执行,异步
22 console.log(`然到达终点!`)
23 },4000)
24 }
25 //错误:
26 // liang();
27 // ran();
28 //用回调函数来解决:
29 liang(
30 function(){//=>亮的che
31 ran();
32 }
33 //...
34 //6s后亮执行完自己的任务后才调用che()
35 //车里的ran()才执行!
36 );
37 </script>
38 </body>
39 </html>
40 运行结果:
41 亮起跑...
42 亮到达终点!
43 然起跑...
44 然到达终点!
5. 问题: 回调地狱(callback hell),由于连续使用多个回调函数,导致深层的嵌套结构
6. 示例: 三个异步函数必须顺序执行,导致的回调地狱现象
1 <!DOCTYPE html>
2 <html lang="en">
3 <head>
4 <meta charset="UTF-8">
5 <meta name="viewport" content="width=device-width, initial-scale=1.0">
6 <title>Document</title>
7 </head>
8 <body>
9 <script>
10 //提前给亮准备一个小车,准备装下一项任务
11 function liang(che){
12 console.log(`亮起跑...`)
13 setTimeout(function(){//等6s后自动执行,异步
14 console.log(`亮到达终点!`);
15 //当亮结束之后,才调用小车里的内容
16 che();//小车里放的内容才开始执行
17 },6000)
18 }
19 function ran(che){
20 console.log(`然起跑...`)
21 setTimeout(function(){//等4s后自动执行,异步
22 console.log(`然到达终点!`);
23 che();
24 },4000)
25 }
26 function dong(){
27 console.log(`东起跑...`)
28 setTimeout(function(){//等2s后自动执行,异步
29 console.log(`东到达终点!`);
30 },2000)
31 }
32
33 liang(
34 function(){//=>亮的che
35 ran(
36 function(){//=>然的che
37 dong();
38 }
39 //...
40 //4s后然执行完自己的最后一句话之后,才自动调用che
41 //车里的dong()才开始执行
42 );
43 }
44 //...
45 //6s后亮执行完自己的任务后才调用che()
46 //车里的ran()才执行!
47 );
48 </script>
49 </body>
50 </html>
51 运行结果:
52 亮起跑...
53 亮到达终点!
54 然起跑...
55 然到达终点!
56 东起跑...
57 东到达终点!
7. 解决: 只要多个异步任务必须顺序执行,都可用promise来实现
8. 如何: 2步:
(1). 前一项任务函数内
function 前一项任务(){
return new Promise(
function(door){原异步任务代码
//在原异步任务最后执行的一句话之后,调用door()开门
}
)
}
(2). 调用前一项任务,用.then()将前一项任务与后一项任务相连
前一项任务().then(后一项任务)
9. 原理:
(1). 调用前一项任务时,做两件事:
a. 既开始执行第一项任务中的代码
b. 又创建了一个new Promise()对象,并返回到函数外部,可以与后续函数相连
(2). 前一项任务返回的new Promise对象,带有.then()函数,可以与后续任务相连。暂时将下一项任务保存起来。暂不执行
(3). 当前一项任务内部调用door()开门后,promise对象才会自动执行自己身上的.then()函数中提前保存的下一项任务。
10. 示例: 使用promise串联两个异步任务先后执行:
1 <!DOCTYPE html>
2 <html lang="en">
3 <head>
4 <meta charset="UTF-8">
5 <meta name="viewport" content="width=device-width, initial-scale=1.0">
6 <title>Document</title>
7 </head>
8 <body>
9 <script>
10 function liang(){
11 //如果只有前后两个任务,只需要改第一个任务为promise即可
12 //函数内创建的promise对象,必须先return出去,才能和别的函数连接
13 return new Promise(
14 // 赠
15 // ↓
16 function(door){
17 console.log(`亮起跑...`)
18 setTimeout(function(){//等6s后自动执行,异步
19 console.log(`亮到达终点!`);
20 door()//开门!
21 //自动通知.then()连接的下一个函数,继续执行
22 },6000)
23 }
24 )
25 }
26 function ran(){
27 console.log(`然起跑...`)
28 setTimeout(function(){//等4s后自动执行,异步
29 console.log(`然到达终点!`)
30 },4000)
31 }
32 //调用liang()
33 liang()//既执行new Promise()中的异步任务
34 //又return new Promise()
35 .then(ran);//千万不要加(),因为ran不是立刻执行!
36 //当前一项任务中调用了door()时,会自动执行.then()中暂存的函数。
37 </script>
38 </body>
39 </html>
40 运行结果:
41 亮起跑...
42 亮到达终点!
43 然起跑...
44 然到达终点!
11. 示例: 使用Promise串联三个函数:
1 <!DOCTYPE html>
2 <html lang="en">
3 <head>
4 <meta charset="UTF-8">
5 <meta name="viewport" content="width=device-width, initial-scale=1.0">
6 <title>Document</title>
7 </head>
8 <body>
9 <script>
10 function liang(){
11 return new Promise(
12 function(door){
13 console.log(`亮起跑...`)
14 setTimeout(function(){//等6s后自动执行,异步
15 console.log(`亮到达终点!`);
16 door()
17 },6000)
18 }
19 )
20 }
21 function ran(){
22 return new Promise(
23 function(door){
24 console.log(`然起跑...`)
25 setTimeout(function(){//等4s后自动执行,异步
26 console.log(`然到达终点!`);
27 door()
28 },4000)
29 }
30 )
31 }
32 function dong(){
33 console.log(`东起跑...`)
34 setTimeout(function(){//等2s后自动执行,异步
35 console.log(`东到达终点!`);
36 },2000)
37 }
38
39 liang()//执行亮的内容
40 //又return new Promise()
41 .then(ran)//不要加()
42 //当liang()中执行door()开门时
43 //自动执行.then()中的ran():
44 //既执行然的内容,又再次返回一个new Promise对象
45 .then(dong);//不要加()
46 </script>
47 </body>
48 </html>
49 运行结果:
50 亮起跑...
51 亮到达终点!
52 然起跑...
53 然到达终点!
54 东起跑...
55 东到达终点!
12. 前后两个任务之间传参:
(1). 如何: 2步:
a. 在前一个任务中: 调用door()开门时,其实可以传入一个实参值
b. 下一项任务函数,通过形参变量来接上一项任务传来的参数值
(2). 示例: 前后两个任务之间传参:
1 <!DOCTYPE html>
2 <html lang="en">
3 <head>
4 <meta charset="UTF-8">
5 <meta name="viewport" content="width=device-width, initial-scale=1.0">
6 <title>Document</title>
7 </head>
8 <body>
9 <script>
10 function liang(){
11 var bang="接力棒";
12 //如果只有前后两个任务,只需要改第一个任务为promise即可
13 //函数内创建的promise对象,必须先return出去,才能和别的函数连接
14 return new Promise(
15 // 赠
16 // ↓
17 function(door){
18 console.log(`亮拿着${bang}起跑...`)
19 setTimeout(function(){//等6s后自动执行,异步
20 console.log(`亮拿着${bang}到达终点!`);
21 door(bang)//开门!并将bang传给下一项任务
22 //自动通知.then()连接的下一个函数,继续执行
23 },6000)
24 }
25 )
26 }
27 function ran(bang2){
28 console.log(`然拿着${bang2}起跑...`)
29 setTimeout(function(){//等4s后自动执行,异步
30 console.log(`然拿着${bang2}到达终点!`)
31 },4000)
32 }
33 //调用liang()
34 liang()//既执行new Promise()中的异步任务
35 //又return new Promise()
36 .then(ran);//千万不要加(),因为ran不是立刻执行!
37 //当前一项任务中调用了door()时,会自动执行.then()中暂存的函数。
38 </script>
39 </body>
40 </html>
41 运行结果:
42 亮拿着接力棒起跑...
43 亮拿着接力棒到达终点!
44 然拿着接力棒起跑...
45 然拿着接力棒到达终点!
1 <!DOCTYPE html>
2 <html lang="en">
3 <head>
4 <meta charset="UTF-8">
5 <meta name="viewport" content="width=device-width, initial-scale=1.0">
6 <title>Document</title>
7 </head>
8 <body>
9 <script>
10 function liang(){
11 var bang="接力棒";
12 return new Promise(
13 function(door){
14 console.log(`亮拿着${bang}起跑...`)
15 setTimeout(function(){//等6s后自动执行,异步
16 console.log(`亮拿着${bang}到达终点!`);
17 door(bang)
18 },6000)
19 }
20 )
21 }
22 function ran(bang2){
23 return new Promise(
24 function(door){
25 console.log(`然拿着${bang2}起跑...`)
26 setTimeout(function(){//等4s后自动执行,异步
27 console.log(`然拿着${bang2}到达终点!`);
28 door(bang2)
29 },4000)
30 }
31 )
32 }
33 function dong(bang3){
34 console.log(`东拿着${bang3}起跑...`)
35 setTimeout(function(){//等2s后自动执行,异步
36 console.log(`东拿着${bang3}到达终点!`);
37 },2000)
38 }
39
40 liang()//执行亮的内容
41 //又return new Promise()
42 .then(ran)//不要加()
43 //当liang()中执行door()开门时
44 //自动执行.then()中的ran():
45 //既执行然的内容,又再次返回一个new Promise对象
46 .then(dong);//不要加()
47 </script>
48 </body>
49 </html>
50 运行结果:
51 亮拿着接力棒起跑...
52 亮拿着接力棒到达终点!
53 然拿着接力棒起跑...
54 然拿着接力棒到达终点!
55 东拿着接力棒起跑...
56 东拿着接力棒到达终点!
总结: this 5种:
1. obj.fun() this->点前的obj对象
2. new Fun() this->new正在创建的新对象
3. 类型名.prototype.共有方法=function(){ ... }
this->将来谁调用这个函数,就指谁
将来调用这个函数的.前的某个子对象
4. fun() 和回调函数 和匿名函数自调 this->默认指window
5. 访问器属性中的this指访问器属性所在的当前对象
总结: 10. ES6:
(1). 模板字符串: 今后,只要拼接字符串,都用模板字符串代替+:
a. 整个字符串包裹在一对儿反引号`...`中
b. 反引号``中支持换行、""、''均可使用
c. 反引号中需要动态生成的内容必须放在${}里
d. ${}里:
1). 可以放一切有返回值的合法的变量或js表达式。
2). 不能放程序结构(分支和循环)以及没有返回值的js表达式
(2). let: 今后,声明变量都用let代替var
a. let的好处:
1). 阻止声明提前
2). 让代码块(分支和循环的{})也变成块级作用域,{}块内的变量出了{}无法使用,不会影响外部
b. let的特点:
1). 在同一作用域内禁止重复声明;
2). 禁止提前使用;
3). 在全局声明也不保存在window中
c. let的原理: 匿名函数自调
(3). 箭头函数: 今后,几乎所有的function都可用箭头函数简写:
a. 如何: 3句话:
1). 去掉function,在()和{}之间加=>
2). 如果只有一个形参,可省略()
3). 如果函数体只有一句话,可省略{}
如果仅有的一句话还是return,必须省略return
b. 特点: 箭头函数内部的this与外部的this,保持一致。
c. 今后:
1). 如果函数中没有this或者恰好希望函数内this与函数外this保持一致时,可用箭头函数简写!
2). 如果不希望内外this相同时不能使用箭头函数简写。
(4). for of: 今后只要遍历数字下标的索引数组、类数组对象和字符串,都用for of
for in: 今后只要遍历自定义下标名称的对象和关联数组都用for in
for | forEach | for of | for in | ||
数字下标 | 索引数组 | √ | √ | √ | 不保险 |
类数组对象 | √ | × | √ | 不保险 | |
字符串 | √ | × | √ | 不保险 | |
自定义名称下标 | 关联数组 | × | × | × | √ |
对象 | × | × | × | √ |
(5). 参数增强:
a. 参数默认值: 定义函数时最后一个形参不确定有没有实参时
function 函数名(形参1, ..., 最后形参=默认值){
... ...
}
b. 剩余参数: 定义函数时: 只要多个形参不确定,都用剩余参数
function 函数名(其它形参, ...数组名){
//...收集除其它形参外多余的实参值保存到指定数组名的数组中
}
c. 打散数组: 今后调用函数时,只要单纯打散数组再传参时
1). 如何: 函数(...数组)
2). ...口诀: 定义函数时...表示收集, 调用函数时...表示打散
3). ...语法糖:
i. 复制一个数组: var arr2=[...arr1];
ii. 合并多个数组和元素值: var arr3=[...arr1,值,...arr2,值]
iii. 复制一个对象: var obj2={ ... obj1 }
iv. 合并多个对象和属性: var obj3={ ...obj1, 属性:值, ...obj2, 属性:值 }
(6). 解构:
a. 只要想提取出数组中个别元素值,单独使用时,就数组解构:
[变量1, 变量2]=arr;
b. 只要想提取出对象中个别属性值或方法,单独使用时,就对象解构:
var {属性名也是变量名, 属性名也是变量名}=对象
c. 只要多个参数不确定有没有,又要求实参值必须传给指定的形参时,就参数结构:
定义函数时:
function 函数名({
属性名也是形参名="默认值",
属性名也是形参名="默认值",
... = ...
}){
函数体
}
调用函数时:
函数名({
要修改的形参名: 实参值,
... : ... ,
})
(7). class extends
a. 定义class:
class 类型名{
constructor(形参列表){
this.属性名=形参;
... = ...;
}
共有方法(){
... this.属性 ...
}
}
class的底层原理和用法与旧js构造函数用法完全相同!——新瓶装旧酒
b. 继承: 只要两种类型间包含部分相同的属性结构和方法定义,都要额外定义一个父类型集中保存两种类型相同的属性结构和方法定义。然后再让子类型继承父类型
class 子类型 extends 父类型{
constructor(...){
super(...);
this.属性名=形参;
}
子类型共有方法(){
...
}
}
两步,两个关键字: extends是继承的意思,super()是调用父类型构造函数,请父类型构造函数帮忙添加相同部分的属性的意思。
(8). Promise: 当多个异步任务必须顺序执行时,就可用promise
a. 前一项任务:
function 前一项任务(){
return new Promise(
function(door){
原异步任务;
原异步任务最后一句话之后door()
}
)
}
b. 调用前一项任务时:
前一项任务().then(下一项任务)
c. 如果下一项任务也返回new Promise对象,则可以继续用.then()相连
前一项任务().the(下一项任务).then(之后任务).then(...)...
d. 前后两个任务间传值:
1). 前一项任务内:
door(一个变量);
//只能传一个变量
//如果传多个值,可放在数组或对象中
2). 后一项任务声明时:
function 后一项任务(形参){ ... }
//前一项任务:door()中传什么
//后一项任务:形参就接住什么