ECMAScript 新特性语法,一次性掌握 ES6+

4,779 阅读13分钟

1 ECMAScript 和 JavaScript 概述

浏览器端JavaScript: ECMAScript、BOM、DOM
node端的JavaScript:ECMAScript、nodeAPI

ECMA: 欧洲计算机标准协会
ECMAScript: 一套语法标准

ECMAScript是JavaScript的规格
JavaScript是ECMAScript的实现

ECMAScript的实现: JavaScript、JScript、ActionScript(flash)

1.1 ECMAScript的重要版本

ECMAScript3.0  简称ES3
ES5.0 / ES5.1  新增一些扩展
ES6 20156月  又叫ES2015 (之后每年6月份出一个新的版本)
ES7 ES2016
ES8 ES2017
ES9 ES2018
ES10 ES2019
ES11 ES2020

2 关键字扩展

2.1 let 关键字

用于声明变量,类似于var
特点:
    ① 不能重复声明
    ② let声明的变量不会提升
    ③ let声明的变量可以具有块级作用域
    ④ let声明的全局变量不再是顶层对象的属性

2.2 let 与块级作用域

// 给三个按钮绑定单击事件,点击输出索引号
let btn = document.querySelectorAll('button')

for (var i = 0; i < btn.length; i++) {
    btn[i].onclick = function () {
      console.log(i); // 3 3 3
    };
  }

for (let i = 0; i < btn.length; i++) {
    btn[i].onclick = function () {
      console.log(i); // 0 1 2
    };
  }

2.3 const 关键字

常量不可改变的量,用途:程序的配置信息习惯用常量定义
特点:
    ① 值不能修改,更不能重复声明
    ② 不会变量提升
    ③ 具有全局、局部、块级作用域
    ④ 全局的常量不是window的属性

3 对象的简写

const name = '小明'
const age = '18'

// 属性同名可简写
const obj = {
  name
  age
  say() {   //方法简写 

  },
}

console.log(obj) // { name: '小明', age: '18' }

4 解构赋值

4.1 数组解构赋值

// 1. 解构声明
const [a, b, c] = [value1, value2, value3]

// 2. 修改变量的值
[a, b, c] = [value1, value2, value3]

// 3 复杂数组 (保证两边的数组形式一致)
const [a, [b], [c, [d,e]]] = [100, [200], [300, [400, 500]]]

// 4. 声明变量的时候可以有默认值 (与函数传参类似)
const [a, b, c, d = 5] = arr 
console.log(d) // 5

4.2 对象解构赋值

// 简写形式
{name, age} = {name: '张三', age: 18} //张三 ,18

// 对象形式
const obj = {
  name: '小明',
  age: 18,
  sex: '男',
  love: {
    eat: '吃饭',
    sleep: '睡觉',
    peas: '打豆豆',
  }
}

const { name, age, sex } = obj
console.log(name, age, sex) // 小明 18 男

// 解构重名
const { name: myname } = obj
console.log(myname) // 小明

// 嵌套解构
const { love: { sleep } } = obj
console.log(sleep) // 睡觉

5 字符串扩展

5.1 模板字符串

两个反引号 ``
特点:
	${变量} 直接解析
	${表达式} 得到表达式的结果

5.2 字符串对象新增的方法

① ES5

trim() 去除两边的空格

② ES6+

startsWith() 返回布尔值
endsWith()   返回布尔值
includes()   返回布尔值
repeat()
padStart()   ES8   补全  //message.padStart(100, '#')
padEnd()     ES8   补全  //message.padEnd(100, '#')
matchAll()   ES10  message.matchAll(/\w/) //返回迭代器(是所有匹配到的结果)
trimStart()  ES10
trimEnd()    ES10

6 数值的扩展

6.1 指数运算符 (**) ES7

2 ** 4;  // 计算2的4次方
2 ** 2 ** 3// 运算顺序,先算右边的

