一、ES6介绍

Javscript分为三大部分:ECMAScript + DOM + BOM(ES就是指 ECMAScript)

  • ECMAScript就是一种语法标准,规定了这个语言的语法要如何书写,何种语法有何种作用。之前学习的很多东西都是在ES5的标准里面的。5只是ECMAScript的其中一个版本,而6也是其中一个大版本。只是ES6和ES5相差的比较大,并且也相对好用,所以企业的使用面积非常大。
  • ES6 既是一个历史名词,也是一个泛指,含义是 5.1 版以后的 JavaScript 的下一代标准,涵盖了 ES2015、ES2016、ES2017 等等,而 ES2015 则是正式名称,特指该年发布的正式版本的语言标准。本书中提到 ES6 的地方,一般是指 ES2015 标准,但有时也是泛指“下一代 JavaScript 语言”。
  • 也就是说从2015年以后推出的新标准,现在统称为ES6

二、ES6变量声明

2.1、var关键字(变量可修改)

1、有预解析,可以先使用,后声明

2、可以重复定义同一个变量,逻辑错误,第二次应该是修改变量,而不是定义

3、没有块级作用域,会挂载到window全局变量污染

2.2、let关键字(变量可修改)

es6中为了统一并提高代码的安全性,引入了let关键字来代替var声明变量

1、没有预解析,声明后才能使用

2、不能重复声明

3、拥有块级作用域{}不会造成全局变量污染

2.3、const关键字(常量不可修改)

ES6中,为了让程序可以执行起来更加高效,推出了const关键字来声明一个只读的常量

1、没有预解析,声明后才能使用

2、一旦声明必须赋值

3、拥有块级作用域不会造成全局变量污染

4、声明引用类型的地址不能修改但值却可以修改

  1. const Arr = [10, 20, 30];
  2. Arr[1] = 40;
  3. console.log(Arr); //[ 10, 40, 30 ]

2.4、块级作用域案例

  1. var btns = document.querySelectorAll('button')
  2. for(var i = 0; i < btns.length; i++){
  3. btns[i].onclick = function(){
  4. console.log(i)
  5. }
  6. }
  1. 此时我们点击按钮,输出的i不会是每个按钮对应的按钮,而是按钮的个数。就是因为for里面的变量是var声明的,没有形成块级作用域,每次访问i都是访问的全局的i。如果我要在事件里面得到按钮的索引,就需要别的方法来实现。
  2. 如果我们换成let
  1. let btns = document.querySelectorAll('button')
  2. for(let i = 0; i < btns.length; i++){
  3. btns[i].onclick = function(){
  4. console.log(i)
  5. }
  6. }
  1. 此时我们点击按钮,就能输出按钮对应的索引了。这是因为我们使用了let关键字,let会在{}之间形成一个块级作用域,for循环执行多少次,我们就会形成多少个块级作用域,每个i对应1个,当我们在事件里面获取i的时候,i会到事件函数的上级作用域找,刚好就是每个i对应的块级作用域。我们就简单地得到了每个按钮对应的索引。

三、模板字符串

  1. `固定字符${变量或者表达式}`
  • 在模板字符串中,可以解析 ${} 之间的变量或者表达式
  • 在整个字符串中允许换行

四、解构

ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构(简化代码)。

4.1、对象解构

  1. let obj = {
  2. name: '张三',
  3. age18
  4. };
  5. // let name = obj.name;
  6. // let age = obj.age;
  7. // 将对象里的每个key对应的value赋值给每个同名的变量(等效于上面的写法)
  8. // let {name, age} = obj;
  9. //部分解构之后重命名(不重命名log打印不到let里面的name)
  10. let {name:newName} = obj;
  11. //解构内置对象里面的方法
  12. let {random} = Math; //{random:fn}
  13. console.log(random());

4.2、对象解构反向操作

将多个变量的打包成一个对象

  1. let name = "张三",age = 18;
  2. function ff() {
  3. console.log('函数');
  4. }
  5. // ES5 写法
  6. let obj = {
  7. name: name,
  8. age: age,
  9. ff: ff
  10. }

