ES6-ES11入门教程

302 阅读27分钟

1. JS 2015(ES6)

1.2 Let关键字

用来声明变量

特点

  • 不允许重复声明
  • 块儿级作用域
  • 不存在变量提升
  • 不影响作用域链

案例

 // 变量不能重复声明
 let star = '罗志祥';
 let star = '小猪'; //报错,变量不能重复声明
 ​
 // 块儿级作用域 
 {
     let girl = '周扬青';
 }
 console.log(girl); // 报错
 ​
 // 不存在变量提升
 console.log(song); //报错,如果是var song="",才能挑用
 let song = '恋爱达人'; 
 ​
 // 不影响作用域链 
 {
     let school = '555';
     function fn () {
         console.log(school);
     }
     fn();
 }

补充作用域知识

  1. 函数作用域 function{ },块级作用域 {}。 一定要有 function 关键字,才会有函数作用域。

  2. js里面 var声明的变量只有函数作用域,没有块级作用域。(也就是说,函数可以隔离变量, *在{}、if、else、while、for里面 *不能隔离变量)。

     // 有两个for循环:
     for (var i = 0; i < 3; i++) {} 
     for (var i = 0; i < 4; i++) {} 
     console.log(i); //在for循环外面可以打印i的值
    
  3. 由于两个并列的for只是两个并列的代码块,并不是两个并列的函数,因此它们里面声明的 i 同为一个全局变量。(第二个for里面的 i 相当于将已经存在的 i 重新声明并赋值而已)

  4. 因此,可在全局(外部)通过console.log看到 i 的值。

1.2 const关键字

用于定义常量,值不能修改,就算重新赋值不会报错,值也不会改变

特点

  • 声明必须赋初始值
  • 标识符一般为大写
  • 不允许重复声明
  • 值不允许修改
  • 块儿级作用域

案例

 // 一般变量使用大写(潜规则)
 // 一定要赋初始值,不赋值报错
 // 常量的值不能被修改
 const SCHOOL = "西南石油大学";
 ​
 // 对于数组和对象元素的修改,不算对常量的修改,不会报错
 const LIKE = ["HG", "MC", "LZZ"];
 LIKE.push("ZZC");
 console.log(LIKE); // ['HG', 'MC', 'LZZ', 'ZZC']

1.3 变量的解构赋值

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

 // 数组结构赋值
 const F4 = ["小沈阳", "赵四", "刘能", "宋小宝"];
 let [xiao, zhao, liu, song] = F4;
 console.log(xiao); // 小沈阳
 console.log(zhao); // 赵四
 console.log(liu); // 刘能
 console.log(song); // 宋小宝
 ​
 // 对象结构赋值
 const zhao = {
   name: "赵本山",
   age: "65",
   xiaoping: function () {
     console.log("我会演小品");
   },
 };
 let { name, age, xiaoping } = zhao;
 console.log(name); // 赵本山
 console.log(age); // 65
 console.log(xiaoping); // 输出ƒ(){}
 xiaoping(); // 我会演小品
 ​
 // 如果不用结构的形式
 console.log(zhao.name);
 console.log(zhao.age);
 zhao.xiaoping();
 zhao.xiaoping();

1.4 模板字符串

用于字符串声明,以及简化变量拼接

 // 内容可以直接出现换行符
 let str = 
   `<ul>
     <li>张三1</li>
     <li>李四2</li>
     <li>王五3</li>
   </ul>`;
 ​
 // 变量拼接
 let str = "许巍"; 
 let lovest = `我喜欢${str}的歌,名字叫做<<蓝莲花>>。`;

1.5 对象的简化写法

ES6 允许在大括号里面,直接写入变量和函数,作为对象的属性和方法。

 // ES6 允许在大括号里面, 直接写入变量和函数, 作为对象的属性和方法
 let name = '小康';
 let change = function () {
   console.log('我们可以改变你!');
 }
 ​
 const school = {
   name,
   // 老方式
   change,
   // 新方式直接写入变量和函数
   improve() {
     console.log('我们可以提高你的技能');
   }
 }
 ​
 console.log(school);

1.6 箭头函数

使用「箭头」(=>)简化定义函数

特点

  • 如果形参只有一个,则小括号可以省略
  • 函数体如果只有一条语句,则花括号可以省略,函数的返回值为该条语句的执行结果
  • 箭头函数中 this 是静态的. this 始终指向函数声明时所在作用域下的this 的值
  • 箭头函数不能作为构造函 数实例化
  • 不能使用 arguments

案例

 ​
 function getName() {
   console.log(this.name);
 }
 // 声明函数
 let getName2 = () => {
   console.log(this.name);
 };
 ​
 window.name = "红石榴21";
 const school = {
   name: "博商学院",
 };
 ​
 getName(); // 红石榴21
 getName2(); // 红石榴21
 ​
 getName.call(school); // 博商学院
 // this 是静态的,this 始终指向函数声明时所在作用域下的 this 的值
 getName2.call(school); // 红石榴21
 ​
 // 不能作为构造函数实例化对象
 let Person = (name, age) => {
   this.name = name;
   this.age = age;
 };
 let me = new Person("hong", 18);
 console.log(me); // 报错:Person is not a constructor
 ​
 // 不能使用 arguments 变量
 let fn = () => {
   console.log(arguments);
 };
 fn(1, 2, 3); // 报错:arguments is not defined
 ​
 // 箭头函数的简写
 // 1) 省略小括号,当形参有且只有一个的时候
 let add = n => {
   return n + n;
 };
 console.log(add(9)); // 18
 // 2) 省略花括号,当代码体只有一条语句的时候,此时 return 必须省略
 // 而且语句的执行结果就是函数的返回值
 let pow = (n) => n * n;
 console.log(pow(9)); // 81