6.2 二进制和八进制的表示

// 二进制
0b101010

// 八进制
0o17

// 比以前的八进制表示方式语法更合理,在严格模式可用(0开头表示八进制,严格模式不可用)

6.3 Math对象新增的方法

Math.trunc()  去掉小数部分
Math.sign()   判断一个数字是正数、负数、还是0
Math.cbrt()   求立方根
Math.hypot()  求所有参数的平方和的平方根(用于勾股定理计算斜边长度)

7 函数扩展

7.1 参数默认值

// ES6的写法
function fn(a,b=默认值) {
    
}

// ES6之前的写法
function fn(a,b) {
    if (b === undefined) {
        b = 默认值
    }
}

7.2 rest参数

// 1、arguments获取实参
function fn() {
    console.log(arguments);
}

// 2、rest参数获取
function fn(...numbers) {
    console.log(numbers); //得到一个数组,里面是所有的实参; numbers的名字只要符合标识名命名规范即可
}

与arguments类似
rest参数得到是数组,arguments得到是伪数组

7.3 箭头函数

① 语法

// 以前定义函数的方式 (表达式方式)
const fn = function () {}

// 箭头函数写法
const fn = () => {}

// 箭头函数简写,如果参数只有一个,可以省略括号
const fn = name => {}

// 只有一个参数,且只有一条返回语句
const fn = num => num * num

// 如果返回的是对象
const fn = name => ({ name: name })

② 特点

1. this指向。 与谁调用了箭头函数无关,与声明箭头函数位置有关
   看声明箭头函数的地方,是不是嵌套在函数内,如果嵌套在了函数内,看外层函数的this指向;如果没有被函数嵌套,指向window
   
2. 箭头函数内无法获取aruguments,可以使用rest参数

3. 箭头函数不能作为构造函数

4. 箭头函数不能作为生成器

③ 适用场景

使用场景:作为回调函数
不适合的场景:给对象添加方法(this指向)、构造函数、生成器函数

7.4 函数对象新增属性

name  返回函数名(声明函数时给的名字)

8 数组扩展

8.1 扩展运算符

① 定义

rest参数的逆运算 把数组转为用逗号分隔的参数序列

② 应用

const nums = [100, 200, 300, 250];
console.log(...nums); // 100 200 300 250

// 1. call 数组传参
fn.call({}, ...nums);
fn.apply({}, nums);

// 2. 计算数组中最大的元素
console.log(Math.max(...nums));

// 3. 把一个数组的成员 追加到另一个数组的尾部
const names = ['曹操', '张仁', '刘备'];
nums.push(...names); // nums, push('曹操', '张仁', '刘备')
console.log(nums);   //[100, 200, 300, 250, "曹操", "张仁", "刘备"]

// 4. 克隆数组
// const nums1 = nums;  //引用类型
const nums1 = [...nums];  //克隆版
nums1[2]= '啦啦啦';
console.log(nums1); //[100, 200, "啦啦啦", 250, "曹操", "张仁", "刘备"]
console.log(nums);  //[100, 200, 300, 250, "曹操", "张仁", "刘备"]

// 5. 合并数组
const newArr = [...nums, ...nums1, ...names];
console.log(newArr);

// 6. 把字符串转换为数组
const newArr1 = [...'hello'];
console.log(newArr1); // ["h", "e", "l", "l", "o"]

// 7. 把类数组对象转为数组
const btns = document.querySelectorAll('button');
console.log(btns); 
console.log([...btns]); 

③ 解构赋值版的rest参数

// 与解构赋值一起使用 (解构赋值版的rest参数)
const [a, ...b] = [10,20,30,40,50,50,60];
console.log(a, b);  // a是10, b是数组[20, 30, 40, 50, 50, 60]

8.2 Array函数新增方法

Array.from()   把类数组/字符串转为纯数组
Array.of()     创建数组,参数是数组的成员(任意个数的参数)

