ES6新特性详解

news2024/9/24 9:24:57

文章目录

  • 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. Iterator和Generator
    • 7.1 Iterator
    • 7.2 Generator
  • 8. Proxy和Reflect
    • 8.1 Proxy
    • 8.2 Reflect
  • 9. Promise
  • 10. Async和Await
    • 10.1 Async
    • 10.2 Await
  • 11. 类
    • 11.1 类的定义和使用
    • 11.2 类的继承
  • 12. 模块
    • 12.1 export
    • 12.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: "xxx@163.com"
  }
};

const {
  name,
  age,
  other: { email }
} = obj; // 等价于 name=obj.name, age=obj.age, email = obj.other.email
console.log(name, age, email); // Bill 18 xxx@163.com

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>Document</title>
  </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>Document</title>
  </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. Iterator和Generator

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遍历数组。

在这里插入图片描述

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. Proxy和Reflect

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>Document</title>
  </head>
  <body>
    <p>Hello World</p>
    <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>Document</title>
  </head>
  <body>
    <p>Hello World</p>
    <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对象,运行结果如下:

在这里插入图片描述

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)

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

在这里插入图片描述

9. 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.链式调用,解决回调地狱问题

链式调用图解:
在这里插入图片描述

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);
  });
        

10. Async和Await

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

async 和 await:

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

10.1 Async

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

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

const res = fn();
console.log(res); // Promise {<fulfilled>: '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
  });
        

10.2 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();
      

运行结果:

在这里插入图片描述

分析:
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

11. 类

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

11.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
      

11.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
      

12. 模块

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

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

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

12.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");
}

12.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>Document</title>
  </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();

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/356708.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

k8s自动化安装脚本(kubeadm-1.23.7)

文章目录介绍软件架构版本介绍更新内容2023-02-192023-02-152023-02-142023-02-102022-10-202022-08-06准备部署包操作步骤环境准备结构备注解压部署包修改host文件脚本使用方式初始化环境验证ansible配置安装k8s集群登录master的节点添加node节点master节点状态检查组件安装安…

高精度算法一

目录 1. 基础知识 2. 大整数 大整数 3. 大整数 - 大整数 1. 基础知识 利用计算机进行数值计算&#xff0c;有时会遇到这样的问题&#xff1a;有些计算要求精度高&#xff0c;希望计算的数的位数可达几十位甚至几百位&#xff0c;虽然计算机的计算精度也算较高了&#xff0c…

大数据之---Nifi-处理器类别_详细了解各种处理器的含义用途---大数据之Nifi工作笔记0007

处理器有很多类这里主要分了,数据转换的可以看到上面的这些是数据转换常用的 然后: 这里这个controlrate,用来控制流的速率,来保护作用,防止崩掉 distributeLoad用来负载均衡,减轻压力 这里的路由是,根据是否满足对应的条件,来执行是否把数据路由到某个地方. 然后来看数据库…

数据库技术-函数依赖、键与约束、范式

一、函数依赖 给定一个x&#xff0c;能唯一确定一个Y&#xff0c;就称x确定Y&#xff0c;或者说Y依赖于x&#xff0c;例如YX*X函数。 函数依赖又可扩展以下两种规则: 部分函数依赖:A可确定C&#xff0c;(A,B)也可确定C,(A,B)中的一部分&#xff08;即A&#xff09;可以确定C&a…

Spring Cloud Nacos实战(一)- 下载和安装

Spring Cloud Alibaba Nacos下载和安装 Nacos介绍 ​ Nacos&#xff08;Naming Configuration Service&#xff09; 是一个易于使用的动态服务发现、配置和服务管理平台&#xff0c;用于构建云原生应用程序 ​ 服务发现是微服务架构中的关键组件之一。Nacos 致力于帮助您发现…

使用nginx单独部署Vben应用

前言 本文主要介绍Vben使用nginx单独部署的方式&#xff0c;其实前端发展到现在已经不是当年的jsp&#xff0c;asp必须要和后端一起部署了。单独部署调试的工具也很多&#xff0c;比如vue-cli-service 和 Vben中用到的vite &#xff0c;当然这些我们一般用在开发的工程中。正式…

SpringCloud(三)Hystrix断路器服务降级、服务熔断、服务监控案例详解

七、Hystrix断路器 7.1 简介 分布式系统面临的问题 复杂分布式体系结构中的应用程序有数十个依赖关系&#xff0c;每个依赖关系在某些时候将不可避免地失败。 多个微服务之间调用的时候&#xff0c;假设微服务A调用微服务B和微服务C&#xff0c;微服务B和微服务C又调用其它的微…

C# chart绘图 鼠标响应

1、图形自动滚动设置 chart1.ChartAreas[0].AxisX.Maximum 横坐标显示区域最大值 chart1.ChartAreas[0].AxisX.Minimum 横坐标显示区域最小值 显示宽度 chart1.ChartAreas[0].AxisX.Maximum - chart1.ChartAreas[0].AxisX.Minimum chart1.ChartAreas[0].AxisX.Maximum x_d…