应用场景

 // 需求-1  点击 div 2s 后颜色变成『粉色』
 // 获取元素
 let add = document.getElementById("add");
 //绑定事件
 add.addEventListener("click", function () {
   // 1、setTimeout未使用箭头函数
   //保存 this 的值
   let _this = this;
   let that = this;
   let self = this;
   setTimeout(function () {
     _this.style.background = "pink";
   }, 2000);
   // 2、setTimeout使用箭头函数, this 是静态的
   setTimeout(() => {
     this.style.background = "pink";
   }, 2000);
 });
 ​
 ​
 // 需求-2  从数组中返回偶数的元素
 const arr = [1, 2, 4, 6, 10, 13];
 const result1 = arr.filter(function (item) {
   if (item % 2 === 0) {
     return true;
   } else {
     return false;
   }
 });
 // const result = arr.filter((item) => {
 //   if (item % 2 === 0) {
 //     return true;
 //   } else {
 //     return false;
 //   }
 // });
 // 简写形式
 const result2 = arr.filter((item) => item % 2 === 0);
 console.log(result2);
 // 箭头函数适合与 this 无关的回调,定时器,数组的方法回调
 // 箭头函数不适合与 this 有关的回调,事件回调,对象的方法

17. 函数参数的默认值设置

给函数的参数设置默认值

 // 形参初始值 具有默认值的参数,一般位置要靠后(潜规则)
 // 设置默认值
 function add(a, b, c = 4) {
   return a + b + c;
 }
 ​
 // 与结构赋值结合
 function connect({ host, username, password, port }) {
   console.log(host);
   console.log(username);
   console.log(password);
   console.log(port);
 }
 connect({
   host: "127.0.0.1",
   username: "admin",
   password: "admin",
   port: 80,
 });

1.8 rest参数

用于获取函数的实参,用来代替 arguments

 // rest 参数必须要放到参数最后
 function fn(a, b, ...args) {
   console.log(a); // 1
   console.log(b); // 2
   console.log(args); // [3, 4, 5, 6] ,变成数组就可以使用 filter、some、every、map
 }
 fn(1, 2, 3, 4, 5, 6);

1.9 扩展运算符的介绍

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

 // 声明一个数组
 const tfbody = ["朴树", "许巍", "刀郎"];
 console.log(...tfbody);// 朴树,许巍,刀郎
 ​
 // 数组的合并,将两个输出合并成一个
 const a = ["1", "2"];
 const b = ["3", "4"];
 // const v = a.concat(b); // 使用concat实现数组拼接
 // console.log(v); // ["1", "2", "3", "4"]
 const c = [...a, ...b];
 console.log(c); //  ["1", "2", "3", "4"]
 ​
 // 数组的克隆
 const a1 = ["A", "B", "C"];
 const a2 = [...a1];
 console.log(a1); // ["A", "B", "C"]
 console.log(a2); // ["A", "B", "C"]
 ​
 // 将伪数组转为真正的数组
 const divS = document.querySelectorAll("div");
 const divArr = [...divS];
 console.log(divArr);

1.10 Symbol

一种新的原始数据类型 Symbol,表示独一无二的值。它是 JavaScript 语言的第七种数据类型,是一种类似于字符串的数据类型。

特点

  • Symbol 的值是唯一的,用来解决命名冲突的问题
  • Symbol 值不能与其他数据进行运算
  • Symbol 定义 的 对象属性 不能 使 用 for…in 循 环遍 历 ,但 是可 以 使 用 Reflect.ownKeys 来获取对象的所有键名

案例

 // 创建Symbol
 let s = Symbol();
 console.log(s, typeof s); // Symbol() 'symbol'
 let s2 = Symbol("嘀嘀咕咕");
 let s3 = Symbol("嘀嘀咕咕");
 console.log(s3.description);// 嘀嘀咕咕 description取值
 console.log(s2, typeof s2); // Symbol(嘀嘀咕咕) 'symbol'
 console.log(s2 === s3); // false
 // Symbol.for 创建
 let s4 = Symbol.for("我的心思");
 let s5 = Symbol.for("我的心思");
 console.log(s4); // Symbol(我的心思)
 console.log(s4 === s5); // true
 ​
 // 不能与其他数据进行运算
 let result = s + 100; // Cannot convert a Symbol value to a number
 let result = s > 100; // Cannot convert a Symbol value to a number
 let result = s + s; // Cannot convert a Symbol value to a number
 ​
 // 对象添加Symbol类型的属性
 let phws = {
   name: "排核污水",
   [Symbol("say")]: function () {
     console.log("打断小日子的狗腿");
   },
   [Symbol("woof")]: function () {
     console.log("小日子公开食海鲜,真香");
   },
 };
 console.log(phws);
 ​

内置属性

 // 当其他对象使用 instanceof 运算符,判断是否为该对象的实例时,会调用这个方法
 class Person {
   /* static Symbol.hasInstance {
     console.log(param);
     console.log("我被用来检测类型了");
     return true;
   } */
 ​
   static [Symbol.hasInstance](param) {
     console.log(param);
     console.log("我被用来检测类型了");
     return true;
   }
 }
 console.log({} instanceof Person);
 ​
 // SymbolisConcatSpreadable 属性等于的是一个布尔值,表示该对象用于Array.prototype.concat()时,是否可以展开
 const arr = [1, 2, 3];
 const arr2 = [4, 5, 6];
 arr2[Symbol.isConcatSpreadable] = false;
 console.log(arr.concat(arr2));

image-20230926141730181

其他内置属性