8.3 Array实例新增的方法

find()          参数是回调函数,返回第一个满足条件的元素
findIndex()	参数是回调函数,返回第一个满足条件的元素的索引
fill()		填充数组,覆盖数组中所有的元素;适合填充 new Array(20)创建的数组
includes()      判断数组中是否包含某个元素,返回布尔值; ES7新增的
flat()          把数组拉平(多维数组变为一维数组),参数默认是1(只拉1层), 可设置Infinity,不论维位数租  ES10新增
flatMap()	参数是回调函数,相当于map和flat的结合 ES10新增

9 对象的扩展

9.1 属性名表达式

{
    [表达式]:值,
     属性名: 值
}

9.2 super 关键字

1. super指向调用该函数的实例的原型 
2. 只有对象的方法中才有super关键字,且简写形式定义的方法
{
    foo(){
        super; //得到super
    }
}

9.3 对象的扩展运算符 ... (ES9新增)

定义:把对象转为用逗号分隔的键值对序列
作用:对象的解构赋值,对象操作(对象合并、对象克隆)
// 定义对象
 const obj = {
    name: '曹操',
    age: 100,
    say() {
      console.log('My Name is ' + this.name);
    },
  };

console.log({ ...obj });
// 相当于
console.log(name:'曹操', age:100, say:fn);

// 对象克隆 (浅克隆)
const obj2 = { ...obj };
obj2.name = '刘备';
console.log(obj2);// {name: "刘备", age: 100, say: ƒ}
console.log(obj); // {name: "曹操", age: 100, say: ƒ}

// 对象合并 如果有重名属性,后面覆盖前面的
const obj3 = { width: 100, height: 200, name: '孙权' };
const obj4 = { ...obj, ...obj3 };
console.log(obj4); // {name: "孙权", age: 100, width: 100, height: 200, say: ƒ}

// 用于对象的解构赋值
const { age, ...b } = obj;
console.log(age); // 100
console.log(b);   // {name: "曹操", say: ƒ}

9.4 Object函数新增的方法

Object.is()  用于比较两个数据是否相等,返回布尔值; 类似于全等,不同点NaNNan相等、+0和-0不相等
Object.assign()  合并对象
Object.getOwnPropertyDescriptor()    获取某个自身属性的描述信息
Object.getOwnProppertyDescriptors()  获取对象所有自身属性的描述信息  ES8新增
Object.getPrototypeOf()     获取对象的原型
Object.setPrototypeOf()     给对象设置原型
Object.keys()     返回数组,由对象的属性名组成。
Object.values()   返回数组,由对象的属性值组成。 ES8新增
Object.entries()  返回二维数组,由属性名和属性值 组成  ES8新增
Object.getOwnPropertyNames()   返回数组,有对象的属性名组成
Object.formEntries()   Object.entries()的逆运算  ES8新增

10 Class语法

10.1 定义类(构造函数)

  // 定义类
  class Person {
    // 属性,会添加到实例上
    // 把所有的属性在这里声明
    name = null;
    age = null;

    // 定义构造方法 实例化的时候自动执行
    constructor(name, age = 10) {
      this.name = name;
      this.age = age;
    }

    // 方法,添加到原型上
    say() {
      console.log('MY Name is ' + this.name);
    }
    eat() {
      console.log('My age is ' + this.age);
    }

    // 静态方法 没有添加到实例上,构造函数本身的方法
    // static getClassName() {
    //   console.log('类名是 Person 构造函数本身的方法');
    // }
  }
  //   Person.getClassName(); // 相当于 Person.getClassName = function(){}

  console.log(Person); //输出整个函数
  console.log(typeof Person); // Function
  console.log(Person.name); // Person ===> name指的是这个对象的名字

  // 不能调用
  // Person();

  // 实例化
  var p = new Person('曹操', 19);
  console.log(p); // Person {name: "曹操", age: 19}
  p.say(); // MY Name is 曹操

  var p1 = new Person('吕布', 21);
  console.log(p1); // Person {name: "吕布", age: 21}
  p1.say(); // MY Name is 吕布

  var p2 = new Person();
  console.log(p2); // Person {name: undefined, age: 10}