如果一个对象的属性名和外面的一个变量名同名,可以直接将变量名作为属性名,并会自动地把变量的值作为属性的值

  1. // es6 写法
  2. let obj = {
  3. name,
  4. age,
  5. ff
  6. };

4.3、数组解构

  1. let arr = [10, 20, 30];
  2. // 部分解构
  3. let [b] = arr;
  4. console.log(b); // 10(默认按顺序解构)
  5. let [,b] = arr;
  6. console.log(b); //20(用逗号占位)
  7. // 复合解构
  8. let arr2 = [10,20,[30,40]];
  9. let [a,b,[c,d]] = arr2;
  10. // 利用数组解构交换两个变量的值
  11. let a=10,b=20;
  12. [newa,newb] = [b,c];(将两变量放进数组)
  13. console.log(a,b) // a=20,b=10

4.4、字符串解构

  1. let str = 'xyz';
  2. let [a,b,c] = str;
  3. console.log(abc); //x y z
  4. str[1] = 'u';
  5. console.log(str); // 还是xyz 无法修改
  6. console.log(str[1]); // y

4.5、函数参数默认值和参数解构

4.5.1、函数形参的默认值

  1. function add(a,b,c){
  2. a = a || 0;
  3. b = b || 0;
  4. c = c || 0;
  5. return a + b + c;
  6. }
  7. //上面的例子就可以写成:
  8. function add(a=0,b=0,c=0){ //参数 为 undefined 时 参数 赋值为 默认值
  9. return a + b + c;
  10. }

4.5.2、函数参数的解构

  1. // 数组参数解构------顺序
  2. function hs([x, y, z]) {
  3. console.log(x, y, z);
  4. }
  5. hs([1, 2, 3]);
  6. // 对象参数解构------无序
  7. function fn({x, y, z}) { // {x, y, z} = obj 解构
  8. console.log(x, y, z);
  9. }
  10. fn({z: 4, x: 5, y: 6});

4.5.3、指定函数参数的默认值解构

  1. function fn({name, age} = {}){ //防止不传实参时候的报错
  2. console.log(name, age);
  3. }
  4. fn(); //undefined undefined
  5. // fn(); //相当于传了一个null {name, age}=null 就会报错
  6. // fn({}); //不会报错,输出:undefined undefined
  1. function fn2({name="张三", age=18} = {}){ //指定默认值
  2. console.log(name, age);
  3. }
  4. fn2(); //张三 18

五、rest 参数和拓展运算符

5.1、rest 参数

arguments 对象:

  1. function fn(){
  2. console.log(arguments);// 伪数组存所有传进来实参
  3. }
  4. fn(10, 20, 30, 50, 60);

ES6提供了新的方法:使用rest 参数搭配的变量是一个数组,该变量将多余的参数放入数组中。

1、rest得到的是真数组,可以用forEach

2、…rest必须是最后一个形参(最后一个形参获取剩余所有参数)

  1. function hs( a, b ,...rest){ // 把剩余的参数都交给rest
  2. console.log(rest);
  3. }
  4. hs(10, 20, 30, 50, 60);
  5. function hs2(...rest){ // rest 接收所有参数作为一个数组
  6. rest.forEach(function (item) {
  7. console.log(item);
  8. });
  9. }
  10. hs2(60, 70, 80, 90);

5.2、… 拓展运算符

ES6中的…扩展运算符,它的作用就是可以将数组或者对象展开,拆开成为一个一个单独的数据。

  1. let arr = [10, 20, 30];
  2. console.log(arr[0],arr[1],arr[2]);
  3. // 快速将一个数组拆开成一个一个的元素
  4. console.log(...arr); // 10, 20, 30 上述代码的简写
  5. // 快速将一个对象里面的数据复制一份到一个新的对象里面
  6. let obj = {name: '张三', age: 18}
  7. console.log({id:1 , ...obj});
  8. // 将一个字符串拆开成为多个单独的字符
  9. let str = 'abc'
  10. console.log(...str);
  11. // 函数传参使用拓展运算符
  12. console.log(Math.max(10,20,30)); //30 取最大值
  13. console.log(Math.max(...arr));