属性名称描述
Symbol.hasInstance当其他对象使用instanceof运算符,判断是否为该对象的实例时,会调用这个方法。
Symbol.isConcatSpreadable这个内置的Symbol用于配置对象作为Array.prototype.concat()方法的参数时是否展开其数组元素。
Symbol.species这个Symbol值用于访问构造函数中用于创建派生对象的函数。
Symbol.match当一个字符串需要匹配正则表达式时,会调用这个方法。
Symbol.replace当一个字符串需要替换匹配的字符时,会调用这个方法。
Symbol.search当一个字符串需要查找匹配的字符时,会调用这个方法。
Symbol.split当一个字符串需要分割为数组时,会调用这个方法。
Symbol.iterator这个Symbol值用于访问对象的默认迭代器。
Symbol.toPrimitive这个Symbol值用于将一个对象转换为原始类型的表示。
Symbol.toStringTag这个Symbol值用于访问对象的默认字符串表示。
Symbol.unscopables这个Symbol值包含一个对象的可作用域属性名集合。

1.11 迭代器

迭代器(Iterator)用于数据遍历。

它是一种接口,任何数据结构只要部署 Iterator 接口(就是对象里面的一个属性方法Symbol.Iterator),就可以实现遍历操作。

通过for...of 循环遍历命令进行迭代器的消费,原生具备 iterator 接口的数据如下:

  • Array
  • Arguments
  • Set
  • Map
  • String
  • TypedArray
  • NodeList
 // 声明一个数组
 const tangShengShiTu = ["唐僧", "孙悟空", "猪八戒", "沙和尚"];
 ​
 // 使用 for...in 遍历的是key
 for (let v in tangShengShiTu) {
     console.log(v); // 0,1,2,3
 }
 ​
 // 使用 for...of 遍历数组value
 for (let v of tangShengShiTu) {
     console.log(v); // "唐僧", "孙悟空", "猪八戒", "沙和尚"
 }

工作原理

  1. 创建一个指针对象,指向当前数据结构的起始位置
  2. 第一次调用对象的 next 方法,指针自动指向数据结构的第一个成员
  3. 接下来不断调用 next 方法,指针一直往后移动,直到指向最后一个成员
  4. 每调用 next 方法返回一个包含 value 和done 属性的对象,done = false 后面还有数据,true后面没有数据
 // 声明一个数组
 const a = ["1111", "2222", "3333", "4444"];
 ​
 // 创建一个指针对象,指向当前数据结构的起始位置
 let iterator =a[Symbol.iterator]();
 ​
 // 第一次调用对象的 next 方法,指针自动指向数据结构的第一个成员
 console.log(iterator.next()); // {value: '1111', done: false}
 ​
 //接下来不断调用 next 方法,指针一直往后移动,直到指向最后一个成员
 console.log(iterator.next()); // {value: '2222', done: false}
 console.log(iterator.next()); // {value: '3333', done: false}
 console.log(iterator.next()); // {value: '4444', done: false}
 console.log(iterator.next()); // {value: '', done: true}

对象实现自定义迭代器

 ​
 const fruits = {
     name: "水果店",
     stuList: ["A", "B", "C", "D"],
     // 重写Symbol.iterator
     [Symbol.iterator]() {
         let index = 0;
 ​
         // 写next函数
         let next = () => {
             if (index < this.stuList.length) {
                 const result = { value: this.stuList[index], done: false };
                 // 下标自增
                 index++;
                 // 返回结果
                 return result;
             } else {
                 return { value: this.stuList[index], done: true };
             }
         };
         return { next };
     },
 };
 ​
 // 遍历fruits对象,就可以调用自定义的迭代器
 for (let v of fruits) {
     console.log(v); // "A", "B", "C", "D" 
 }

1.12 Generator函数

生成器函数是一种异步编程解决方案,语法行为与传统函数完全不同

定义生成器函数

  1. 需要再方法上面加*(function * 方法名(){})
  2. 函数内执行语句必须与yield搭配使用

yield作用:

(1)函数代码的分隔符,断开,回找,上下步有顺序

(2)接收下一个next传回来的参数

基本使用

 // 定义生成器函数
 function * gen() {
     console.log(111);
     yield "光辉岁月";// 函数代码的分隔符,yield 定义next的返回值
 ​
     console.log(222);
     yield "蓝莲花";
 ​
     console.log(333);
     yield "像风一样自由";
     
     console.log(444);
 }
 gen();// 生成器函数普通调用不执行
 let iterator = gen();
 console.log(iterator.next()); // 111 {value: '光辉岁月', done: false}
 console.log(iterator.next()); // 222 {value: '蓝莲花', done: false}
 console.log(iterator.next()); // 333 {value: '像风一样自由', done: false}
 console.log(iterator.next()); // 444 {value: 'undefined', done: true}
 ​
 // next时done=truefor就没有值了,因为已经循环到末尾了
 for(let o of iterator){
      console.log("yield=",o);// 返回yield定义的值,并执行yield上方的代码
 }

生成器函数参数

 function * gen (arg) {
     console.log(arg); 
     let one = yield 111;// 将第一次next的返回值传递给下一次
 ​
     console.log(one); // 使用上一次的返回值变量
     let two = yield 222;
 ​
     console.log(two);
     let three = yield 333;
     
     console.log(three);
 ​
   }
 ​
   // 执行获取迭代器对象
   let iterator = gen('AAA');
   console.log(iterator.next()); // AAA
 ​
   // next 方法可以传入实参
   console.log(iterator.next('BBB'));
   console.log(iterator.next('CCC'));
   console.log(iterator.next('DDD'));

案例

// 异步编程 文件操作 网络操作 (ajax, request) 数据库操作
// 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);
        iterator.next();
    }, 3000);
}

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

// 调用生成器函数
let iterator = gen();
iterator.next();

1.13 Promise 对象(异步)

是异步编程的一种解决方案,Promise 是一个对象,从它可以获取异步操作的消息。

状态的特点

Promise 异步操作有三种状态:

  • pending(进行中)、fulfilled(已成功)和 rejected(已失败)。
  • 除了异步操作的结果,任何其他操作都无法改变这个状态。

Promise 对象只有:从 pending 变为 fulfilled 和从 pending 变为 rejected 的状态改变。