注意:

class定义的类 本质还是个函数,但是不能被调用,只能被实例化

typeof 类名 === 'funciton'

10.2 实例化

new 类名;
new 类名(构造方法的参数);

10.3 静态方法

class Person{
    static 方法名() {
    
    }
}

Person.方法名()

// 静态方法没有添加给实例,添加给构造函数(类)本身

10.4 getter 和 setter

class Person {
    firstName = '东方';
    lastName = '不败';

    get fullName() {
      return this.firstName + '_' + this.lastName;
    }

    set fullName(val) {
      const name = val.split('_');
      this.firstName = name[0];
      this.lastName = name[1];
    }
  }

  let p = new Person();
  console.log(p); //Person {firstName: "东方", lastName: "不败"}

  console.log(p.fullName); //可读可写 东方_不败

10.5 继承

1. 使用 extends 来继承
2. 继承之后:
    子类的实例的原型指向父类的一个实例
    子类自己的原型指向父类 (静态方法也可以继承)
3. 可以在子类上添加属性和方法
4. 在子类上重写父类的方法,子类重写的方法必须调用super()
class 子类 extends 父类 {
    constructor() {
        super();
    }
}

11 新增的数据类型(原始类型)

11.1 Symbol

1. 创建一Symbol数据(只能调用,不能实例化)
    Symbol()

2. symbol数据特点:
    ① 每创建一个symbol类型的数据,都是唯一的。
    ② symbol类型的数据可以作为属性名,同字符串一样

3. 应用:
    给对象添加属性(不会被覆盖)

4. 注意:(可以作为对象的属性名,(字符串)symbol创建的都是唯一的)
    for...inObject.keys()、Object.values()、Object.entries()、Object.getOwnPropertyNames()  这些方法都取不到属性名时Symbol类型的属性
    Object.getOwnPropertySymbols()  获取类型是symbol的属性名
    Reflect.ownKeys(obj)  获取对象自身所有的属性(不论属性名时什么类型)

11.2 BigInt (ES10)

① 安全数

通过 Number.MAX_SAFE_INTEGERNumber.MIN_SAFE_INTEGER  两个属性获得
如果整数超过了这个范围,无法按照整型的方式存储,计算会造成不精确

② BigInt类型

// 1. 字面量
var a = 100n
console.log(typeof a); //bigint

// 2 转换函数
var b = BigInt(1000)
console.log(b); //1000n
bigInt类型的数据适合比较大的数字运算
bigInt类型的数据只能和BigInt类型的数据运算,不能和number类型数据 相互运算

12 Set 和 Map

12.1 Set

① 描述

无序且不重复的多个值的 集合

② 创建一个Set类型的数据

new Set();  //创建的是空的Set
new Set(数组);  //把数组变为Set,去掉重复的值
new Set(类数组)

③ Set实例的方法

add(value)      添加一个值
delete(value)   删除一个值
has(value)      判断是否存在某个值
clear()         删除所有的值
keys()          返回遍历器
values()        返回遍历器
entries()       返回遍历器
forEach()       用于遍历
size 属性       获取Set成员的个数

④ Set应用

1. 实现数组去重
const arr = new Set([100, 200, 300, 400, 300, 500, 500, 500]);
console.log(arr); //Set(5) {100, 200, 300, 400, 500}

12.2 Map

① 描述

Object对象是一种键值对(key-value)的集合,属性名就是键(key),属性值就是值(value) 
Map类似于对象,也是键值对的集合,对象的Key只能是字符串和Symbol类型,MapKey可以是任意类型

② 创建Map

// 创建空的
new Map();

