ES6新特性详解

文章目录

  • 1. let和const
    • 1.1 let声明变量
    • 1.2 const声明常量
  • 2. 模板字符串
  • 3. 解构赋值
    • 3.1 数组的解构赋值
    • 3.2 对象的解构赋值
  • 4. 函数扩展
    • 4.1 参数默认值
    • 4.2 剩余参数
    • 4.3 箭头函数
  • 5. 对象扩展
    • 5.1 对象简写
    • 5.2 属性名表达式
    • 5.3 扩展运算符
  • 6. Symbol
  • 7. 迭代器和生成器
    • 7.1 Iterator
    • 7.2 Generator
  • 8. 代理和反射
    • 8.1 Proxy
    • 8.2 Reflect
  • 9. 异步
    • 9.1 Promise
    • 9.2 Async和Await
      • 9.2.2 Async
      • 9.2.3 Await
  • 10. 类
    • 10.1 类的定义和使用
    • 10.2 类的继承
  • 11. 模块
    • 11.1 export
    • 11.2 import

1. let和const

1.1 let声明变量

varlet 关键字的区别:

关键字 是否能重复声明变量 是否有块作用域 是否有变量提升 是否影响顶层对象
var ×
let × × ×

1.let 不允许重复声明变量

// var允许重复声明变量
var a = 10;
var a = 10; 
// let不允许重复声明变量
let a = 10;
let a = 10; // Uncaught SyntaxError: Identifier 'a' has already been declared

2.let 声明的变量有块作用域

// var声明的变量,没有块作用域
if (true) {
  var a = 10;
  console.log(a); // 10
}

console.log(a); // 10

// let声明的变量,有块作用域
if (true) {
  let a = 10; 
  console.log(a); // 10
}

console.log(a); // Uncaught ReferenceError: a is not defined

3.let 声明的变量,没有变量提升

/* 由于有变量提升,以下代码会转换成如下:
var a;
console.log(a);
a = 10; */

// var声明的变量,有变量提升
console.log(a); // undefined
var a = 10;

// let声明的变量,没有变量提升
console.log(a); // Uncaught ReferenceError: Cannot access 'a' before initialization
let a = 10;

4.let 声明的变量,不会与顶层对象挂钩

顶层对象:在broswer环境中,指window对象;在node环境中指global对象

// var声明的变量,会与顶层对象挂钩
var a = 10;
console.log(window.a); // 10
// let声明的变量,不会与顶层对象挂钩
let a = 10;
console.log(window.a); // undefined

1.2 const声明常量

const 拥有 let 的全部特性,与 let 不同的是,const声明的变量是常量,一经声明,无法修改。

1.const声明的变量无法修改

// let声明的变量,可以修改
let a = 10;
a = 20;
// const声明的变量,无法修改
const a = 10;
a = 20; // Uncaught TypeError: Assignment to constant variable

2.const声明的变量必须声明时就初始化

const a; // Uncaught SyntaxError: Missing initializer in const declaration

3.当变量是对象时,虽然不能修改对象名,但是可以修改对象的内部属性

解释:因为对象是引用类型,对象名存的是一个地址,而改变对象的内部属性,并没有改变地址本身。

const obj = {
  name: "Bill",
  age: 18
};

obj.age = 28;
console.log(obj); // {name: 'Bill', age: 28}

letconst 的使用场景:默认情况下使用 const,在知道变量值需要修改时使用 let

2. 模板字符串

模板字符串:其实就是占位符,这样就可以简化字符串拼接。

1.用于字符串拼接

const name = "Bill";
const age = 18;
const person = `my name is ${name}, my age is ${age}`;
console.log(person); // my name is Bill, my age is 18

2.支持使用表达式

// 1.支持四则运算
const a = 10;
const b = 20;
const result = `result is ${a + b}`;
console.log(result); // result is 30

// 2.支持三元表达式
const isEqual = `结果是${a === 10 ? "相等" : "不相等"}`;
console.log(isEqual); // 结果是相等

3. 解构赋值

解构赋值:用于赋值运算,可以简化赋值。

3.1 数组的解构赋值

1.简化数组的赋值

const arr = [1, 2, 3];

const [a, b, c] = arr; // 等价于 a=1, b=2, c=3
console.log(a, b, c); // 1 2 3

const [d, , e] = arr; // 等价于 d=1, e=3
console.log(d, e); // 1 3

2.嵌套情况下的数组赋值