状态的缺点

  • 无法取消 Promise ,一旦新建它就会立即执行,无法中途取消。
  • 如果不设置回调函数,Promise 内部抛出的错误,不会反应到外部。
  • 当处于 pending 状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。

基本使用


// 实例化 Promise 对象
// 传入的方法有两个参数,参数1成功需要调用的方法。参数2失败需要调用的方法。调用一个后才会执行then方法
const p = new Promise(function (resolve, reject) {
    setTimeout(() => {
        let data = "成功";
        // 成功调用resolve方法
        resolve(data);

        let err = "失败";
        // 失败调用reject方法
        reject(err);
    }, 1000);
});

// 调用 promise 对象的 then 方法
// then 方法接收两个函数作为参数,参数1:是 Promise 执行成功时的回调,参数2是 Promise 执行失败时的回调,两个函数只会有一个被调用。
p.then(
    function (value) {
        console.log(value); // 成功
    },
    function (error) {
        console.error(error); // 失败
    }
);

// 可重复添加回调函数
const c = new Promise(function (resolve, reject) {
    resolve(1);
}).then(function (value) { // 第一个then // 1
    console.log(value);
    return value * 2;
}).then(function (value) { // 第二个then // 2
    console.log(value);
}).then(function (value) { // 第三个then // undefined
    console.log(value);
    return Promise.resolve('resolve');
}).then(function (value) { // 第四个then // resolve
    console.log(value);
    return Promise.reject('reject');
}).then(function (value) { // 第五个then //reject:reject
    console.log('resolve:' + value);
}, function (err) {
    console.log('reject:' + err);
});

Promise对象catch方法

const a = new Promise((resolve, reject) => {
    setTimeout(() => {
        // 设置 P 对象的状态为失败,并设置失败的值
        reject("出错啦!");
    }, 1000);
});
// p.then(value=> {
//   console.log(value);
// }, reason=>{
//   console.log(reason);
// });
a.catch((reason) => {
    console.warn(reason); // 出错啦!
});

1.14 Map 与 Set

1.14.1 Map集合

键值对的存储方式,现了iterator 接口,所以可以使用[扩展运算符]和[for...of...] 进行遍历。

属性和方法

属性/方法描述
map.size返回map成员数
map.get(key)返回对象的value
map.set(key,value)在第一部分初始化map时已经详细讲过了。添加一对值到map里。如果key已经存在,会覆盖以前值。
map.has(key)返回一个布尔值,表明该key是否存在于map中
map.delete(key)删除map中的一个成员(返回一个布尔值,表明是否删除成功)
map.clear()清除map中所有成员
map.keys()返回键的遍历器
map.values()返回值的遍历器
map.entries()返回所有成员的遍历器
map.forEach(function(value, key) {...})遍历Map的所有成员

示例

// 声明 Map
let m = new Map();

// 添加元素
m.set("name", "成都大运会");
// console.log(m); // Map(1) {'name' => '成都大运会'}

// 也可以将方法存进去
m.set("change", function () {
    console.log("我可以改变世界!!");
});
let key = {
    school: "西南石油大学",
};
m.set(key, ["北京", "上海", "广州", "深圳"]);

// 遍历
for (let v of m) {
    console.log(v);
}

1.14.2 Set集合

Set(集合)类似于数组,数据无法存入相同值,自带去重,实现了 iterator 接口,可以使用[扩展运算符]和[for...of...] 进行遍历

属性和方法

属性/方法描述
set.size返回set成员数
set.add(value)添加一个成员到set中
set.delete(value)删除set中的一个成员,返回一个布尔值,表明是否删除成功
set.has(value)返回一个布尔值,表明该value是否存在于set中
set.clear()清除set中所有成员
set.forEach(function(value, index, set) {...})遍历Set的所有成员

示例

// 声明一个 set
let s = new Set();
console.log(s); // Set(0) {size: 0}
let s2 = new Set(["昨天", "今天", "明天", "未来", "今天"]);
console.log(s2); // Set(4) {'昨天', '今天', '明天', '未来'}
// 元素的个数
console.log(s2.size);
// 添加新的元素
s2.add("过去");
console.log(s2); // Set(5) {'昨天', '今天', '明天', '未来', '过去'}
// 删除元素
s2.delete("昨天");
console.log(s2); // Set(4) {'今天', '明天', '未来', '过去'}
// 清空
s2.clear();
console.log(s2); // Set(0) {size: 0}

// 数组去重
let arr = [1, 2, 3, 4, 5, 4, 3, 2, 1];
let result = [...new Set(arr)];
console.log(result); // [1, 2, 3, 4, 5]

// 交集
// 集合论中,设A,B是两个集合,由所有属于集合A且属于集合B的元素所组成的集合,叫做集合A与集合B的交集(intersection)
let a = [1, 2, 3, 4, 5];
let b = [4, 5, 6, 7, 8];
let c = [...new Set(a)].filter(item=>new Set(b).has(item));
console.log(c); // [4, 5]

// 并集
let j1 = [4, 5, 6, 5, 6];
let j2 = [1, 2, 3, 4, 5, 4, 3, 2, 1];
let union = [...new Set([...j1, ...j2])];
console.log(union); // [1, 2, 3, 4, 5, 6]

// 差集
let c1 = [1, 2, 3, 4, 5, 4, 3, 2, 1];
let c2 = [4, 5, 6, 5, 6];
let diff = [...new Set(c1)].filter(item=>!(new Set(c2).has(item)));
console.log(diff); // [1, 2, 3]

1.15 class类

class (类)作为对象的模板被引入,可以通过 class 关键字定义类。class 的本质是 function。

特性

  • class声明类
  • constructor 定义构造函数初始化
  • extends 继承父类
  • super 调用父级构造方法
  • static 定义静态方法和属性
  • 父类方法可以重写