// 创建的时候,指定初始值
new Map([
    [key,value],
    [key,value]
]);

③ Map实例的方法

get(key)        获取指定key的值
set(key,value)  设置或添加某个key的值
delete(key)     删除指定的某个key和他值
clear()         清空所有
has(key)        判断某个key是否存在
keys()          返回遍历器,所有key的集合
values()        返回遍历器,所有值的集合
entries()       返回遍历器,所有key-value的集合(二维)
forEach()       用于遍历

13.2 iterable 可遍历对象

① 什么是 iterable 可遍历对象

把部署了 iterator 接口的数据结构,称之为'iterable'(可遍历对象)
可以使用 for...of 遍历 iterable对象

iterator接口部署在了数据结构的Symbol.iterator属性上
一个对象,只有具有Symbol.iterator属性,且该属性指向一个返回遍历器的函数,该对象就是

② 原生实现了iterator接口的数据结构 (可遍历对象)

Array
Set
Map
String
Arguments
NodeList
HTMLcollection

14 generator 生成器

14.1 什么是生成器

生成器就是生成遍历器的函数

14.2 定义生成器

function* 生成器名() {
     yield 值;
     yield 值;
     yield 值;
     yield 值;
}

14.3 yield 关键字

yeild关键返回一个值, 遍历的时候每次得到就是yield的值
调用next(),执行到yield就会停止; 下一次调用next(),执行到下一个yield停止

调用生成器函数的时候,函数内不会执行

当调用next()的时候,才开始执行生成器函数内的代码; 执行到yield停止

14.4 使用生成器函数给对象部署iterator接口

const obj = {
    name: '曹操',
    age: 18,
    score: 80,
    height: 170
};

// 把obj变为一个 iterable
// 部署iterator接口
obj[Symbol.iterator] = function* (){
    for (let i in obj) {
        yield [i, obj[i]];
    }
};

for (let i of obj) {
    console.log(i); //可遍历对象
}

15 模块

15.1 模块中导出数据

// 模块内部
function say() {}
function eat() {}

export {
    say,
    eat
}

15.2 导入模块

import {say, eat} from '模块文件路径';

16 总结

16.1 ECMAScript中数据类型

原始类型(值类型): string、number、boolean、nullundefined、symbol、bigint
对象类型(引用类型): array、object、regexp、set、map......

16.2 ECMAScript中声明变量的方式

varletconst (常量) 、 classimportfunction

16.3 实现数组扁平化的方式

const arr = [['a', 'b'],[10, [100, 200]],[['A', 'B'],['一', '二'],],1000,];

  //方式一
  //console.log(arr.flat(Infinity)); //["a", "b", 10, 100, 200, "A", "B", "一", "二", 1000]

  //方式二 利用字符串的join方法  缺点:数组的元素都会变为字符串类型
  //const arr2 = arr.join().split(',');
  //console.log(arr2); //["a", "b", "10", "100", "200", "A", "B", "一", "二", "1000"]

  //方式三: 递归
  console.log(flatArray(arr));

  function flatArray(array) {
    //创建一个空数组
    let res = [];
    // 遍历传进来的数组
    for (var i = 0; i < array.length; i++) {
      //判断数组的元素还是不是数组
      if (array[i] instanceof Array) {
        res = res.concat(flatArray(array[i])); //继续调用把返回值拼起来,然后再赋值给res
        // res = [...res, ...flatArray(arr[i])];
        // res = res.concat(arguments.callee(arr[i]));
      } else {
        res.push(array[i]); //添加一个数组
      }
    }
    //返回新数组
    return res;
  }

16.4 实现数组对象拷贝(克隆)的方式 (浅拷贝)

数组Array:
1. [...arr]
2. arr.concat()  不写参数 数组合并方式
3. arr.splice(0) / arr.substring(0) / arr.substr(0)  数组截取方式

对象Object:
1. {...obj}
2. Object.assign(obj)   对象合并方式