const arr = [1, [2, 3, 4], 5];
const [a, [b, c], d] = arr; // 等价于 a=1, b=2, c=3, d=5
console.log(a, b, c, d); // 1 2 3 5

3.2 对象的解构赋值

1.简化对象的赋值

const obj = {
  name: "Bill",
  age: 18
};

const { name, age } = obj; // 等价于 name=obj.name, age=obj.age
console.log(name, age); // Bill 18

2.声明的变量名需要与对象的属性名一致时,否则在对象的属性名中匹配不到,得到的值是undefined

对象的解构赋值,变量赋值时会与属性名匹配,不会按照声明变量的前后顺序匹配

const obj = {
  name: "Bill",
  age: 18,
  addr: "Shanghai"
};

const { name, addr, a } = obj; // 等价于 name=obj.name, addr=obj.addr
console.log(name, addr, a); // Bill Shanghai undefined

3.嵌套情况下的对象赋值

const obj = {
  name: "Bill",
  age: 18,
  other: {
    addr: "Shanghai",
    email: "[email protected]"
  }
};

const {
  name,
  age,
  other: { email }
} = obj; // 等价于 name=obj.name, age=obj.age, email = obj.other.email
console.log(name, age, email); // Bill 18 [email protected]

4. 函数扩展

4.1 参数默认值

参数默认值:不传参时,函数使用默认的参数值,传参后会覆盖默认值。

function fn(a, b = 10) {
  console.log(a, b);
}

fn(10); // 10 10
fn(10, 20); // 10 20

4.2 剩余参数

剩余参数:也称可变参数,剩余参数语法允许将一个不定数量的参数表示为一个数组。

function fn(a, b, ...args) {
  console.log(args);
}

fn(10, 20, 30, 40, 50); // [30, 40, 50]

4.3 箭头函数

箭头函数:箭头函数可用于简化匿名函数的定义,使书写更为简洁。

1.无参数、无返回值的箭头函数

// 匿名函数写法,无参数、无返回值
const fn = function () {
  console.log(100);
};

fn(); 

// 箭头函数写法,无参数、无返回值
const fn = () => {
  console.log(100);
};

fn();

2.有返回值的箭头函数

// 匿名函数写法,有返回值
const fn = function () {
  console.log(100);
  return 100;
};

// 箭头函数写法,有返回值

// 1.函数内部只有 return,无其它内容
const fn1 = () => 100; 
const fn2 = () => ({ name: "Bill", age: 18 }); // 返回的是对象时,要使用小括号包裹起来

// 2.函数内部除了有 return,还有其它内容
const fn3 = () => {
  console.log(100);
  return 100;
};

3.带参数的箭头函数

// 匿名函数写法,带参数
const fn = function (a, b) {
  console.log(a + b);
  return a + b;
};

// 箭头函数写法,带参数

// 1.只有一个参数时,可以省略括号
const fn1 = a => {
  console.log(a);
};

const fn2 = a => a;

// 2.带多个参数时,不可省略括号
const fn3 = (a, b) => a + b;

const fn4 = (a, b) => {
  console.log(a + b);
  return a + b;
};

4.箭头函数没有this

DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Documenttitle>
  head>
  <body>
    <button value="按钮">点击按钮button>
    <script>
      const btn = document.querySelector("button");
      btn.addEventListener("click", function () {
        console.log(this.value); // 按钮
        // 使用匿名函数
        const fn = function () {
          console.log(this.value); // undefined
        };
        fn();
      });
    script>
  body>
html>

DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Documenttitle>
  head>
  <body>
    <button value="按钮">点击按钮button>
    <script>
      const btn = document.querySelector("button");
      btn.addEventListener("click", function () {
        console.log(this.value); // 按钮
        // 使用箭头函数
        const fn = () => {
          console.log(this.value); // 按钮
        };
        fn();
      });
    script>
  body>
html>

注:监听事件的函数,要写成匿名函数,不要写成箭头函数,否则this指向的就是window

5. 对象扩展

5.1 对象简写

对象简写:简写对象的属性和方法

  • 当属性名和属性值相同时,可以简写为只写一个属性名
  • 可以省略方法中的:function
// es5写法
const name = "Bill";
const obj = {
  name: name,
  test: function () {}
};
// es6写法
const name = "Bill";
const obj = {
  name,
  test() {}
};

5.2 属性名表达式

属性名表达式:对象的属性名,可以使用变量和表达式的方式命名