基本使用

// ES5
function Huawei(bank, price) {
    this.bank = bank;
    this.price = price;
}
// 在原型上添加方法
Huawei.prototype.call = function () {
    console.log(this.price + "我可以打电话哦!");
};
let hW = new Huawei("3999", "华为荣耀");
hW.call();
console.log(hW);


// ES6
class XiaoMi {
    // 静态属性
    static name = '手机';
    static change() {
        console.log('我可以改变世界');
    }

    // 构造函数
    constructor(bank, price) {
        this.bank = bank;
        this.price = price;
    }
    // 方法必须使用该语法,不能使用 ES5 的对象完整形式
    call() {
        console.log(this.price + "我为发烧而生");
    }
}
let xM = new XiaoMi("699", "红米");
xM.call();

console.log(xM.name); // undefined,静态变量用实例化对象无法获取
console.log(XiaoMi.name); // 手机

类继承

通过extends的关键字进行父类继承,super关键字可以调用父类方法。

class Phone {
    // 构造方法
    constructor(brand, price) {
        this.brand = brand;
        this.price = price;
    }
    call() {
        console.log("我可以打电话!");
    }
}

class SmartPhone extends Phone {
    // 构造方法
    constructor(brand, price, color, size) {
        // super可以调用父类,super()则是调用父类的构造方法
        super(brand, price); // Phone.call(this, brand, price);
        this.color = color;
        this.size = size;
    }
    photo() {
        console.log("我能拍照!");
    }
    playGame() {
        console.log("我能玩游戏!");
    }
}
let xiaoMi = new SmartPhone("小米", 699, "黑色", "5.5inch");
console.log(xiaoMi);
xiaoMi.call();// 我可以打电话!
xiaoMi.photo();// 我能拍照!
xiaoMi.playGame();// 我能玩游戏!

get和set方法

  • getter 与 setter 必须同级出现
  • 可自定义get和set方法,这样当被赋值或者是被读取值就会执行get或set方法
class Phone {
    get price() {
        console.log('价格属性被获取了');
        return 'iloveyou';
    }

    set price(newVal) {
        console.log('价格属性被修改了');
    }
}

// 实例化对象
let s = new Phone();
console.log(s.price);// 打印,价格属性被获取了,返回的值iloveyou

s.price = 'free';// 打印,价格属性被修改了

1.16 数值扩展

  • Number.EPSILON 是 JavaScript 表示的最小精度

    EPSILON 属性的值接近于 2.2204460492503130808472633361816E-16

    function equal(a, b) {
        if (Math.abs(a - b) < Number.EPSILON) {
            return true;
        } else {
            return false;
        }
    }
    console.log(0.1 + 0.2); // 0.30000000000000004
    console.log(0.1 + 0.2 === 0.3); // false
    console.log(equal(0.1 + 0.2, 0.3)); // true
    
  • 二进制和八进制

    let b = 0b1010;
    console.log(b); // 10
    let o = 0o777;
    console.log(o); // 511
    let d = 100;
    console.log(d); // 100
    let x = 0xff;
    console.log(x); // 255
    
  • Number.isNaN 检测一个数值是否为 NaN

    console.log(Number.isNaN(123)); // false
    
  • Number.parseInt Number.parseFloat字符串转整数

    console.log(Number.parseInt("511111fuck")); // 511111
    console.log(Number.parseFloat("3.1415926疯狂")); // 3.1415926
    
  • Number.isInteger() 判断一个数是否为整数

    console.log(Number.isInteger(5)); // true
    console.log(Number.isInteger(2.5)); // false
    
  • Math.trunc 将数字的小数部分抹掉

    console.log(Math.trunc(3.5)); // 3
    
  • Math.sign 判断一个数到底为正数 负数 还是零

    console.log(Math.sign(100)); // 1
    console.log(Math.sign(0)); // 0
    console.log(Math.sign(-20000)); // -1
    

1.17 对象方法扩展

// 1. Object.is 判断两个值是否完全相等
console.log(Object.is(120, 120)); // true
console.log(Object.is(NaN, NaN)); // true
console.log(NaN === NaN); // false

// 2. Object.assign 对象的合并
const config1 = {
    host: 'localhost',
    port: 3306,
    name: 'root',
    pass: 'root',
    test: 'test'
};

const config2 = {
    host: 'http://codeslive.top',
    port: '33060',
    name: 'xiaokang',
    pass: 'iloveyou',
    test2: 'test2'
}

console.log(Object.assign(config1, config2));

// 3. Object.setPrototypeOf 设置原型对象 Object.getPrototypeof
const school = {
    name: '小康'
}

const cities = {
    xiaoqu: ['beijing', 'shanghai', 'shenzhen']
}

Object.setPrototypeOf(school, cities);
console.log(Object.getPrototypeOf(school));
console.log(school);

1.18 模块化

模块化是指将一个大的程序文件,拆分成许多小的文件,然后将小文件组合起来。

模块化的优势有以下几点:

  1. 防止命名冲突
  2. 代码复用
  3. 高维护性

ES6之前的模块化规范有:

  1. CommonJS => NodeJS、Browserify
  2. AMD => requireJS
  3. CMD => seaJS

ES6 的模块化分为导出(export) @与导入(import)两个模块。

  1. export 命令用于规定模块的对外接口
  2. import 命令用于输入其他模块提供的功能,单例模式:多次重复执行同一句 import 语句,那么只会执行一次,而不会执行多次。import 同一模块,声明不同接口引用,会声明对应变量,但只执行一次 import 。

基础使用


// m1.js文件
// 分别暴露
export let school = "西南石油大学";
export function study() {
    console.log("我可以学习到新的技能!!")
}

// m2.js文件
// 默认暴露
export default {
    school: "西南石油大学",
    change: function () {
        console.log("我可以改变自己!");
    }
}