16.5 实现对象的深度克隆(深拷贝)

let sons = ['曹丕', '曹植', '曹冲'];
let sister = { name: '曹芹', age: 18 };

  const obj = {
    name: '曹操',
    age: 100,
    sons,
    sister,
    say() {},
    eat() {},
  };
  // 1. 借助于JSON,无法拷贝方法,适合于纯数据对象
  // JSON.parse(JSON.stringify(obj));

  // 2. 使用递归函数 实现深度克隆
  let obj1 = deepClone(obj);
  obj1.sister.name = '曹雪芹';
  console.log(obj);
  console.log(obj1);

  //定义函数 获取对象的构造函数(类)名
  function getObjectClass(obj) {
    return Object.prototype.toString.call(obj).slice(8, -1);
  }

  //深拷贝的函数
  function deepClone(obj) {
    //判断obj是对象是数组还是其他
    if (getObjectClass(obj) === 'Object') {
      var res = {}; //创建空的对象
    } else if (getObjectClass(obj) === 'Array') {
      var res = []; //创建空数组
    } else {
      return obj;
    }

    //对传入的对象(遍历)进行遍历
    for (let i in obj) {
      res[i] = deepClone(obj[i]);
    }
    //返回新数组或对象
    return res;
  }

16.6 遍历对象属性的方式

1. for ... in  遍历自身以及原型上可以被遍历的属性,属性名是symbol类型不可以
2. Object.keys()、Object.value()、Object.entries()    自身的属性,属性名是symbol类型不可以
3. Object.getOwnPropertyNames()     自身属性名的集合, 属性名是symbol类型不可以
4. Object.getOwnPropertySymbols()   自身属性名时symbol类型的属性名的集合
5. Reflec.ownKeys()                 自身所有的属性名的集合 (字符串和symbol都可以)

16.7 Promise 对象

// promise对象状态:成功 -> 触发then的第一个回调
// promise对象状态:失败 -> 触发then的第二个回调

// then(onFulfilled, onRejected)
// 传了两个函数,这两个仅会执行一个

// then方法默认返回值 成功状态promise对象
// 什么时候会返回失败状态promise对象呢?
//     1. 方法中函数的返回值是一个失败状态的promise对象
//     2. 方法报错
// 除了以上两个方式,默认就是成功promise

const promise = new Promise((resolve, reject) => {
  // 同步调用
  resolve();
  // reject();
  console.log(111);
});

promise
  .then(
    () => {
      console.log(222);
    },
    () => {
      console.log(333);
      // 返回一个失败状态promise对象
      // return Promise.reject();
      return new Promise((resolve, reject) => {
        reject();
      });
      // 报错
      // 抛异常(立即产生一个错误)
      // throw 'error';
    } // 默认返回成功状态promise对象
  )
  // 下一个then触发哪个?看上一个then的返回值promise状态
  .then(
    () => {
      console.log(444);
    },
    () => {
      console.log(555);
    }
  );

console.log(666); // 运行结果 111 666 222 444