const name = "a";
const obj = {
  [name]: "Bill",
  [name + "bc"]: "Jackson"
};

console.log(obj); // {a: 'Bill', abc: 'Jackson'}
      

注:对象简写和属性名表达式不能同时使用

5.3 扩展运算符

扩展运算符:用于取出参数对象中的所有可遍历属性,拷贝到当前对象之中

  • 对象的扩展运算符:用于拷贝对象属性
  • 数组的扩展运算符:将数组分割为参数序列、拷贝数组

1.拷贝对象

const obj1 = { name: "Bill", age: 18 };
const obj2 = { name: "Jackson" };
const obj3 = { addr: "Shanghai" };
const obj = { ...obj1, ...obj2, ...obj3 };
console.log(obj); // {name: 'Jackson', age: 18, addr: 'Shanghai'}    

注:当对象有同名属性时,后合并的对象,会覆盖之前的对象的属性

2.拷贝数组

const arr1 = [10, 20, 30];
const arr2 = [10, 40, ...arr1];
console.log(arr2); // [10, 40, 10, 20, 30]

3.将数组分割为参数序列

const arr = [10, 30, 50];
console.log(Math.max(10, 20, ...arr)); // 50

剩余参数的...和扩展运算符...的区别:

  • 剩余运算符:把多个参数合并为一个数组
  • 扩展运算符:把一个数组分割为多个参数

6. Symbol

Symbol:是一种基本数据类型,表示独一无二的值。

1.创建Symbol变量

// 1.创建不带描述的Symbol变量
const s1 = Symbol();
console.log(s1); // Symbol()

// 2.带描述的Symbol变量
const s2 = Symbol("Symbol2"); // 传入的字符串为Symbol变量的描述
console.log(s2); // Symbol(Symbol2)

注:Symbol是基本数据类型,不是对象类型,所以不能使用new创建Symbol变量

2.Symbol的值是唯一的

const s1 = Symbol();
const s2 = Symbol();
console.log(s1 === s2); // false

const s3 = Symbol("Symbol");
const s4 = Symbol("Symbol");
console.log(s3 === s4); // false

3.Symbol变量不能进行运算

const s = Symbol();
console.log(s + "hello"); // Uncaught TypeError: Cannot convert a Symbol value to a string

7. 迭代器和生成器

7.1 Iterator

迭代:从一个数据集合中按照一定的顺序,不断取出数据的过程。

迭代器(Iterator):

  • 为各种数据结构,提供了一个可以统一、简便的访问接口
  • 使得数据结构的成员,能够按照某种顺序排列
  • Symbol.iterator 为对象定义了迭代器,可以被 for...of 循环使用

1.当对象具有Symbol.iterator接口时,就可以使用for...of迭代对象

1.原生具备 Symbol.iterator 接口的数据结构有:Array、Set、Map、String、arguments、NodeList
2.for...of 是语法糖,本质上还是用的迭代器方法

const arr = [1, 3, 5, 7, 9];
console.log(arr);

for (const i of arr) {
  console.log(i);
}
      

点击Array对象,展开[[Prototype]]: Array(0)以后,发现具有Symbol(Symbol.iterator) : ƒ values(),因此可以使用for...of遍历数组。

ES6新特性详解_第1张图片

2.通过 Symbol.iterator 来创建迭代器

const arr = [1, 3, 5, 7, 9];
const iter = arr[Symbol.iterator](); // 创建迭代器

console.log(iter.next()); // {value: 1, done: false}
console.log(iter.next()); // {value: 3, done: false}
console.log(iter.next()); // {value: 5, done: false}
console.log(iter.next()); // {value: 7, done: false}
console.log(iter.next()); // {value: 9, done: false}
console.log(iter.next()); // {value: undefined, done: true}
      

next方法返回一个对象,该对象包含两个属性:

  • value:下一个数据的值
  • done:已经迭代到序列中的最后一个值,为 true,否则为false

Iterator 的遍历过程:

  1. 创建一个指针对象,指向当前数据结构的起始位置。也就是说,迭代器对象本质就是一个指针对象。
  2. 第一次调用指针对象的next方法,可以将指针指向数据结构的第一个成员。
  3. 第二次调用指针对象的next方法,可以将指针指向数据结构的第二个成员。
  4. 不断地调用指针对象的next方法,直到它指向数据结构的结束位置。

7.2 Generator