ChatGPT从业影响之个人浅见

文章目录前言ChatGPT简介AI简介ChatGPT对从业者的影响参考资料前言 ChatGRT最近很火&#xff0c;网上有一些观点&#xff0c;他会取代很多职业&#xff0c;让产业产生颠覆性的变化。今天聊聊自己的想法&#xff0c;他的出现&#xff0c;是否会让我&#xff08;程序员&#xff…

qmt api 接口文档

http://qmt.ptradeapi.com 用户自行安装 Python 三方库 对于有经验的 Python 开发者来说&#xff0c;平台提供了自行安装第三方库的方式。为了引入额外的第三方库&#xff0c;用户需要做如下一些操作&#xff1a; 安装前注意事项&#xff1a; 三方库的安装有可能会引起系统错误…

JVM学习篇对象创建与内存分配机制深度剖析

1. 对象的创建 对象创建的主要流程: 1.1 类加载检查 虚拟机遇到一条new指令时&#xff0c;首先将去检查这个指令的参数是否能在常量池中定位到一个类的符号引用&#xff0c;并且检查这个符号引用代表的类是否已被加载、解析和初始化过。如果没有&#xff0c;那必须先执行相…

【Java 面试合集】内存中为什么要区分栈和堆

内存中为什么要区分栈和堆 1. 概述 嗨&#xff0c;大家好Java 面试合集又来了&#xff0c;今天我们分享的主题很大众化以及普通。无论是Java 语言本身还是别的语言都会有所涉及&#xff0c;但是今天我们从Java角度来讲下 2. 分析 今天我们会从多个方向来分享这个话题 2.1 栈是…

ETCD本地多成员集群部署

目录安装部署etcdctl 操作etcd使用http请求操作etcd本地多成员集群搭建python获取成员信息参考安装部署 按照官网文档&#xff0c;安装release版本 https://etcd.io/docs/v3.4/install/ [rootVM-33-162-centos /usr/local/bin]# etcd --version etcd Version: 3.4.16 Git SH…

点云配准方法原理(NDT、ICP)

配准是点云处理中的一个基础问题&#xff0c;众多学者此问题进行了广泛而深入的研究&#xff0c;也出现了一系列优秀成熟的算法&#xff0c;在三维建模、自动驾驶等领域发挥着重要的作用。 本文主要介绍粗配准NDT (Normal Distribution Transform) 与 精配准ICP (Iterative Cl…

最新最全中小微企业研究数据:海量创业公司信息与获取投资信息(1985-2021年)

一、企业获取投资名单&资方信息 数据来源&#xff1a;搜企网、企查查、天眼查 时间跨度&#xff1a;1985年8月-2021年9月 区域范围&#xff1a;全国范围 数据字段&#xff1a;企业名称、时间、获得投资金额以及投资方信息 部分数据&#xff1a; DateCompany_nameUnit…

摄影师没了?!生成式人工智能即将降维打击摄影行业

本文是Mixlab无界社区成员的投稿&#xff1a;滚石deepfacelab和deepfacelive项目组成员摄影师失业了&#xff1f;&#xff1f;怎么说&#xff1f;##你还以为AI绘画影响的只是插画师行业吗&#xff1f;错了&#xff0c;摄影行业也即将面临技术洗牌。话不多说&#xff0c;先看一下…

java并发编程原理2 (AQS, ReentrantLock,线程池)

一、AQS&#xff1a; 1.1 AQS是什么&#xff1f; AQS就是一个抽象队列同步器&#xff0c;abstract queued sychronizer&#xff0c;本质就是一个抽象类。 AQS中有一个核心属性state&#xff0c;其次还有一个双向链表以及一个单项链表。 首先state是基于volatile修饰&#x…

分享113个HTML艺术时尚模板,总有一款适合您

分享113个HTML艺术时尚模板&#xff0c;总有一款适合您 113个HTML艺术时尚模板下载链接&#xff1a;https://pan.baidu.com/s/1ReoPNIRjkYov-SjsPo0vhg?pwdjk4a 提取码&#xff1a;jk4a Python采集代码下载链接&#xff1a;采集代码.zip - 蓝奏云 女性化妆用品网页模板 粉…

【Linux】用户分类+权限管理+umask+粘滞位说明

目录 1.用户分类 su指令 2.认识Linux权限 2.1 文件访问者的分类 2.2 文件类型和访问权限 a. 文件类型 file指令 b. 访问权限 2.3 文件权值的表示方法 a. 字母表示法 b. 八进制表示法 3.如何修改文件访问者的权限及相关指令 1. chmod指令 2. chown指令 3. chgrp指…

Python语言零基础入门教程(二十七)

Python OS 文件/目录方法 Python语言零基础入门教程&#xff08;二十六&#xff09; 61、Python os.utime() 方法 概述 os.utime() 方法用于设置指定路径文件最后的修改和访问时间。 在Unix&#xff0c;Windows中有效。 语法 utime()方法语法格式如下&#xff1a; os.uti…