ES6---ES11 新特性

这是一篇笔记, 原视频:

【尚硅谷Web前端ES6教程,涵盖ES6-ES11】 https://www.bilibili.com/video/BV1uK411H7on/?share_source=copy_web\&vd_source=7336f6dae330ae9637a6a01932103639

let 声明

javascript 复制代码
let a; // 单个声明
let b, c, d; // 多个声明
let e = 10; // 单个声明并赋值
let f = 10,
  g = "hello",
  h = [1, 3, 5],
  i = true; // 多个声明并赋值

注意事项:

  • 变量不能重复声明
  • 有块级作用域(全局, 函数, eval)
  • 不存在变量提升
  • 不影响作用域链
javascript 复制代码
{
  let a = 10;
  function f() {
    console.log(a); // 10
  }
  f(); // 输出10
}

const 声明

javascript 复制代码
const PI = 3.1415926; // 常量声明

注意事项:

  • 一定要赋初始值
  • 一般声明使用大写
  • 常量的值不能修改
  • let 的注意事项

解构赋值

javascript 复制代码
// 数组
const arr = [1, 2, 3];
const [a, b, c] = arr; // 解构赋值
console.log(a, b, c); // 1 2 3
javascript 复制代码
// 对象
const Student = {
  name: "Tom",
  age: 20,
  gender: "male",
  Introduce: function () {
    console.log(
      `My name is ${this.name}, I am ${this.age} years old, I am ${this.gender}.`
    ); // 注意this
  },
};

let { name, age, gender, Introduce } = Student; //方法频繁调用, 可使用解构赋值, 属性名要对应相同
Introduce(); // My name is Tom, I am 20 years old, I am male.

let { name: myName, age: myAge, gender: myGender } = Student; // 别名
console.log(myName, myAge, myGender); // Tom 20 male

模版字符串(``)

javascript 复制代码
let str = `<ul>
            <li>item1</li>
            <li>item2</li>
           </ul>`; // 可以出现换行符

let name = "Tom";
let sayHello = `Hello, ${name}!`; // 使用模版字符串拼接字符串

对象的简化写法

javascript 复制代码
let name = "Tom";
let age = 20;
let obj = {
  name, // 等同于 name: name
  age,
  sayHello() {
    console.log(`Hello, ${this.name}!`);
  },
}; // 简化写法

箭头函数(=>)

javascript 复制代码
// 传统函数
let add = function (a, b) {
  return a + b;
};

// 箭头函数
let addNew = (a, b) => {
  return a + b;
};

// 箭头函数可以省略return关键字
let addNew2 = (a, b) => a + b; // 等同于 addNew, 只有一行代码时可以省略{}

注意事项:

  • this 是静态的, this 始终指向函数声明时所在作用域下的 this 的值, 也不能使用 call, apply, bind 方法改变 this 的指向
javascript 复制代码
// 示例
function getName() {
  console.log(this.name);
}

let getNameNew = () => {
  console.log(this.name);
};

window.name = "window";

let obj = {
  name: "Tom",
};

getName(); // window
getNameNew(); // window
getName.call(obj); // Tom
getNameNew.call(obj); // window, 因为在window作用域下声明
  • 不能作为构造函数实例化对象

  • 不能使用 arguments 变量(...args)

  • 简写规则:

    1. 省略小括号: 只有一个参数时可以省略小括号
    2. 省略 return 关键字和{}: 只有一行代码时可以省略 return 关键字
  • 箭头函数适合与 this 无关的回调, 如定时器, 数组的方法回调

  • 不适合与 this 有关的回调, 如事件监听(回调), 对象的方法

函数参数默认值

javascript 复制代码
function add(a, b = 10) {
  return a + b;
} // 具有默认值的参数, 最好放到最后

console.log(add(1)); // 11
javascript 复制代码
// 与解构赋值相结合
const connect = {
  host: "localhost",
  user: "root",
  password: "123456",
  port: 3306,
};

function connectDB({ host = "127.0.0.1", user, password, port }) {
  console.log(`Connect to ${host}:${port} with ${user}:${password}`);
}

rest 参数

javascript 复制代码
function add(...args) {
  // 数组, 增加了对数据的处理
  let sum = 0;
  for (let i = 0; i < args.length; i++) {
    sum += args[i];
  }
  return sum;
}