Generator(生成器)函数:

  • 是一种异步编程解决方案
  • 是一个状态机,封装了多个内部状态
  • 执行Generator函数会返回一个遍历器对象,可以遍历每一个状态

1.next方法执行下一个状态,一个yield代表一个状态,碰到yield停下

function* gen() {
  console.log(10);
  yield;
  console.log(20);
  yield;
  console.log(30);
}

const g = gen();
g.next(); // 10
g.next(); // 20
g.next(); // 30

2.yield可以跟返回的结果

function* gen() {
  console.log(10);
  yield "aaa";
  console.log(20);
  yield "bbb";
  console.log(30);
  return "ccc";
}

const g = gen();
const res1 = g.next(); // 10
console.log(res1); // {value: 'aaa', done: false}

const res2 = g.next(); // 20
console.log(res2); // {value: 'bbb', done: false}

const res3 = g.next(); // 30
console.log(res3); // {value: ccc, done: true}

3.可以使用for...of进行遍历每个状态

function* gen() {
  console.log(10);
  yield "aaa";
  console.log(20);
  yield "bbb";
  console.log(30);
  return "ccc";
}

const g = gen();
for (const i of g) {
  console.log(i);
}

4.通过next方法,可以传入参数到生成器函数

注:第一个next方法传入的参数不会生效

function* gen() {
  const res1 = yield;
  console.log(res1);
  const res2 = yield;
  console.log(res2);
}

const g = gen();
g.next(10); // 无输出
g.next(20); // 20
g.next(30); // 30
      

分析:
1.执行第一个next时,碰到第一个yield停下来了,还没有执行赋值操作
2.执行第二个next时,将传入的参数20赋值给了res1,接着碰到第二个yield停下
3.执行第三个next时,将传入的参数30赋值给了res2,接着走到函数结束

8. 代理和反射

8.1 Proxy

代理(Proxy ):作用是在对象和对象的属性值之间设置一个代理,获取该对象的值、设置该对象的值以及实例化等操作,都会被拦截住。经过这一层,我们可以统一处理,我们可以认为它就是代理器。

拦截:获取和修改数据时会进行拦截,用于脏数据检查。

1.es5 拦截方法:Object.defineProperty()

Object.defineProperty(obj, prop, descriptor)

  • obj:要被修改拦截的对象
  • prop:要被修改拦截的属性
  • descriptor:当访问属性时,会调用get函数;当属性值被修改时,会调用set函数。

缺点:
1.一次只能拦截一个属性
2.只能拦截对象

DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Documenttitle>
  head>
  <body>
    <p>Hello Worldp>
    <script>
      const p = document.querySelector("p");
      const obj = {};
      
	  // 为obj对象的data属性设置拦截
      Object.defineProperty(obj, "data", {
        get() {
          return p.innerHTML;
        },
        set(value) {
          p.innerHTML = value;
        }
      });
    script>
  body>
html>

分别输入obj.dataobj.data='aaa'obj.data,触发 get、set 和 get, 运行结果如下:

在这里插入图片描述

2.es6 拦截方法:Proxy(target, handler)

Proxy(target, handler)

  • target:要被修改拦截的对象
  • handler:执行各种操作时代理的行为

优点:
1.一次可以拦截多个属性
2.可以拦截任何类型的对象,包括数组、函数等

DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Documenttitle>
  head>
  <body>
    <p>Hello Worldp>
    <script>
      const p = document.querySelector("p");
      const obj = {};
      
	  // 为obj对象创建一个代理
      const proxy = new Proxy(obj, {
        get(target, key) {
          return target[key];
        },
        set(target, key, value) {
          // 访问data属性时,修改dom元素
          if (key === "data") {
            p.innerHTML = value;
          }
          target[key] = value;
        }
      });
    script>
  body>
html>

操作obj对象,运行结果如下:

在这里插入图片描述

操作proxy对象,运行结果如下:

ES6新特性详解_第2张图片

8.2 Reflect

反射(Reflect):用于获取目标对象的行为,它与Object类似,但是更易读,为操作对象提供了一种更优雅的方式。它的方法与proxy handlers 的方法相同。

1.代替Object的某些方法

const obj = {};
Reflect.defineProperty(obj, "name", {
  value: "Bill"
});

2.修改某些Object方法的返回结果

// Object.defineProperty返回值是对象,只能使用try...catch做异常处理
try {
  Object.defineProperty(target, property, attributes);
  // success
} catch (e) {
  // fail
}