使用场景:

  1. // 1、合并数组(不影响原数组)
  2. let arr1 = [1, 2, 3];
  3. let arr2 = [10, 20, 30];
  4. let newArr = [...arr1, ...arr2]; //[1, 2, 3, 10, 20, 30]
  5. // 2、合并对象(不影响原对象)
  6. let obj1 = {
  7. name: "张三1",
  8. age: 18
  9. }
  10. let obj2 = {
  11. name: "张三2",
  12. email: "zhangsan@163.com"
  13. }
  14. let newObj1 = {...obj1,...obj2};
  15. //注意点: 对象的key不能重复的,张三2的会覆盖前面张三1
  16. console.log(newObj1); // {name: '张三2', age: 20, email: 'zhangsan@163.com'}
  17. //Object.assign();对象的合并, 将第二个以及后面的对象参数合并到第一个对象参数中
  18. let newObj2 = Object.assign({}, obj1, obj2); //第一个参数传{}目的: 不改变原对象
  19. console.log(newObj2); // {name: '张三2', age: 20, email: 'zhangsan@163.com'}

5.3、… 在解构赋值中的使用

数组解构赋值

按顺序把数组里面的元素解构到等号左边的变量里面,当左边的变量不够,会把剩下的数据放到d这个数组里面,此时d是就一个新数组。

  1. let arr = [1, 2, 3, 4, 5, 6, 7];
  2. let [a, b, c, ...d] = arr;
  3. console.log(a, b, c, d); // 1 2 3 [4, 5, 6, 7]

字符串解构赋值

  1. let str = '一个字符串';
  2. let [a, b, c, ...d] = str;
  3. console.log(a, b, c, d); // 一 个 字 ['符', '串']

六、箭头函数

6.1、基本语法

ES6 允许使用 “箭头”(=>)简化函数的定义,让我们在使用回调函数的时候更简单。

箭头函数不可以使用 arguments 获取参数列表,可以使用 rest 参数代替。

  1. 箭头函数语法: (参数) => { 函数体 }
  2. let fn = ()=>{
  3. console.log("我是一个箭头=>");
  4. }
  5. fn();

简写方式:

  1. // 无参数无返回
  2. let fn1 = () => console.log('fn1');
  3. fn1();
  4. // 无参数有返回:如果函数体里面只有一个语句,可以省略大括号不写, 并且他会默认返回 => 符号后面的数据。
  5. let fn2 = () => 'fn2';
  6. console.log(fn2());
  7. // 有一个参数无返回:形参个数如果为1个,可以省略小括号不写。
  8. let fn3 = x => console.log('fn3', x);
  9. fn3(2);
  10. // 有多个参数有返回:如果函数体有多个语句,则不能省略大括号。
  11. let fn4 = (x, y) => {
  12. let sum = x + y;
  13. return sum + 'fn4';
  14. };
  15. console.log(fn4(1, 2));
  16. // 如果return的是单一个对象,则需要加上大括号和return,例如:
  17. // let fn5 = (x, y) => {a:,x b:y}; //报错
  18. let fn6 = (x, y) => {
  19. return { a: x, b: y };
  20. };
  21. console.log(fn6(1, 2));

使用场景(回调函数):

  1. // 定时器中的回调函数
  2. setInterval(() => {
  3. console.log("我用了箭头函数");
  4. }, 1000);
  5. // forEach中的回调函数
  6. var arr = [22, 32, 11, 3, 5, 7, 88];
  7. arr.forEach(item => console.log(item));

6.2、this指向