console.log(add(1, 2, 3, 4, 5)); // 15

注意事项: 只能作为最后一个参数

扩展运算符

... 运算符能将数组转换为逗号分隔的参数序列

javascript 复制代码
//1. 数组的合并
const odds = [1, 3, 5];
const evens = [2, 4, 6];
// const all = odds.concat(evens);  ES5
const all = [...odds,...evens]; // [1, 3, 5, 2, 4, 6]

//2. 数组的克隆(浅拷贝)
cosnt strArr = ["hello", "world"];
const newArr = [...strArr]; // ["hello", "world"]

// 3. 将伪数组转换为数组
const divs = document.querySelectorAll("div");
const arr = [...divs]; // [div, div, div]

Symbol 数据类型

引入的一种新的数据类型, 表示独一无二的值, 是一种类似于字符串的数据类型

特点:

  • 值是唯一的, 用来解决 命名 冲突的问题
  • 不能与其他数据进行 运算
  • 定义的对象属性不能 使用 for...in 循环遍历, 但可以使用 Reflect.ownKeys()方法获取对象的所有键名
javascript 复制代码
// 创建 Symbol
let s1 = Symbol(); // 自动生成唯一的 Symbol 值

let s2 = Symbol("name"); // 带有描述的 Symbol 值
let s3 = Symbol("name"); // 与 s2 不同的值

let s4 = Symbol.for("name"); // 全局 Symbol 值, 与其他 Symbol 值不同
let s5 = Symbol.for("name"); // 与 s4 相同的值
javascript 复制代码
// 需求: 向对象添加方法, 关于 up, down
let game = {
  name: "game",
  up() {
    console.log("up");
  },
  down() {
    console.log("down");
  },
};

let methods = {
  up: Symbol(),
  down: Symbol(),
};

game[methods.up] = function () {
  console.log("I am up");
};

game[methods.down] = function () {
  console.log("I am down");
};

// 调用方法
game[methods.up](); // I am up
game["up"](); // up
javascript 复制代码
// 创建对象时添加
let game = {
  name: "game",
  [Symbol("up")]() {
    console.log("I am up");
  },
};

迭代器

Iterator 是一种接口, 为各种不同的数据结构提供统一的访问机制

ES6 创造了一种新的遍历命令, for...of

原生具备 Iterator 接口的数据结构: Array, Map, Set, String, TypedArray, 函数的 arguments 对象, NodeList 对象

javascript 复制代码
const arr = [1, 2, 3];

for (let value of arr) {
  console.log(value);
}

工作原理:

  1. 创建一个指针对象, 指向当前 数据结构的起始位置
  2. 第一次调用对象的 next()方法, 指针自动指向数据结构的第一个成员
  3. 接下来不断调用 next 方法, 指针一直往后移动, 直到指向最后一个成员
  4. 每调用 next 方法返回一个包含 value 和 done 属性的对象

需要 自定义遍历数据的时候, 要想到迭代器

javascript 复制代码
// 使用 for...of 遍历数组, 并返回 姓名
const StuClass = {
  name: "一班",
  stus: ["Tom", "Jerry", "Lucy"],

  [Symbol.iterator]() {
    let index = 0;
    return {
      next: () => {
        if (index < this.stus.length) {
          const res = { value: this.stus[index], done: false };
          index++;
          return res;
        } else {
          return { value: undefined, done: true };
        }
      },
    };
  },
};

for (let name of StuClass) {
  console.log(name); // Tom Jerry Lucy
}

自定义迭代器的步骤:

  1. 定义Symbol.iterator方法, 返回一个包含 next()方法的对象
  2. 在 next()方法中, 定义遍历规则, 返回一个包含 value 和 done 属性的对象
  3. 其中遍历未完成时, done 属性值为 false, 遍历完成时, done 属性值为 true, 且 value 属性值为 undefined

生成器函数

是一个特殊的函数

  • 声明时使用 function* 关键字
  • 内部可使用 yield 关键字, 将函数暂停, 使用 next()方法进行到下一模块
javascript 复制代码
function* gen() {
  console.log("start");
  yield 1;
  console.log("continue");
  yield 2;
  console.log("end");
}

let g = gen();
g.next(); // start
g.next(); // continue
g.next(); // end