// Reflect.defineProperty方法的返回值为Boolean值,可以使用if...else做异常处理
if (Reflect.defineProperty(target, property, attributes)) {
  // success
} else {
  // fail
}
      

3.命令式写法改为使用函数写法

// 命令式写法
const obj = { name: "Bill" };
console.log("name" in obj); // true
delete obj.name;
// Reflect函数写法
const obj = { name: "Bill" };
console.log(Reflect.has(obj, "name")); // true
Reflect.deleteProperty(obj, "name");

4.配合Proxy使用,反射到代理对象原来的默认行为上

const s = new Set();
const proxy = new Proxy(s, {
  get(target, key) {
    const value = Reflect.get(target, key); // 等价于const value = target[key]
    // 判断如果是方法,修正this指向
    if (value instanceof Function) {
      return value.bind(target);
    }
    return value;
  },
  set(target, key, value) {
    Reflect.set(target, key, value); // 等价于target[key] = value
  }
});

注:还可以和扩展运算符配合使用,将以上的 getset 方法,改为 Reflect.get(...arguments)
Reflect.set(...arguments)

在控制台输入内容,运行结果如下:

ES6新特性详解_第3张图片

9. 异步

9.1 Promise

Promise:是异步编程的一种解决方案,比传统的解决方案回调函数,更合理和更强大。

Promise的作用: 解决异步回调地狱的问题。

回调地狱:当一个回调函数嵌套一个回调函数的时候,就会出现一个嵌套结构,当嵌套多了就会出现回调地狱的情况。
例如发送三个ajax请求:

  • 第一个正常发送
  • 第二个请求需要第一个请求的结果中某一个值作为参数
  • 第三个请求需要第二个请求的结果中某一个值作为参数

Promise的状态:

  • 待定(pending):初始状态,既没有被兑现,也没有被拒绝。
  • 已兑现(fulfilled):意味着操作成功完成。
  • 已拒绝(rejected):意味着操作失败。
  1. 这三种状态的变化途径只有两种:从待定到已兑现、从待定到已拒绝。
  2. 一旦状态发生变化,就凝固了,不会再有新的状态变化,这是Promise(承诺)这个名字的由来,一旦承诺生效,就不能再改变了。

Promise构造器语法:

new Promise(executor)

1.Promise的基础示例

const promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("success");
  }, 1000);
});

promise.then(res => {
  console.log(res); // success
});

当执行了resolve方法,则会执行第一个回调函数then方法

2.异步操作成功执行resolve方法,然后回调then方法,异步操作失败执行reject方法,然后回调catch方法

1.resolve方法传入的参数,作为then方法的入参
2.reject方法传入的参数,,作为catch方法的入参

const promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("success");
    reject("failed");
  }, 1000);
});

promise
  .then(res => {
    console.log(res); // success
  })
  .catch(err => {
    console.log(err); // failed
  });
  

运行结果:

在这里插入图片描述

分析:先调用了 resolve 方法,导致状态从待定到已兑现,然后状态发生凝固,后续reject方法不再执行。

3.链式调用,解决回调地狱问题

链式调用图解:
ES6新特性详解_第4张图片

const promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("success");
    reject("failed");
  }, 1000);
});

promise
  .then(res => {
    console.log(`first ${res}`); // first success
    return res;
  })
  .then(res => {
    console.log(`second ${res}`); // second success
  })
  .catch(err => {
    console.log(err);
  });
        

分析:

  1. 先调用了resolve,然后执行第一个then方法,promise对象的状态从待定到已兑现。
  2. 第一个then方法执行完成后,又返回了一个新的promise对象(同时返回值也会作为第二个then方法的入参),新的promise对象继续执行了第二个then方法,然后新promise对象的状态也从待定到已兑现。

4.Promise.all():等待所有传入的 promise 都变为完成状态后,再执行回调

const p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve(1000);
  }, 1000);
});

const p2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve(2000);
  }, 2000);
});

const p3 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve(3000);
  }, 3000);
});

// p1, p2, p3都执行了resolve后,调用then
Promise.all([p1, p2, p3])
  .then(res => {
    console.log(res); // [1000, 2000, 3000]
  })
  .catch(err => {
    console.log(err);
  });
        

5.Promise.race():所有传入的 promise 中,先变为完成状态的 promise 执行回调

// p1等待一秒,最先执行完毕
const p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve(1000);
  }, 1000);
});

const p2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve(2000);
  }, 2000);
});