/*-----export [test2.js]-----*/
let myName = "Jerry";
export { myName }

/*-----export [test.js]-----*/
let myName = "Tom";
export { myName as exportName }


// 引入 m1.js 模块内容
import * as m1 from "./m1.js";
import * as m2 from "./m2.js";

// 调用m1文件的方法
m1.study();

// 调用m2文件的默认暴露方法
m2.default.study();

// 引用赋值
// import { default as m3 } from "./src/js/m3.js";
// console.log(m3);

// 结构赋值形式
// import { school, study } from "./m1.js";
// console.log(school);
// study();

// 使用as 定义别名引用
// import { school as xueXiao, study as learn } from "./m1.js";
// console.log(xueXiao);
// learn();// 调用的是m1文件的study方法

// 简便形式 针对默认暴露
// import ezm from "./src/js/m1.js";
// ezm.study();

2. JS 2016(ES7)

2.1 判断数组是否包含特定元素

Includes 方法用来检测数组中是否包含某个元素,返回布尔类型值

const mingZhu = ["西游记", "红楼梦", "三国演义", "水浒传"];
console.log(mingZhu.includes("西游记")); // true
console.log(mingZhu.includes("石头记")); // false

const numbers = [1, 2, 3, 4, 5];

console.log(numbers.includes(3)); // true
console.log(numbers.includes(6)); // false
console.log(numbers.includes(1, 2)); // false,从索引 3 开始查找

2.2 指数操作符

在ES7中引入指数运算符[**],用来实现幂运算,功能与Math.pow 结果相同

2.2.1 语法

base ** exponent
// base: 底数,即要进行乘方运算的数值。
// exponent: 指数,表示要将底数乘方的次数。

2.2.2 示例

console.log(2 ** 10); // 1024
console.log(Math.pow(2, 10)); // 1024

3. JS 2017(ES8)

3.1 async函数

async函数是异步的一种方案,可以让异步的操作同步执行。

  1. async函数的返回值为 promise 对象
  2. promise对象的结果由 async函数执行的返回值决定
// async 函数
async function fn() {
    // 返回一个字符串
    // return "好炎热";
    // 返回的结果不是一个 Promise 类型的对象,返回的结果就是成功 Promise 对象
    // return;
    // 抛出错误,返回的结果是一个失败的 Promise
    // throw new Error("出错啦!");
    // 返回的结果如果是一个 Promise 对象
    return new Promise((resolve, reject) => {
        // resolve("读取数据成功!");
        reject("读取数据失败!");
    });
}

const result = fn();
// console.log(result);
// 调用 then 方法
result.then(value => {
    console.log(value);
}, reason => {
    console.warn(reason);  // 读取数据失败!
}
);

3.2 await表达式

  • await 必须写在async函数中
  • await 右侧的表达式一般为 promise 对象
  • await 返回的是 promise 成功的值
  • await 的 promise 失败了,就会抛出异常,需要通过 try...catch 捕获处理*
// 创建 Promise 对象
let p = new Promise((resolve, reject) => {
    // resolve("请求成功!");
    reject("请求失败!");
});

// await 要放在 async 函数中.
async function main() {
    try {
        let result = await p;
        console.log(result); // 请求失败!
    } catch (e) {
        console.error(e);
    }
}

// 调用函数
main();

3.3 async与await结合发送AJAX请求

异步按照顺序执行

// 发送 AJAX 请求,返回的结果是 Promise 对象
function sendAJAX(url) {
    return new Promise((resolve, reject) => {
        // 1、创建对象
        const x = new XMLHttpRequest();
        // 2、初始化
        x.open("GET", url);
        // 3、发送
        x.send();
        // 4、事件绑定
        x.onreadystatechange = function () {
            if (x.readyState === 4) {
                if (x.status >= 200 && x.status < 300) {
                    // 请求成功
                    resolve(x.response);
                } else {
                    // 请求失败
                    reject(x.status);
                }
            }
        };
    });
}
// Promise then 方法的测试
// sendAJAX("https://api.apiopen.top/api/getDynamic").then(value=>{
//   console.log(value);
// },reason=>{
// });
// async 与 await 测试
async function main() {
    // 发送 AJAX 请求
    let result = await sendAJAX("https://api.apiopen.top/api/getDynamic");
    // console.log(result);
    let tianQi = await sendAJAX(
        "https://www.tianqiapi.com/api/?version=v1&city=%E5%8C%97%E4%BA%AC&appid=23941491&appsecret=TXoD5e8P"
    );
    console.log(tianQi);
}
main();

3.4 对象方法扩展

  • Obiect.values()方法返回一个给定对象的所有可枚举属性值的数组
  • Obiect.entries()方法返回一个给定对象自身可遍历属性 [key,value] 的数组
// 声明对象
const school = {
    name: 'xiaokang',
    cities: ['beijing', 'shanghai', 'shenzhen'],
    xueke: ['前端', 'Java', '大数据', '运维']
};

// 获取对象所有的键
console.log(Object.keys(school));

// 获取对象所有的值
console.log(Object.values(school));

const obj = { a: 1, b: 2 };
const values = Object.values(obj);
console.log(values); // 输出:[1, 2]


// entries
const obj = { a: 1, b: 2 };
const entries = Object.entries(obj);
console.log(entries); // 输出:[['a', 1], ['b', 2]]

console.log(Object.entries(school));
// entries,用来创建map
const m = new Map(Object.entries(school));
console.log(m.get('cities'));

// 对象属性描述对象
// 对象属性的描述对象,即获取name的值
// 主要用于拷贝时,拷贝一份与原来对象相同属性的对象
console.log(Object.getOwnPropertyDescriptors(school));

const obj = { a: 1, b: 2 };
const descriptors = Object.getOwnPropertyDescriptors(obj);
console.log(descriptors);
// 输出:
// {
//   a: { value: 1, writable: true, enumerable: true, configurable: true },
//   b: { value: 2, writable: true, enumerable: true, configurable: true }
// }