for (let value of gen()) {
  console.log(value);
} // 1 2  , 使用for...of遍历时, 输出 yield 后面的语句

参数:

  • 可以接收外部的参数, next()方法也可以传递参数
  • 第二个 next()方法的参数为 第一个 yield 的"返回值"
javascript 复制代码
function* gen(x) {
  console.log(x);
  let one = yield "first";
  console.log(one);
}

let g = gen(10);
g.next(); // 10
g.next("hello"); // hello
javascript 复制代码
// 需求 1s后控制台输出 111, 2s后输出 222, 3s后输出 333
function one() {
  setTimeout(() => {
    console.log("111");
    iterator.next();
  }, 1000);
}

function two() {
  setTimeout(() => {
    console.log("222");
    iterator.next();
  }, 2000);
}

function three() {
  setTimeout(() => {
    console.log("333");
  }, 3000);
}

function* gen() {
  yield one();
  yield two();
  yield three();
}

let iterator = gen();

iterator.next();
javascript 复制代码
// 模拟获取 用户数据 订单数据 商品数据, 按照顺序每一秒后获得, 并在生成器函数中打印

function getUsers() {
  setTimeout(() => {
    let data = "用户数据";
    iterator.next(data);
  }, 1000);
}

function getOrders() {
  setTimeout(() => {
    let data = "订单数据";
    iterator.next(data);
  }, 1000);
}

function getGoods() {
  setTimeout(() => {
    let data = "商品数据";
    iterator.next(data);
  }, 1000);
}

function* gen() {
  let users = yield getUsers();
  console.log(users);
  let orders = yield getOrders();
  console.log(orders);
  let goods = yield getGoods();
  console.log(goods);
}

let iterator = gen();
iterator.next();

集合(set)

javascript 复制代码
let set0 = new Set();

let set = new Set([1, 2, 3, 2, 1]); // 去重

// 元素个数
console.log(set.size);
// 添加元素
set.add(4);
// 删除元素
set.delete(2);
// 判断元素是否存在
set.has(2); // false
// 清空集合
set.clear();
// 遍历集合
for (let value of set) {
} // 实现了 Iterator 接口
javascript 复制代码
// 集合的应用

const arr = [1, 2, 3, 2, 1];
// 数组去重
const uniqueArr = [...new Set(arr)];
console.log(uniqueArr); // [1, 2, 3]

// 数组求交集
let arr2 = [2, 3, 4];
let arr3 = [3, 4, 5];

let res = [...new Set(arr2)].filter((value) => arr3.includes(value));
console.log(res); // [3, 4]

// 数组求并集
let unionArr = [...new Set([...arr2, ...arr3])];

// 数组求差集
let diffArr = [...new Set(arr2)].filter((value) => !arr3.includes(value)); //交集取反

映射(map)

javascript 复制代码
let map = new Map();

map.set("name", "Tom"); // 添加键值对, key 可以是任意类型
map.get("name"); // 获取值
map.has("name"); // 判断是否存在
map.delete("name"); // 删除键值对
map.clear(); // 清空

使用 for...of 遍历时返回多个数组, 每个数组内第一个元素是键, 第二个元素是值

类(class)

javascript 复制代码
class Person {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  } // 构造函数

  sayHello() {
    console.log(`Hello, my name is ${this.name}, I am ${this.age} years old.`);
  } // 实例方法
}
javascript 复制代码
class Phone {
  static name = "手机"; // 静态属性
  static getName() {
    // 静态方法
    return this.name;
  }
}
// 只有类可以调用 静态方法和静态成员变量, 实例化对象不能调用
javascript 复制代码
class Student extends Person {
  constructor(name, age, grade) {
    super(name, age);
    this.grade = grade;
  }
}
// 也可以对父类方法进行重写, 但重写方法内部不能调用父类的同名方法
// 也有get 和 set方法, 用于对属性的读写控制

数值扩展

javascript 复制代码
// Number.EPSILON: 2.220446049250313e-16 是 JavaScript 能表示的最小的正浮点数

function isEqual(a, b) {
  if(Math.abs(a - b) < Number.EPSILON){
    return true;
  }else{
    return false;
  }
}   // 判断两个浮点数是否相等

let b = 0b101;  // 二进制
let o = 0o77;   // 八进制
let h = 0x1F;  // 十六进制