const p3 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve(3000);
  }, 3000);
});

// p1, p2, p3第一个执行resolve的,调用then
Promise.race([p1, p2, p3])
  .then(res => {
    console.log(res); // 1000
  })
  .catch(err => {
    console.log(err);
  });
        

9.2 Async和Await

Async + AwaitPromise + Generator的语法糖,它可以使得Promise的书写更为简单,可以使用同步的方式,来执行异步操作。

async 和 await:

  • async 使函数返回 Promise
  • await 使函数等待 Promise

9.2.2 Async

1.函数前加上关键字 async,使函数返回 promise 对象

async function fn() {
  return "Hello World"; // 等价于return Promise.resolve("Hello World");
}

const res = fn();
console.log(res); // Promise {: 'Hello World'}

2.return的值为promise对象时,异步操作成功执行resolve方法,然后回调then方法,异步操作失败执行reject方法,然后回调catch方法

1.resolve方法传入的参数,作为then方法的入参
2.reject方法传入的参数,,作为catch方法的入参

async function fn() {
  return new Promise((resolve, reject) => {
    resolve("success");
    // reject("failed");
  });
}

const promise = fn();
promise
  .then(res => {
    console.log(res); // success
  })
  .catch(err => {
    console.log(err); // failed
  });
        

9.2.3 Await

await 使函数等待 promise

注:await 关键字只能在 async 函数中使用

1.不使用await,不会等待promise

function fn() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log(1);
      resolve(2);
    }, 2000);
  });
}

// 不使用await
async function test() {
  const res = fn();
  console.log(res); 
  console.log(3);
}

test();
      

运行结果:

ES6新特性详解_第5张图片

分析:
1.未使用await,因为setTimeout是异步的,setTimeout的回调函数还未执行,那么resolve也还没执行到,就已经把promise对象返回了,因此打印出来的res是一个还在pending状态的promise对象。
2.因为setTimeout是异步的,没有阻塞后续代码的执行,所以先打印了3。
3.两秒钟结束后,执行setTimeout的回调函数,打印了1,再执行了resolve方法。

2.使用await,会等待promise返回

function fn() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log(1);
      resolve(2);
    }, 2000);
  });
}

// 使用await
async function test() {
  const res = await fn();
  console.log(res);
  console.log(3);
}

test();

运行结果:

在这里插入图片描述

分析:
1.使用了await,会一直等待promise返回,直到resolve方法执行后,再执行后续代码,因此先执行了setTimeout的回调函数(因为resolve方法在该回调函数中),打印了1
2.执行了resolve,将resolve的结果,返回给res,打印了2
3.再执行后续代码,最后打印了3

3.使用await,只会等待promise,而不会等待其他的异步

以下代码,使用了await,并不会等待setTimeout先执行回调函数,因为resolve不在setTimeout的回调函数中

function fn() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log(1);
    }, 2000);

    resolve(2); // 不把resolve放在setTimeout回调函数内
  });
}

// 使用await
async function test() {
  const res = await fn();
  console.log(res);
  console.log(3);
}

test();
      

运行结果:

在这里插入图片描述

分析:
1.调用fn时,先执行到了setTimeout,由于它是异步的,其回调函数没有先执行
2.然后执行了resolve方法,将结果返回给了res,打印了2
3.再执行后续代码,最后打印了3
4.两秒钟结束后,执行setTimeout的回调函数,打印了1

10. 类

类:类不是对象,而是对象的模板

10.1 类的定义和使用

类的语法:

class ClassName {
  constructor() { ... }
  method_1() { ... }
  method_2() { ... }
  method_3() { ... }
}

1.类的定义和使用

  • 使用关键字 class 创建一个类
  • 通过构造方法 constructor() 给属性赋值
  • 创建类方法的语法与对象方法相同

构造方法:
1.创建对象时会自动调用构造方法
2.构造方法用于初始化对象属性
3.如果没有定义构造方法,JS 会添加一个空的构造方法

// 类的定义
class Person {
  // 构造方法
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }
  // 类方法
  say() {
    console.log(this.name, this.age);
  }
}

const person = new Person("Bill", 18); // 使用类来创建对象
console.log(person.name); // Bill
console.log(person.age); // 18
person.say(); // Bill 18

2.类可以使用 getter 和 setter

  • 在类中添加 getter 和 setter,请使用 getset 关键字
  • getter/setter 的方法名不能与属性名相同
  • 建议使用下划线字符将 getter/setter 与实际属性分开