this指向总结:1、this一般写在函数中 2、this永远指向一个引用地址(内存空间)

  • 全局使用(函数全局调用)指向window
  1. // 全局使用---------window(全局对象)
  2. console.log(this);
  3. function fn(){
  4. console.log(this);
  5. }
  6. fn();
  1. // 定时器中使用---------window(全局对象)
  2. window.setTimeout(function () {
  3. console.log(this);
  4. }, 1000)
  • 对象调用指向该对象(事件中的事件源)
  1. // 事件中使用----------事件源
  2. btn.onclick=function(){
  3. console.log(this);
  4. }
  1. // 对象方法调用,函数作用域内的this--------指向对象
  2. var name = "全局变量";
  3. let obj = {
  4. name: "局部变量",
  5. myThis: this,
  6. ff:function(){
  7. console.log(this.name); // 局部变量
  8. },
  9. ff2(){ // 简写
  10. console.log(this.name); // 局部变量
  11. }
  12. // 网上甚至有文档说,ff2(){}是箭头函数简写?????----错误
  13. }
  14. obj.ff(); // this指向obj
  15. obj.ff2(); // this指向obj
  16. console.log(obj.myThis); // this指向window(obj属性myThis: this)
  • 箭头函数没有自己的作用域,即箭头函数 this 指向其外层作用域
  1. var name = "全局变量";
  2. let obj = {
  3. name:"局部变量",
  4. // 对象方法不能使用箭头函数
  5. ff:()=>{
  6. console.log(this.name); // 没有自己的作用域,this 指向其外层作用域
  7. }
  8. }
  9. obj.ff(); // this指向外层作用域(所以创建字面量对象,不适合书写箭头函数)

6.3、箭头函数 this 指向案例

需求: 点击盒子,1s之后,改变这个盒子的宽度

  1. <div id="box" style="width: 50px; height: 50px"></div>
  1. // ES5解决: 存储this
  2. box.onclick = function () {
  3. // console.log(this);// 事件源----触发对象
  4. var _this = this;
  5. setTimeout(function () {
  6. // console.log(this);// 指向window
  7. // this.style.width = "300px"; // 用this修改不了
  8. _this.style.width = "300px";
  9. }, 1000)
  10. }
  1. // ES6解决: 箭头函数(没有this,就没有灵魂)-----最强大的功能:改变this执行,指向外层作用域
  2. box.onclick=function(){
  3. // console.log(this);// 事件源
  4. setTimeout(()=>{
  5. // this----->理论上当前作用域(window)---改变this指向--->指向了事件源(外层作用域)
  6. console.log(this);
  7. this.style.width = "300px"; // 用this修改成功
  8. },1000)
  9. }

七、Promise对象

7.1、Promise产生背景

我们如果在进行ajax请求的时候,一个效果需要有多个请求按照一定的顺序完成,如果不使用Promise实现,做起来就容易形成回调地狱

如:

  1. $.ajax({
  2. url:'url1',
  3. success(res){
  4. if(res.code == 200){
  5. // 再次发起请求
  6. $.ajax({
  7. url:'url2',
  8. success(res){
  9. if(res.code === 200){
  10. // 再次发起请求
  11. $.ajax({
  12. url:'url3',
  13. success(res){
  14. if(res.code === 200){
  15. // ..................
  16. }
  17. }
  18. })
  19. }
  20. }
  21. })
  22. }
  23. }
  24. })

这样的代码是非常恶心的,将来想要维护的时候,难度非常高,所以我们不推荐这样的写法。

小结:Promise的出现是用来解决异步回调的多层嵌套问题的(地狱回调)

7.2、Promise三个状态两个特点

Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。它由社区最早提出和实现,ES6 将其写进了语言标准,统一了用法,原生提供了Promise对象。

功能:避免了回调地狱,把异步代码改成调用起来像同步代码。

所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。

**Promise** 对象有三种状态:

  • pending: 初始状态,既不是成功,也不是失败状态。
  • fulfilled: 意味着操作成功完成。
  • rejected: 意味着操作失败。