// Number.isFinite() 判断是否为有限数
console.log(Number.isFinite(10)); // true
console.log(Number.isFinite(Infinity)); // false

// Number.isNaN() 判断是否为 NaN
console.log(Number.isNaN(NaN)); // true

// Number.ParseInt() 解析整数  Number.parseFloat() 解析浮点数
console.log(Number.parseInt("123沙发上")); // 123
console.log(Number.parseFloat("123.456沙发上")); // 123.456

// Number.isInteger() 判断是否为整数
console.log(Number.isInteger(10)); // true
console.log(Number.isInteger(10.1)); // false

// Math.trunc() 截取整数部分
console.log(Math.trunc(10.1)); // 10

// Math.sign() 符号函数, 返回数值的正负号
console.log(Math.sign(-5)); // -1
console.log(Math.sign(0)); // 0
console.log(Math.sign(5)); // 1

对象方法的扩展

javascript 复制代码
// Object.is() 判断两个值是否相同
console.log(Object.is(0, -0)); // false
console.log(Object.is(NaN, NaN)); // true  注: NaN !== NaN

// Object.assign() 用于对象的合并
// 第一个参数是目标对象, 后面的参数是源对象
let obj1 = { a: 1, b: 2 };
let obj2 = { b: 3, c: 4 };
let obj3 = Object.assign(obj1, obj2);
console.log(obj3); // {a: 1, b: 3, c: 4}

// Object.setPrototypeOf() 设置原型对象

模块化

优势: 代码复用, 命名空间隔离, 提高代码可读性、可维护性

javascript 复制代码
// 分别暴露
export let school = "北京大学";
export function saySchool() {
  console.log("这是北京大学");
}
// 统一暴露
//   export {
//     school,
//     saySchool,
//   }
// }

// export default {
//   school: "清华大学",
//   saySchool() {
//     console.log("这是清华大学");
//   }
// }
html 复制代码
<script type="module">
  import * as schoolModule from "./school.js";
  console.log(schoolModule.school); // 北京大学
  console.log(schoolModule.default.school)  // 清华大学
</script>
html 复制代码
<script type="module">

import {school, saySchool} from "./school.js";
import {school as school2, saySchool as saySchool2} from "./school2.js";
// 重名时, 可以使用别名, 加 as 关键字, 引入default时, 必须使用 as 关键字

  </script>

一般在入口文件 app.js 中引入其他模块

html 复制代码
<script type="module" src="./app.js">
</script>

使用工具来提升不同浏览器的兼容性

  1. 安装工具 babel-cli babel-preset-env browserify(webpack)
  2. babel 转换(文件夹也行)
    npx babel app.js -o app-compiled.js --presets=babel-preset-env
  3. browserify 打包
    npx browserify app.js -o bundle.js

ES7新特性

javascript 复制代码
// Array.prototype.includes
// 用来判断数组是否包含某个元素,返回布尔值
let arr = [1, 2, 3, 4, 5];
console.log(arr.includes(3)); // true
console.log(arr.includes(6)); // false

// ** 运算符 相当于Math.pow()

async/await

async 函数

  1. 返回值为 promise 对象
  2. promise 对象的结果由 async 函数的返回值决定
javascript 复制代码
async function myFunc() {
    return 1;   // 函数内部的return只要不是promise对象,返回的promise对象就直接resolve(成功)

    throw new Error('出错了'); // 若函数内部抛出错误, 则reject(失败), 返回一个失败的promise对象

    return new Promise((resolve, reject) => {
        resolve(1); 
    }); // 若函数内部返回一个promise对象, 则resolve(成功), 返回一个成功的promise对象, 成功的值就是函数内部resolve的参数的值, 1
}

console.log(myFunc()); // Promise {<resolved>: 1}

await 关键字

  1. await 关键字只能在 async 函数中使用
  2. await 右侧的表达式一般为 promise 对象
  3. await 返回 的是 promise 成功的值
  4. 如果 promise 失败, 则会抛出异常, 需要通过 try...catch 捕获异常

对象方法的扩展

javascript 复制代码
const school = {
    name: 'xxx',
    cities: ['北京', '上海', '广州'],
    classes: ['语文', '数学', '英语'],
}