class Person {
  constructor(name, age) {
    this._name = name;
    this._age = age;
  }
  // getter
  get getName() {
    return this._name;
  }
  // setter
  set setName(x) {
    this._name = x;
  }
}

const person = new Person("Bill", 18);
console.log(person.getName); // Bill
person.setName = "Jackson";
console.log(person.getName); // Jackson
      

10.2 类的继承

类继承:使用类继承创建的类,继承了另一个类的所有方法

1.使用关键字 extends来继承类

super()方法引用父类:
1.在constructor方法中调用 super方法,则调用了父类的 constructor 方法,获得了父级的属性和方法的访问权限
2.在类方法中调用 super方法,则调用了父类的类方法

// 创建一个Person类
class Person {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }
  say() {
    console.log(this.name, this.age);
  }
}

// 创建一个Student类,继承了Person类
class Student extends Person {
  constructor(name, age, score) {
    super(name, age); // 调用父类的constructor方法
    this.score = score;
  }
}

const student = new Student("Bill", 18, 90);
student.say(); // 调用父类的方法
      

2.重写方法

  • 当子类的方法名与父类的方法名一致时,子类的方法会覆盖父类的方法
  • 子类的对象调用该方法时,调用的是子类的方法,而不会调用父类的方法
// 创建一个Person类
class Person {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }
  say() {
    console.log(this.name, this.age);
  }
}

// 创建一个Student类,继承了Person类
class Student extends Person {
  constructor(name, age, score) {
    super(name, age);
    this.score = score;
  }
  // 重写父类的方法
  say() {
    console.log(`${this.name}: age is ${this.age}, score is ${this.score}`);
  }
}

const student = new Student("Bill", 18, 90);
student.say(); // Bill: age is 18, score is 90
      

11. 模块

模块:一个模块(module)就是一个js文件,可以通过exportimport命令来实现两个模块之间的交互,也就是一个js文件可以引用另一个js文件的内容。

  • export:表示当前模块的变量和函数可以被外部访问
  • import:导入了其它模块的变量和函数到当前模块

注:以前使用的方式来引用js文件,会带来一些问题(如引用的两个js文件有函数名和变量名相同),而通过模块化可以解决这些问题。

11.1 export

1.定义变量和函数的同时就导出

export const name = "Bill";
export const age = 18;

export function fn() {
  console.log("Hello World");
}

2.定义变量和函数后再导出

const name = "Bill";
const age = 18;

function fn() {
  console.log("Hello World");
}

export { name, age, fn };

3.使用as关键字,可以给导出的变量和函数取别名

注:导出的时候取了别名的话,导入时也需要使用导出时的别名

const name = "Bill";
const age = 18;

function fn() {
  console.log("Hello World");
}

export { name as gName, age as gAge, fn as gFn };

4.export default默认导出

  • 一个模块中,只能有一个export default
  • 默认导出时可以不需要指定名字
  • 导入时不需要使用{},并且可以自己来指定名字
// 默认导出变量
const name = "Bill";
export default { name };
// 默认导出函数
export default function fn() {
  console.log("Hello World");
}

11.2 import

准备一个test.js文件,用于导出变量和函数,内容如下:

const name = "Bill";
const age = 18;

function fn() {
  console.log("Hello World");
}

export { name, age, fn };

1.在js文件中导入另一个js文件

test.js同级目录下,新建一个demo.js,内容如下:

import { name, age, fn } from "./test.js";

console.log(name, age); // Bill 18
fn(); // Hello World

2.在html文件中导入另一个js文件

注:html文件导入js文件时,要设置script标签的属性type="module",否则会报错

test.js同级目录下,新建一个demo.html,内容如下:

DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Documenttitle>
  head>
  <body>
    <script type="module">
      // 导入test.js
      import { name, age, fn } from "./test.js";

      console.log(name, age); // Bill 18
      fn(); // Hello World
    script>
  body>
html>

3.使用as关键字,可以给导入的变量和函数取别名

import { name as gName, age as gAge, fn as gFn } from "./test.js";

console.log(gName, gAge);
gFn();

4.使用*as,可以把所有导入变量和函数指定到一个模块对象上

注:使用这种方式导入时,要去掉{}

import * as obj from "./test.js";

console.log(obj.name, obj.age);
obj.fn();

你可能感兴趣的:(Web前端,javascript,es6,前端)