const obj = Object.create(null,{
    name:{
        value:'Java',
        //属性特性
        writable:true,  // 是否可写
        configurable:true,  //是否可配置
        enumerable:true,  //是否可枚举
    }
})

4. JS 2018(ES9)

4.1 扩展运算符与rest参数

Rest 参数与 spread 扩展运算符在 ES6 中已经引入,不过 ES6 中只针对于数组,在 ES9 中为对象提供了像数组一样的 rest 参数和扩展运算符。

// rest 参数
function connect({ host, port, ...user }) {
    console.log(host); // 127.0.0.1
    console.log(port); // 3306
    console.log(user); // {username: 'root', password: 'root', type: 'master'}
}
connect({
    host: "127.0.0.1",
    port: 3306,
    username: "root",
    password: "root",
    type: "master",
});


// 对象合并
const skillOne = { q: '天音波' }
const skillTwo = {w: '金钟罩'}
const skillThree = {e: '天雷破'}
const skillFour = {r: '猛龙摆尾'}

const mangseng = { ...skillOne, skillTwo, skillThree, skillFour };
console.log(mangseng);// {q: '天音波',skillTwo: { w: '金钟罩' },skillThree: { e: '天雷破' },skillFour: { r: '猛龙摆尾' }}

4.2 正则扩展

4.2.1 命名捕获分组

// 声明一个字符串
// let str = `<a href="https://codeslive.top">小康</a>`;
// // 提前 url 与 标签文本
// const reg = /<a href="(.*)">(.*)</a>/;
// // 执行
// const result = reg.exec(str);
// console.log(result);
// console.log(result[1]); // https://codeslive.top
// console.log(result[2]); // 小康

let str = `<a href="https://codeslive.top">小康</a>`;
// 分组命名
const reg = /<a href="(?<url>.*)">(?<text>.*)</a>/;
const result = reg.exec(str);
console.log(result.groups.url);// https://codeslive.top
console.log(result.groups.text);// 小康

4.2.2 正则表达式反向断言

// 声明字符串
let str = 'JS5211314你知道么555啦啦啦';

// 1.正向断言
const reg1 = /\d+(?=啦)/;// 匹配在啦之前的多个数字,不包含啦
const result1 = reg1.exec(str);
console.log(result1);

// 2.反向断言
const reg2 = /(?<=么)\d+/;// 匹配在么之后的多个数字,不包含么
const result2 = reg2.exec(str);
console.log(result2);

4.3 dotAll模式

//dot  .  元字符  除换行符以外的任意单个字符
let str = `
        <ul>
            <li>
                <a>肖生克的救赎</a>
                <p>上映日期: 1994-09-10</p>
            </li>
            <li>
                <a>阿甘正传</a>
                <p>上映日期: 1994-07-06</p>
            </li>
        </ul>`;
//声明正则
// const reg = /<li>\s+<a>(.*?)</a>\s+<p>(.*?)</p>/;
const reg = /<li>.*?<a>(.*?)</a>.*?<p>(.*?)</p>/gs;
//执行匹配
// const result = reg.exec(str);
let result;
let data = [];
while (result = reg.exec(str)) {
    data.push({ title: result[1], time: result[2] });
}
//输出结果
console.log(data);
/* 
[
    { title: '肖生克的救赎', time: '上映日期: 1994-09-10' },
    { title: '阿甘正传', time: '上映日期: 1994-07-06' }
] 
*/

5. JS 2019(ES10)

5.1 对象扩展方法Object.fromEntries

用来创建一个对象,参数是一个数组或者是map对象。

将二维数组转为对象,而ES8 中 Object.entries 方法是把对象转化为数组

// 二维数组转换成对象
const result = Object.fromEntries([
    ["name", "西南石油大学"],
    ["zhuanYe", "计算机科学与技术, 软件工程, 人工智能"],
]);
console.log(result); // {name: '西南石油大学', zhuanYe: '计算机科学与技术, 软件工程, 人工智能'}

// Map
const m = new Map();
m.set("name", "电子科大");
m.set("zhuanYe", "大数据");
const result1 = Object.fromEntries(m);
console.log(result1); // {name: '电子科大', zhuanYe: '大数据'}

// Object.entries ES8 对象转二维数组
const arr = Object.entries({
    name: "红石榴21",
});
console.log(arr); // [["name","红石榴21"]]

5.2 字符串方法扩展-trimStart-trimEnd

trimStart : 清除字符串左侧的字符

trimEnd: 清除字符串右侧的字符

let str = ' asd  '
console.log(str) //asd
console.log(str.trimStart()) //asd  清空头空格
console.log(str.trimEnd()) //  asd  清空尾空格

5.3 数组方法扩展-flat与flatMap

flat: 将多维数组转化为低维数组

flatMap: 将map对象进行降维

const arr = [1, 2, 3, [4, 5, 6, [7, 8, 9]]]
//参数为深度,是一个数字,2表示2层
console.log(arr.flat(2)) //[1,2,3,4,5,6,7,8,9]

const arr3 = [1, 2, 3, 4];
const result = arr3.flatMap((item) => [item * 10]);
console.log(result); // [10, 20, 30, 40]

5.4 Symbol.prototype.description

用来获取Symbol的字符串描述

// 创建Symbol
let s = Symbol("莫得感情的鲨手");
console.log(s.description); // 莫得感情的鲨手

6. JS 2020(ES11)

6.1 私有属性

私有属性外部不可读,属性名前加#符号为私有属性