// 获取对象所有的键
console.log(Object.keys(school)); // ['name', 'cities', 'classes']
// 获取对象所有的值
console.log(Object.values(school)); // ['xxx', ['北京', '上海', '广州'], ['语文', '数学', '英语']]
// 获取对象所有键值对
console.log(Object.entries(school)); // [['name', 'xxx'], ['cities', ['北京', '上海', '广州']], ['classes', ['语文', '数学', '英语']]]
const m = new Map(Object.entries(school)); // 转换为 Map 对象

console.log(Object.getOwnPropertyDescriptors(school)); //获取对象 属性的描述

对象展开

javascript 复制代码
// function connect({host, port, username, password}){
//     console.log(host);
//     console.log(port);
//     console.log(username);
//     console.log(password);
// }

function connect({ host, port, ...user }) {
  console.log(host);
  console.log(port);
  console.log(user);
}

connect({
  host: "localhost",
  port: 8080,
  username: "admin",
  password: "123456",
});
javascript 复制代码
const one = {
    a: 1
}

cosnt two = {
    b: 2
}

const three = {
    c: 3
}
const merged = {...one, ...two, ...three}
console.log(merged); //{a: 1, b: 2, c: 3}

正则: 命名捕获分组

javascript 复制代码
let str = "<a href='http://www.baidu.com'>百度</a>"

const reg = /<a href="(?<url>.*?)">(?<text>.*?)</a>/;

const res = reg.exec(str);

console.log(res.groups.url); // http://www.baidu.com
console.log(res.groups.text); // 百度

正则: 反向断言

javascript 复制代码
// 提取555

let str = "JS12319534可可来说555啦啦啦";

// 正向断言
const reg = /\d+(?=啦)/;
const res = reg.exec(str);

// 反向断言
const reg2 = /(?<=说)\d)/;
const res2 = reg2.exec(str);

Object.fromEntries()

将一个二维数组或者 Map 转换成一个对象。

javascript 复制代码
const entries = [
  ["name", "John"],
  ["age", 30],
  ["city", "New York"],
];

const obj = Object.fromEntries(entries);

console.log(obj); // { name: 'John', age: 30, city: 'New York' }

// Object.entries(), can be used to convert an object to an array of key-value pairs.

trimStart() and trimEnd()

前者负责清除字符串左侧的空白, 后置负责清除尾部的空白

flat 与 flatMap

  • flat() 方法会按照一个可迭代对象的结构展平一个数组,即将嵌套的数组展平为一个一维数组。
  1. 参数默认是1, 表示只展平一层, 参数为展开的深度
  • flatMap() 方法与 flat() 方法类似,但它会将数组中的每一个元素都先进行映射操作,然后再展平。

私有属性

javascript 复制代码
class Person {
  #name;
  #age;
  constructor(name, age) {
    this.#name = name;
    this.#age = age;
  }
  get name() {
    return this.#name;
  }
  get age() {
    return this.#age;
  }

  set age(newAge) {
    if (newAge > 0 && newAge < 150) {
      this.#age = newAge;
    } else {
      console.log("年龄不合法");
    }
  }

  set name(newName) {
    this.#name = newName;
  }
}
// 类似于java中的private关键字,在属性名前面加上#符号,则该属性只能在类的内部访问。

promise.allSettled([promise, promise, promise])

这个方法返回一个 promise,当所有 promises 都被 resolve 或 reject 时,返回一个数组,每个 promise 的状态都标记为 fulfilled 或 rejected。

promise.all 是必须每个结果都成功, 才能往下走

String.prototype.matchAll()

用于正则匹配中字符串的批量提取

可选链操作符(?.)

javascript 复制代码
function main(config){
  const dbHonst = config?.db?.host; // 等同于 config && config.db && config.db.host
  console.log(dbHonst);
}

main({
  db: {
    host: "192.168.1.1",
    user: "root"
  }
  cache: {
    host: "192.168.1.2",
    user: "root"
  }
})

动态import

javascript 复制代码
function main(){
  import("./module.js").then(module => {}) // 动态导入模块, 返回结果为promise对象
}

BigInt

javascript 复制代码
let bigInt = 12345678901234567890n;

BigInt(124);  // 124n

globalThis

javascript 复制代码
// 始终指向全局对象, 无论在浏览器还是在node环境下