**Promise**对象有两个特点:

  • 对象的状态不受外界影响。Promise对象代表一个异步操作,有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是Promise这个名字的由来,它的英语意思就是“承诺”,表示其他手段无法改变。
  • 一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pending变为rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果,这时就称为 resolved(已定型)。如果改变已经发生了,你再对Promise对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。
  1. /*语法:
  2. Promise------承诺
  3. var p = new Promise(回调函数);
  4. 回调函数也有两个参数 :
  5. resolve --------- 坚定信念
  6. reject --------- 拒绝爱情
  7. 这两个参数也是函数
  8. */
  9. var p = new Promise((resolve, reject) => {
  10. // ***默认状态----pending 悬而未决的
  11. // ***成功状态----fulfilled 满足了
  12. resolve(); // pending----->fulfilled
  13. // ***失败状态----rejected 拒绝,不开心了
  14. reject(); // pending----->rejected
  15. });
  16. console.log(p);

小结:

  1. Promise是一个构造函数,new出来实例对象可以帮我们解决异步操作的一些问题
  2. 当我们遇到异步操作的时候,都可以使用Promise来加工,便于后期的代码书写

7.3、Promise的基本使用

Promise的基本语法:

  1. // 假设: flag是ajax的返回状态
  2. let flag = true;
  3. let p = new Promise((resolve, reject) => {
  4. if (flag) {
  5. resolve("假装有返回数据");
  6. } else {
  7. reject("假装抛出异常");
  8. }
  9. console.log("Promise"); // 执行顺序1 **** Promise 本身是同步的文档自上而下执行所以先执行
  10. })
  11. // 执行顺序3 **** .then() .catch() 是异步的最后执行
  12. // .then()方法处理成功的回调------也就是resolve打包的数据
  13. // .catch()方法处理失败的回调-----也就是reject暴露的异常
  14. p.then(data => {
  15. console.log(data); // data就是成功时候抛出/打包/暴露的数据
  16. }).catch(error => {
  17. console.log(error); // error就是失败时候暴露的异常信息
  18. })
  19. // 上述代码只是基本语法
  20. console.log("全局"); // 执行顺序2 **** 全局也是同步的

7.4、使用Promise解决回调地狱

Promise的then链式调用的特点:
1、第一个then执行完会执行第二个then
2、then里面的函数的返回值,会被下一个then的形参接收
3、如果返回的是一个promise对象,下一个then的形参接收到的不是这个promise对象,而是这个promise对象内部调用resolve时候的实际参数(拿到的数据)

7.4.1、解决多重请求(回调地狱)

  1. let p1 = new Promise((resolve, reject) => {
  2. $.ajax({
  3. url: "http://baidu.com/aaa",
  4. type: "GET",
  5. success(res) {
  6. resolve(1);
  7. },
  8. error(err){
  9. reject(err);
  10. }
  11. })
  12. })
  13. let p2 = new Promise((resolve, reject) => {
  14. $.ajax({
  15. url: "http://baidu.com/aaa",
  16. type: "GET",
  17. success(res) {
  18. resolve(2);
  19. },
  20. error(err){
  21. reject(err);
  22. }
  23. })
  24. })
  25. let p3 = new Promise((resolve, reject) => {
  26. $.ajax({
  27. url: "http://baidu.com/aaa",
  28. type: "GET",
  29. success(res) {
  30. resolve(3);
  31. },
  32. error(err){
  33. reject(err);
  34. }
  35. })
  36. })
  37. // 如何解决回调地狱----将异步代码改成看起来像同步代码(方便维护)
  38. p1.then(data1=>{
  39. console.log(data1);
  40. return p2; // 改变下一个.then作用的对象
  41. }).then(data2=>{
  42. console.log(data2);
  43. return p3;
  44. }).then(data3=>{
  45. console.log(data3);
  46. }).catch(err=>{
  47. console.log(err.responseText);
  48. })