class Person {
    // 公有属性
    name;
    // 私有属性
    #age;
    #weight;
    // 构造方法
    constructor(name, age, weight) {
        this.name = name;
        this.#age = age;
        this.#weight = weight;
    }
    intro() {
        console.log(this.name); // 尕车
        console.log(this.#age); // 18
        console.log(this.#weight); // 45kg
    }
}
// 实例化
const girl = new Person("尕车", 18, "45kg");
console.log(girl.name);
//   console.log(girl.#age); // error
//   console.log(girl.#weight); // error
girl.intro();

6.2 Promise.allSettled方法

allSettled 和all 方法,主要用于批量处理异步任务。

//声明两个promise对象
const p1 = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('商品数据-1')
    }, 1000)
})

const p2 = new Promise((resolve, reject) => {
    setTimeout(() => {
        reject('出错了!')
    }, 1000)
})

//调用allsettled方法:返回的结果始终是一个成功的,并且异步任务的结果和状态都存在
const res = Promise.allSettled([p1, p2]);
console.log(res)

// Promise {<pending>}
//     __proto__: Promise
//     [[PromiseState]]: "resolve"
//     [[PromiseResult]]: Array(2)
//			0: {status: 'fulfilled', value: '商品数据-1'}
//			1: {status: 'rejected', reason: '出错了!'}

//=========================================================
//调用all方法:返回的结果是按照p1、p2的状态来的,
//如果都成功,则成功,
//如果一个失败,则失败,失败的结果是失败的Promise的结果
const result = Promise.all([p1, p2])
console.log(result)

6.3 String.prototype.matchAll方法

返回一个包含所有匹配正则表达式的结果及分组捕获组的迭代器

let str = `<ul>
      <li>
          <a>肖生克的救赎</a>
          <p>上映日期: 1994-09-10</p>
      </li>
      <li>
          <a>阿甘正传</a>
          <p>上映日期: 1994-07-06</p>
      </li>
  </ul>`;

//声明正则
const reg = /<li>.*?<a>(.*?)</a>.*?<p>(.*?)</p>/sg

//调用方法
const result = str.matchAll(reg); //返回的是一个可迭代对象

//展开可迭代对象  方法1
// for(let v of result){
//     console.log(v);
// }

//展开可迭代对象  方法2
const arr = [...result];

console.log(arr);

6.4 可选链操作符 ?.

//相当于一个判断符,如果前面的有,就进入下一层级
function main(config) {
    const dbHost = config?.db?.host  //等价于 config && config.db && config.db.host
    console.log(dbHost) //192.168.1.100
}

main({
    db: {
        host: '192.168.1.100',
        username: 'root'
    },
    cache: {
        host: '192.168.1.200',
        username: 'admin'
    }
})

6.5 动态import

当用到对应的文件时在进行加载

// hello.js
export function hello(){
    alert('Hello');
}


// app.js

// import * as m1 from "./hello.js";  //静态传入
const btn = document.getElementById('btn');
btn.onclick = function(){
		//使用之前并未引入,动态引入,返回的其实是一个Promise对象
		// 使用的时候再进行传入,import函数,参数是路径
    import('./hello.js').then(module => {
        module.hello();
    });
}

6.6 BigInt类型

// 大整形
// let n = 521n;
// console.log(n, typeof n); // 521n 'bigint'
// 函数
// let n = 123;
// console.log(BigInt(n)); // 123n
// console.log(BigInt(1.2)); // error
// 大数值运算
let max = Number.MAX_SAFE_INTEGER;
console.log(max); // 9007199254740991
console.log(max + 1); // 9007199254740992
console.log(max + 2); // 9007199254740992 当数字上线加多少值也不会变
console.log(BigInt(max)); // 9007199254740991n
console.log(BigInt(max) + BigInt(1)); // 9007199254740992n
console.log(BigInt(max) + BigInt(2)); // 9007199254740993n

6.7 绝对全局对象globalThis

globalThis是一个全局对象,它提供了全局可访问性,可以在任何地方访问。这意味着,不论在哪个环境中运行JavaScript(浏览器、Node.js、Web Workers等),globalThis始终指向这个环境的全局对象。

globalThis是为了解决不同环境中全局对象名称不统一的问题。在浏览器中,全局对象是window;在Node.js中,全局对象是global。而globalThis则提供了一个统一的方式,可以在任何JavaScript环境中访问全局对象。

console.log(globalThis) //window  //适用于复杂环境下直接操作window

7. JS 2021(ES12)

node版本>=15.0.0

7.1 逻辑运算符和赋值表达式

  • &&=:逻辑与赋值表达式,将右侧的值赋给左侧的变量,但仅当左侧的变量在布尔上下文中为真时。
  • ||=:逻辑或赋值表达式,将右侧的值赋给左侧的变量,但仅当左侧的变量在布尔上下文中为假时。
  • ??=:空值合并赋值表达式,将右侧的值赋给左侧的变量,但仅当左侧的变量为 null 或 undefined 时。
let x = 5;
let y = 10;

// 逻辑与赋值表达式
x &&= y;
console.log(x); // Output: 10 (因为 x 为真,所以将 y 赋给 x)

let a = 0;
let b = 20;

// 逻辑或赋值表达式
a ||= b;
console.log(a); // Output: 20 (因为 a 为假,所以将 b 赋给 a)

let foo = null;
let bar = 'Hello';

// 空值合并赋值表达式
foo ??= bar;
console.log(foo); // Output: "Hello" (因为 foo 为 null,所以将 bar 赋给 foo)

7.2 String.prototype.replaceAll()替换字符串

返回一个全新的字符串,所有符合匹配规则的字符都将被替换掉

const str = '11-22-33-44';
const newStr = str.replaceAll('-', '+'); 
console.log(newStr); // 11+22+33+44

7.3 数字分隔符

数字分隔符是一种增强的数值表示法,可以在数字中使用下划线 _ 进行分隔,以提高数字的可读性。

const billion = 1_000_000_000;
const pi = 3.14_15_92_65;
console.log(billion); // Output: 1000000000
console.log(pi); // Output: 3.14159265