16.8 Promise 对象其它状态

  //   Promise 一个异步编程的解决方案
  //   作用:用来解决异步回调地狱问题(消除回调函数,以同步方式表达异步代码)

  //   特点:
  //     状态:
  //       1. pending 初始化状态
  //       2. resolved / fulfilled 成功状态
  //       3. rejected 失败状态

  //       注意:
  //         同一时间,只能是一个状态
  //         只能由初始化状态变成成功/失败状态
  //         不能由成功变成失败,也不能由失败变成成功
  //         (状态初始化为pending,只能改变一次,要么成功,要么失败)
  //       怎么判断promise对象的状态?
  //         then() / catch()

  //     结果值:内部的结果值(value/reason)
  //       resolve(value)
  //       reject(reason)
  //       怎么得到promise对象内部的结果值?
  //         then((value) => {}) / await
  //         catch((reason) => {})

  //     怎么创建promise对象?
  //       new Promise()  默认是pending
  //       Promise.resolve() 默认是resolved
  //       Promise.reject() 默认是rejected

  //     其他方法:
  //       Promise.all([promise1, promise2...])
  //         返回值是一个新的promise对象,新promise对象状态看传入的promise
  //           如果传入的promise状态都是成功的状态,新promise也成功
  //           如果传入的promise状态有一个失败,新promise立即失败
  
  //       Promise.race([promise1, promise2...])
  //         返回值是一个新的promise对象,新promise对象状态看传入的promise
  //           只看传入的n个promise,哪一个传入promise状态先发生变化
  //           新promise和先发生变化promise的状态一致
  
  //       Promise.allSettled([promise1, promise2...])  源自ES11/ES2020
  //         返回值是一个新的promise对象,一定是成功状态
  //           promise对象内部状态值,包含传入的n个promise对象的状态值

  const promise11 = Promise.resolve();
  const promise22 = Promise.reject();

  console.log('成功状态', promise11); //成功状态 Promise {<fulfilled>: undefined}
  console.log('失败状态', promise22); //失败状态 Promise {<rejected>: undefined}

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

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

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

  // 只有全部成功才成功,只要有一个失败即失败
  //  Promise.all([promise1, promise2, promise3])
  //   .then(value => {
  //     console.log("成功了", value);
  //   })
  //   .catch(reason => {
  //     console.log("失败了", reason);
  //   });

  // 只看执行最快那个,结果和先发生变化promise的状态一致,无论成功或失败
  //  Promise.race([promise2, promise1, promise3])
  //   .then(value => {
  //     console.log("成功了", value);
  //   })
  //   .catch(reason => {
  //     console.log("失败了", reason);
  //   });

  // 等所有promise都执行完,返回成功状态
  Promise.allSettled([promise1, promise2, promise3])
    .then((value) => {
      console.log('成功了', value);
    })
    .catch((reason) => {
      console.log('失败了', reason);
    });

16.9 防抖和节流

// 1、事件防抖 html代码部分
<input type="text" id="myInput" />

<script>
  // 触发事件后,代码延迟执行 实用---> Keyup
  // 持续触发事件,如果一定时间间隔内事件没有再次触发,执行事件逻辑
  // 如果一定时间间隔内容事件再次触发,再次延迟一段事时间执行。多次事件只执行最后一次(无论触发多少次,只执行最后一次)

  //获取元素
  var myInput = document.querySelector('#myInput');
  //事件防抖
  var delay = 1000;
  var timeId = null;
  myInput.addEventListener('keyup', function () {
    //清除掉上次的定时
    clearTimeout(timeId);
    //事件逻辑延迟执行
    timeId = setTimeout(function () {
      console.count();
    }, delay);
  });
</script>
// 2、事件节流 html代码部分
<input type="text" id="myInput" />

<script>
  // 持续触发事件,保证一段时间内只触发一次
  // 两次事件的时间间隔只有达到一定时间才能触发--->首先执行一次,然后在规定时间内执行

  //获取元素
  var myInput = document.querySelector('#myInput');
  //监听事件 事件节流方式,1s之内只能触发一次
  var prev = Date.now(); //上一次触发时间
  var delay = 1000; //最小时间间隔
  myInput.addEventListener('keyup', function () {
    //记录此时的事件
    var now = Date.now();
    //两次事件的时间间隔 >= 1s种才能触发
    if (now - prev < delay) {
      return false;
    }
    //事件的逻辑
    console.count();
    //更新一下时间
    prev = now;
  });
</script>

牛人不是培养出来的,都是自己努力拼搏出来的,靠谁都不如靠自己,自己都不想主动多学习,只期望用一把锤子,就能搞定所有钉子,那你还不如想想怎么买彩票中500万吧,还更实际些,喜欢收藏,不喜勿喷,谢谢 ^_^