7.4.2、解决多重请求的简化写法

  1. function getPromiseObj(url,test) {
  2. return new Promise((resolve, reject) => {
  3. $.ajax({
  4. url, // 对象简写
  5. type: "GET",
  6. success(res) {
  7. resolve(test); // test用于测试返回的数据便于观看才这样写
  8. },
  9. error(err) {
  10. reject(err);
  11. }
  12. })
  13. })
  14. }
  15. let p1 = getPromiseObj("http://baidu.com/aaa",1);
  16. let p2 = getPromiseObj("http://baidu.com/aaa",2);
  17. let p3 = getPromiseObj("http://baidu.com/aaa",3);
  18. // 如何解决回调地狱----将异步代码改成看起来像同步代码(方便维护)
  19. p1.then(data1 => {
  20. console.log(data1);
  21. return p2; // 改变下一个.then作用的对象
  22. }).then(data2 => {
  23. console.log(data2);
  24. return p3;
  25. }).then(data3 => {
  26. console.log(data3);
  27. }).catch(err=>{
  28. console.log(err.responseText);
  29. })

小结: 我们可以通过多个promise调用then方法来把地狱回调转化为链式编程,便于后期的维护

7.5、Promise的all方法和race方法

  1. // 这里用定时器setTimeout,时间可控
  2. let p1 = new Promise((resolve, reject) => {
  3. setTimeout(() => {
  4. // resolve("成功1"); // 这里可以切换状态测试
  5. reject("失败1");
  6. }, 3000)
  7. })
  8. let p2 = new Promise((resolve, reject) => {
  9. setTimeout(() => {
  10. // resolve("成功2");
  11. reject("失败2");
  12. }, 2000)
  13. })
  14. let p3 = new Promise((resolve, reject) => {
  15. setTimeout(() => {
  16. resolve("成功3");
  17. // reject("失败3");
  18. }, 1000)
  19. })

Promise.all方法: (类似&&的关系 )

1、如果多个异步程序都是成功状态, p的状态就是成功, 多个异步程序的成功结果会打包成一个数组统一返回

2、但凡发现一个失败,最快直接返回第一个失败的结果

  1. // 场景:页面一进来,就要加载三个ajax,只有三个全部成功,才可以渲染页面
  2. // all里面的数组接收三个异步程序的结果,用来统一处理多个异步程序
  3. let p = Promise.all([p1,p2,p3]); // 试一下切换状态!!!
  4. p.then(res=>{
  5. console.log(res);
  6. }).catch(err=>{
  7. console.log(err);
  8. })

Promise.race方法:(类似||的关系)

1、有一个实例率先改变状态,p的状态就跟着改变,谁快返回谁 。

  1. let p = Promise.race([p1,p2,p3]); // 试一下切换状态!!!
  2. p.then(res=>{
  3. console.log(res);
  4. }).catch(err=>{
  5. console.log(err);
  6. })

7.6、异步代码同步化

async函数和await关键字一般成对出现,当函数执行的时候,一旦遇到await就会先返回,等到异步操作完成,再接着执行函数体内后面的语句。

  1. // 使用定时器模拟ajax , 时间就是响应成功的时间
  2. let p1 = new Promise((resolve, reject) => {
  3. setTimeout(() => {
  4. resolve("成功1");
  5. }, 3000)
  6. })
  7. let p2 = new Promise((resolve, reject) => {
  8. setTimeout(() => {
  9. resolve("成功2");
  10. }, 2000)
  11. })
  12. let p3 = new Promise((resolve, reject) => {
  13. setTimeout(() => {
  14. resolve("成功3");
  15. }, 1000)
  16. })
  17. // 需求: p1 p2 p3 三个ajax异步程序 顺序执行(同步代码执行顺序)
  18. // async await 一组关键字
  19. // 重点(项目中要用)*****
  20. // async 用来修饰函数,表示这是一个异步函数
  21. // await 在异步函数中使用,表示同步代码(异步程序变成同步代码)
  22. // ------await后面的异步执行完毕才会执行后续的同步代码
  23. async function getVal() {
  24. // p1 p2 p3 三个ajax异步程序 顺序执行
  25. await p1.then(res => console.log(res));
  26. await p2.then(res => console.log(res));
  27. await p3.then(res => console.log(res));
  28. console.log("同步");
  29. }
  30. getVal(); // 成功1 成功2 成功3 同步