ES6
let和const
- let
- 作用: 与var类似, 用于声明一个变量
- 特点: 在块作用域内有效 不能重复声明 不会预处理, 不存在提升
- 应用: 循环遍历加监听 使用let取代var是趋势
- const
- 作用: 定义一个常量
- 特点: 不能修改 其它特点同let
- 应用: 保存不用改变的数据
- let 定义 变量
- const 定义 常量
- 默认变量使用let定义,今后let使用很多,只有确定不变的量用const 90%以上用let定义的
- 默认变量使用const定义,今后const使用很多,只有确定可变的量用let 90%以上用const定义的 默认就用const定义,只有后面的值发生变化,在改为let
变量的解构赋值
- 理解:
- 从对象或数组中提取数据, 并赋值给变量(多个)
const person = {name: 'jack', age: 18};
const { age, sex, name } = person;
console.log(name, age, sex);
- 对象的解构赋值: 没有顺序关系
let {n, a} = {n:'tom', a:12}
- 数组的解构赋值: 根据顺序一一对应
let [a,b] = [1, 'atguigu'];
- 用途
- 给多个形参赋值
- 对函数参数解构赋值,一个解构赋值语法对应一个参数(与结构赋值语法中多少个变量没有关系)
模板字符串
- 模板字符串 : 简化字符串的拼接
- 模板字符串必须用 `` 包含
- 变化的部分使用${xxx}定义
const person = {name: 'jack', age: 18}; console.log('姓名:' + person.name + ' 年龄:' + person.age); console.log(`姓名:${person.name} 年龄: ${person.age}`);复制代码
对象的简写方法
简化的对象写法
- 省略同名的属性值
- 省略方法的function
- 例如:
let y = 2;let point = { x, y, setX (x) {this.x = x}};复制代码
形参默认值
形参的默认值----当不传入参数的时候默认使用形参里的默认值
function Point(x = 1,y = 2) {this.x = x;this.y = y;}复制代码
默认值:没有传值就使用默认值,传值了就使用传入的值
三点运算符
用途
- rest(可变)参数
- 用来取代arguments 但比 arguments 灵活,只能是最后部分形参参数
function fun(...values) { console.log(arguments); arguments.forEach(function (item, index) { console.log(item, index); }); console.log(values); values.forEach(function (item, index) { console.log(item, index); })}fun(1,2,3);复制代码
- 扩展运算符
let arr1 = [1,3,5]; let arr2 = [2,...arr1,6]; arr2.push(...arr1); function sum(a, ...args) { // ...运算符 取代 arguments console.log(args); // 真数组 console.log(arguments); // 伪数组 }复制代码
箭头函数
-
作用: 定义匿名函数
-
基本语法:
- 没有参数: () => console.log('xxxx')
- 一个参数: i => i+2
- 大于一个参数: (i,j) => i+j
- 函数体不用大括号: 默认返回结果
- 函数体如果有多个语句, 需要用{}包围,若有需要返回的内容,需要手动返回
-
使用场景: 多用来定义回调函数
-
箭头函数的特点: 1、简洁 2、箭头函数没有自己的this,箭头函数的this不是调用的时候决定的,而是在定义的时候处在的对象就是它的this 3、扩展理解: 箭头函数的this看外层的是否有函数, 如果有,外层函数的this就是内部箭头函数的this, 如果没有,则this是window。
// 箭头函数 const fn = () => {}; // 形参只有一个, 可以省略括号 const fn1 = x => { console.log(x); }; // 形参没有或者有多个 const fn2 = (x, y) => {}; console.log(fn1(1)); // 当代码只有一条语法时, 可以省略大括号,会将语句结果作为函数的返回值返回 const fn3 = x => x + 1; console.log(fn3(1)); // 当代码没有或多条语句时 const fn4 = x => { console.log(x); return x + 1; }复制代码
Promise对象
- promise就是一个异步编程的解决方案,用来解决回调地狱问题
- 理解:
- Promise对象: 代表了未来某个将要发生的事件(通常是一个异步操作)
- 有了promise对象, 可以将异步操作以同步的流程表达出来, 避免了层层嵌套的回调函数(俗称'回调地狱')
- ES6的Promise是一个构造函数, 用来生成promise实例
- 使用promise基本步骤(2步):
- 创建promise对象
let promise = new Promise((resolve, reject) => { //初始化promise状态为 pending //执行异步操作 if(异步操作成功) { resolve(value);//修改promise的状态为fullfilled } else { reject(errMsg);//修改promise的状态为rejected }})复制代码
- 调用promise的then()
promise.then(function( result => console.log(result), errorMsg => alert(errorMsg)))复制代码
- promise对象的3个状态
- pending: 初始化状态
- fullfilled: 成功状态
- rejected: 失败状态
- 应用:
-
使用promise实现超时处理
-
使用promise封装处理ajax请求
let request = new XMLHttpRequest();request.onreadystatechange = function () {}request.responseType = 'json';request.open("GET", url);request.send();复制代码
-
new Promise(), 会创建promise实例对象,实例对象内部默认是pending状态(初始化状态)
- resolve() 将promise状态由初始化状态改为 fullfilled 成功的状态
- reject() 将promise状态由初始化状态改为 rejected 失败的状态
- promise状态只能有初始化状态改为成功/失败的状态。不能由成功变成失败或者失败变成成功
-
promise实例对象, then方法
promise.then((result) => {// 当promise对象状态变成成功的状态时,会调用当前函数// 成功的回调函数可以接受resolve方法传入参数console.log('成功的回调函数触发了~');console.log(result);sum(1, 2);}, (error) => {// 当promise对象状态变成失败的状态时,会调用当前函数// 失败的回调函数可以接受reject方法传入参数console.log('失败的回调函数触发了~');console.log(error);})复制代码
Symbol
- 前言:ES5中对象的属性名都是字符串,容易造成重名,污染环境
- 每次调用Symbol函数,返回一个唯一的symbol数据 一般给对象设置唯一的属性。 多了一个数据类型:Symbol
- Symbol: 概念:ES6中的添加了一种原始数据类型symbol(已有的原始数据类型:String, Number, boolean, null, undefined, 对象)
- 特点:
- Symbol属性对应的值是唯一的,解决命名冲突问题
- Symbol值不能与其他数据进行计算,包括同字符串拼串
- for in, for of遍历时不会遍历symbol属性。
- 使用:
- 调用Symbol函数得到symbol值
let symbol = Symbol();let obj = {};obj[symbol] = 'hello';复制代码
- 传参标识
let symbol = Symbol('one');let symbol2 = Symbol('two');console.log(symbol);// Symbol('one')console.log(symbol2);// Symbol('two')复制代码
- 内置Symbol值
- 除了定义自己使用的Symbol值以外,ES6还提供了11个内置的Symbol值,指向语言内部使用的方法。
- Symbol.iterator
- 对象的Symbol.iterator属性,指向该对象的默认遍历器方法(后边讲)
- 调用Symbol函数得到symbol值
Iterator遍历器
- 概念: iterator是一种接口机制,为各种不同的数据结构提供统一的访问机制
- 作用:
- 为各种数据结构,提供一个统一的、简便的访问接口;
- 使得数据结构的成员能够按某种次序排列;
- ES6创造了一种新的遍历命令for...of循环,Iterator接口主要供for...of消费。 工作原理:
- 创建一个指针对象(遍历器对象),指向数据结构的起始位置。
- 第一次调用next方法,指针自动指向数据结构的第一个成员
- 接下来不断调用next方法,指针会一直往后移动,直到指向最后一个成员
- 每调用next方法返回的是一个包含value和done的对象,{value: 当前成员的值,done: 布尔值}
- value表示当前成员的值,done对应的布尔值表示当前的数据的结构是否遍历结束。
- 当遍历结束的时候返回的value值是undefined,done值为false 原生具备iterator接口的数据(可用for of遍历)
- Array
- arguments
- set容器
- map容器
- String
- 。。。
- iterator是一个接口机制,为了让所有数据用统一的方式遍历( for of )
- 通过查看数据类型上是否有Symbol(Symbol.iterator)方法
- String 、Array、Set、Map、 arguments、dom元素集合(querySelectorAll)
- 遍历的方法
- forEach只能数组使用,推荐使用
- for 只能数组使用, 性能最好
- while / do while 任意值使用
- for in 通常用于对象
- for of 当你不确定要遍历的是什么数据类型。这时候用for of
Generator函数
概念: 1、ES6提供的解决异步编程的方案之一 2、Generator函数是一个状态机,内部封装了不同状态的数据, 3、用来生成遍历器对象 4、可暂停函数(惰性求值), yield可暂停,next方法可启动。每次返回的是yield后的表达式结果特点: 1、function 与函数名之间有一个星号 2、内部用yield表达式来定义不同的状态 例如: function* generatorExample(){ let result = yield 'hello'; // 状态值为hello yield 'generator'; // 状态值为generator } 3、generator函数返回的是指针对象(接11章节里iterator),而不会执行函数内部逻辑 4、调用next方法函数内部逻辑开始执行,遇到yield表达式停止,返回{value: yield后的表达式结果/undefined, done: false/true} 5、再次调用next方法会从上一次停止时的yield处开始,直到最后 6、yield语句返回结果通常为undefined, 当调用next方法时传参内容会作为启动时yield语句的返回值。复制代码
function* fn() { console.log('函数开始执行了~'); const flag = true; const result = yield flag ? 123 : 456; console.log(result); console.log('函数执行完了~'); } // 执行generator函数,返回值是一个iterator对象 const iteratorObj = fn(); console.log(iteratorObj); // 通过iterator对象的next方法执行函数体代码(推着函数动一下) const result1 = iteratorObj.next(111); console.log(result1); // {value: 123, done: false} value 看yield后面表达式的值, done 看函数是否执行完毕:没有执行完就是false 执行完了就是true const result2 = iteratorObj.next(222); console.log(result2); // 手动给对象添加iterator接口 console.log(Symbol.iterator); const person = { name: 'jack', age: 18, sex: '男' } Object.prototype[Symbol.iterator] = function* () { for (let key in this) { yield this[key]; } }复制代码
- 案例
/* 需求:请求a数据,再请求b数据,请求c数据 */ function* generator() { console.log('函数开始执行了~'); const result1 = yield setTimeout(() => { console.log('请求回来了a数据'); // 请求成功,让generator继续执行 iteratorObj.next('a数据'); }, 3000); const result2 = yield setTimeout(() => { console.log('请求回来了b数据'); // 请求成功,让generator继续执行 iteratorObj.next('b数据'); }, 2000); const result3 = yield setTimeout(() => { console.log('请求回来了c数据'); // 请求成功,让generator继续执行 iteratorObj.next('c数据'); }, 1000); console.log(result1, result2, result3); console.log('函数执行完毕了~'); } const iteratorObj = generator(); // 为了执行第一个请求 iteratorObj.next();复制代码
async函数
- async函数(源自ES2017 - ES8)
- 概念: 真正意义上去解决异步回调的问题,同步流程表达异步操作 本质: Generator的语法糖
- 语法: async function foo(){ await 异步操作; await 异步操作; }
- 特点:
- 不需要像Generator去调用next方法,遇到await等待,当前的异步操作完成就往下执行
- 返回的总是Promise对象,可以用then方法进行下一步操作
- async取代Generator函数的星号*,await取代Generator的yield
- 语意上更为明确,使用简单,经临床验证,暂时没有任何副作用
- 案例1
async function asyncFn() { console.log('函数开始执行了~'); /* const promise = new Promise((resolve, reject) => setTimeout(() => reject(123456), 2000)); const promise = new Promise((resolve, reject) => setTimeout(reject.bind(null, 123456), 2000)); */ const promise = new Promise((resolve, reject) => setTimeout(resolve.bind(null, 123456), 2000)); // const promise = Promise.resolve(); // await只等promise对象:等promise对象状态由初始化变成成功状态。 // (一旦promise对象状态是初始化状态,一直等。一旦promise对象状态变成成功的状态,就不等了,执行后面代码) // 一旦promise对象状态变成失败的状态,就不执行后面代码(如果捕获了async函数promise的异常,就不报错,没有捕获,就会报错) // result的值就是resolve()传入的参数 const result = await promise; console.log(result); await promise; console.log('函数执行完毕了~'); return 666; } // async函数返回值是一个promise对象: 默认是resolved状态 // 如果函数中有promise对象变成失败的状态,就是rejected状态 const result = asyncFn(); result // 看async函数里面返回值,就是result的值 .then((result) => { console.log(result); }) .catch((error) => { console.log(error); }) console.log(result);复制代码
- 案例2
async function asyncFn() { const result1 = await new Promise((resolve, reject) => { setTimeout(() => { console.log('a数据请求成功了~'); resolve('a数据'); }, 3000) }) const result2 = await new Promise((resolve, reject) => { setTimeout(() => { console.log('b数据请求成功了~'); resolve('b数据'); }, 2000) }) const result3 = await new Promise((resolve, reject) => { setTimeout(() => { console.log('c数据请求成功了~'); resolve('c数据'); }, 2000) }) console.log(result1, result2, result3); return [result1, result2, result3]; } const promise = asyncFn(); promise .then((res) => { console.log(res); // [result1, result2, result3] })复制代码
class类
- 通过class定义类/实现类的继承
- 在类中通过constructor定义构造方法
- 通过new来创建类的实例
- 通过extends来实现类的继承
- 通过super调用父类的构造方法
- 重写从父类中继承的一般方法
// 定义类:构造函数 class Father { // 给实例对象添加属性 constructor(name, age) { this.name = name; this.age = age; } // 给实例对象添加方法 setName(name) { this.name = name; } } // 定义子类继承父类,自动继承父类的属性和方法 // 使用继承必须在constructor函数中调用super方法或者不写constructor class Son extends Father{ // 给实例对象添加属性 constructor(name, age, sex) { super(name, age); // 调用父类的构造方法: constructor this.sex = sex; } // 给实例对象添加方法 setAge(age) { this.age = age; } } console.log(Father.prototype); console.log(typeof Son); const s = new Son('bob', 20, '男'); console.log(s);复制代码
字符串扩展
- String.prototype.includes(str) : 判断是否包含指定的字符串
- String.prototype.startsWith(str) : 判断是否以指定字符串开头
- String.prototype.endsWith(str) : 判断是否以指定字符串结尾
- String.prototype.repeat(count) : 重复指定次数
const str = 'atguigu'; console.log(str.includes('gug')); // true console.log(str.startsWith('atg')); // true console.log(str.endsWith('gu')); // true console.log(str.repeat(3)); // 'atguiguatguiguatguigu'复制代码
数值扩展
- 二进制与八进制数值表示法: 二进制用0b, 八进制用0o
- Number.isFinite(i) : 判断是否是有限大的数
- Number.isNaN(i) : 判断是否是NaN
- Number.isInteger(i) : 判断是否是整数
- Number.parseInt(str) : 将字符串转换为对应的数值
- Math.trunc(i) : 直接去除小数部分
console.log(0o666); // 0 - 7 console.log(0b1010); // 0 - 1 8421法 console.log(Infinity); // 正无穷大 console.log(-Infinity); // 负无穷大 console.log(NaN); // not a number console.log(Number.isFinite(Infinity)); // false console.log(Number.isNaN(NaN)); // true x !== x console.log(NaN === NaN); // NaN不与任何数相等,包括它自身 console.log(Number.isInteger(1.1)); // false console.log(Number.parseInt('123a.123')); // 123 整型:整数 console.log(Number.parseFloat('123.123')); // 123.123 浮点型:小数 console.log(Math.trunc(456.865)); // 456 console.log(Math.floor(456.865)); // 456复制代码
数组扩展
- Array.from(v) : 将伪数组对象或可遍历对象转换为真数组
- Array.of(v1, v2, v3) : 将一系列值转换成数组
- Array.prototype.find(function(value, index, arr){return true}) : 找出第一个满足条件返回true的元素
- Array.prototype.findIndex(function(value, index, arr){return true}) : 找出第一个满足条件返回true的元素下标
// 伪数组对象 const btns = document.querySelectorAll('button'); // 将伪数组转化为真数组 const newBtns1 = Array.from(btns); console.log(newBtns1); const newBtns2 = Array.prototype.slice.call(btns); console.log(newBtns2); const newBtns3 = [...btns]; console.log(newBtns3); console.log(Array.of(1, true, {})); // [1, true, {}] const arr = [{age: 18}, {age: 19}, {age: 20}, {age: 21}]; // const obj = arr.find((item, index, arr) => item.age === 20); const index = arr.findIndex((item, index, arr) => item.age === 20); console.log(index);复制代码
对象扩展
- Object.is(v1, v2)
- 判断2个数据是否完全相等
- Object.assign(target, source1, source2..)
- 将源对象的属性复制到目标对象上
- 直接操作 proto 属性
let obj2 = {};obj2.__proto__ = obj1;复制代码
// 全等运算符的问题 console.log(0 === -0); // true false console.log(NaN === NaN); // false true // Object.is方法解决 console.log(Object.is(1, 1)); // true console.log(Object.is(0, -0)); // false console.log(Object.is(NaN, NaN)); // true//is方法用原生实现 function is(a, b) { /*if (a === b) { // 判断 0 和 -0 的特殊情况 返回 false // 0 和 0 / -0 和 -0 的情况返回 true // return !(-a === b); return !(-1 / a === 1 / b); } else { // 判断 NaN 和 NaN 的特殊情况 return a !== a && b !== b; }*/ // return a === b ? ((-1 / a === 1 / b) ? false : true) : (a !== a && b !== b ? true : false); // return (a === b && (!(-1 / a === 1 / b)) || (a !== a && b !== b); return a === b ? !(-1 / a === 1 / b) : a !== a && b !== b; } console.log(is(0, -0)); // false console.log(is(0, 0)); // true console.log(is({}, {})); // false console.log(is(NaN, NaN)); // true console.log(is(true, true)); // true const obj = {}; const obj1 = {name: 'jack'}; const obj2 = {age: 18}; // 将后面目标对象上的属性和方法复制到源对象上 const result = Object.assign(obj, obj1, obj2); console.log(result === obj);复制代码
深度克隆
- 数据类型:
- 数据分为基本的数据类型(String, Number, boolean, Null, Undefined)和对象数据类型
- 基本数据类型: 特点: 存储的是该对象的实际数据
- 对象数据类型: 特点: 存储的是该对象在栈中引用,真实的数据存放在堆内存里
- 复制数据
- 基本数据类型存放的就是实际的数据,可直接复制 let number2 = 2; let number1 = number2;
- 克隆数据:对象/数组
- 区别: 浅拷贝/深度拷贝 判断: 拷贝是否全部产生了新的数据还是拷贝的是数据的引用
- 知识点:对象数据存放的是对象在栈内存的引用,直接复制的是对象的引用
let obj = {username: 'kobe'}let obj1 = obj; // obj1```复制了obj在栈内存的引用复制代码
- arr.concat(): 数组浅拷贝 2). arr.slice(): 数组浅拷贝
- JSON.parse(JSON.stringify(arr/obj)): 数组或对象深拷贝, 但不能处理函数数据
- 浅拷贝包含函数数据的对象/数组
- 深拷贝包含函数数据的对象/数组
- 区别: 浅拷贝/深度拷贝 判断: 拷贝是否全部产生了新的数据还是拷贝的是数据的引用
- 浅度克隆
let obj3 = Object.assign({}, obj1); obj3.hobby.push('rap'); console.log(obj3); console.log(obj1);复制代码
- 深度克隆
- JSON能实现深度克隆,不能克隆函数数据
let obj1 = {name: 'jack', age: 18, hobby: ['篮球', '唱', '跳'], setName (name) {this.name = name;}};const json = JSON.stringify(obj1);const obj4 = JSON.parse(json);console.log(obj1, obj4);obj4.hobby.push('rap');console.log(obj1, obj4);复制代码
// 检查数据类型 function checkType(target) { return Object.prototype.toString.call(target).slice(8, -1); }复制代码
// 深度克隆: 深度克隆所有数据 function deepClone(target) { // 因为不确定克隆的是什么数据,但是能确定的是一定是引用数据类型 let result = null; // 检查数据的类型 const type = checkType(target); // 判断数据的类型,如果是对象/数组就处理,不是就直接返回 if (type === 'Object') { result = {}; } else if (type === 'Array') { result = []; } else { // 其他类型就直接返回 return target; } // for in 即能遍历对象也能遍历数组 for (let key in target) { // 获取属性值 const value = target[key]; // 将克隆的值作为新对象的某个属性的值 // const newValue = deepClone(value); // result[key] = newValue; result[key] = deepClone(value); } return result; } const person = {name: 'jack', age: 18, hobby: ['篮球', '唱', '跳'], sex: { option1: '男', option2: '女' }, setName (name) {this.name = name;}}; const newObj = deepClone(person); newObj.hobby.push('rap'); console.log(person, newObj);复制代码
Set和Map数据结构
- Set容器 : 无序不可重复的多个value的集合体
- Set()
- Set(array)
- Set.prototype.add(value) 给set容器添加一个值
- Set.prototype.delete(value) 删除一个
- Set.prototype.has(value)
- Set.prototype.clear() 清空所有
- size
- Map容器 : 无序的 key不重复的多个key-value的集合体
- Map()
- Map(array)
- set(key, value)//添加
- get(key)
- delete(key)
- has(key)
- clear()
- size
const arr = [2, 5, 8, 5, 1, 4, 8]; const s1 = new Set(arr); console.log(s1); // 数组去重 console.log([...new Set(arr)]); s1.add(9); console.log(s1); console.log(s1.has(9)); s1.clear(); console.log(s1); // 无序的 key不重复的多个key-value的集合体 const array = [1, 2, 3]; const m1 = new Map([[{name: 'jack'}, function fn() {}], [array, true], [array, false]]); console.log(m1);复制代码
ES7(常用的)
- 指数运算符(幂): **
- Array.prototype.includes(value) : 判断数组中是否包含指定value
console.log(3 ** 3);//27const arr = [1, 2, 5, 6, 8];console.log(arr.includes(3));//true复制代码
ES8(常用的)
==async函数也是ES8中提出的==
- Object.values()
- Object.entries()
- Object.keys()
const person = {name: 'jack', age: 18}; // [['name', 'jack'], ['age': 18]] // 提取对象中所有属性名,作为一个数组返回 console.log(Object.keys(person)); // 提取对象中所有属性值,作为一个数组返回 console.log(Object.values(person)); console.log(Object.entries(person)); // [['name', 'jack'], ['age': 18]]复制代码
ES9(常用的)Promise.finally
const promise = new Promise((resolve, reject) => setTimeout(reject, 1000)); promise .then(() => { console.log('then'); }) .catch(() => { console.log('catch'); }) .finally(() => { // 不管成功还是失败都会触发 console.log('finally'); })复制代码
ES10(常用的)
- Array扩展方法
- 数组降维、数组的扁平化
// 数组降维、数组的扁平化 const arr = [[1, 2], [[[3]]], [[4], [[[[5]], 6]]]]; console.log(arr.flat(1)); console.log(arr.flat(Infinity));/* 1. flat 全部降成1维 2. 可以传参,根据参数降维 flat方法的实现源代码 Array.prototype.flat = function (num) { // this 就指向要处理的数组 --> arr.flat() let result = []; /!*this.forEach((item, index) => { if (Array.isArray(item)) { result = result.concat(item.flat()); } else { result.push(item); } })*!/ this.forEach((item) => Array.isArray(item) ? result = result.concat(item.flat()) : result.push(item)); return result; } Array.prototype.flat = function (num) { // this 就指向要处理的数组 --> arr.flat() num--; if (num < 0) return this; let result = []; /!*this.forEach((item, index) => { if (Array.isArray(item)) { result = result.concat(item.flat()); } else { result.push(item); } })*!/ this.forEach((item) => Array.isArray(item) ? result = result.concat(item.flat(num)) : result.push(item)); return result; }*/复制代码
- 动态import
// 按需加载 document.getElementById('btn').onclick = function () { // 只会加载一次 import('./a.js'); }复制代码
函数节流和函数防抖
- 节流函数
// 绑定滚轮事件 // 需求:滚轮事件发现单位时间内触发回调函数的次数太多,性能不好 // 解决:让函数调用次数更少 // 节流函数:在单位时间内让函数只调用一次 document.onscroll = throttle(function (e) { console.log('滚轮事件触发了~'); console.log(e); console.log(this); }, 1000) // 节流函数 function throttle(fn, time) { // 开始时间 let startTime = 0; // 实际上下面函数就是DOM事件回调函数 return function () { // 结束时间: 调用当前函数的时间 const endTime = Date.now(); // fn函数的this指向问题,参数有问题(少event) if (endTime - startTime >= time) { // 大于1s, 可以触发, 小于1s就不触发 fn.apply(this, arguments); // 重置开始时间 startTime = endTime; } } }复制代码
- 防抖函数
// 绑定滚轮事件 // 需求:滚轮事件发现单位时间内触发回调函数的次数太多,性能不好 // 解决:让函数调用次数更少 // 节流函数:在单位时间内让函数只调用一次,是第一次生效 // 防抖函数:在单位时间内让函数只调用一次,是最后一次生效 document.onscroll = debounce(function (e) { console.log('滚轮事件触发了~'); console.log(e); console.log(this); }, 1000) // 防抖函数 function debounce(fn, time) { let timerId = null; // 实际上下面函数就是DOM事件回调函数 return function () { clearTimeout(timerId); const args = arguments; timerId = setTimeout(() => { // fn函数的this指向问题,参数有问题(少event) // 大于1s, 可以触发, 小于1s就不触发 fn.apply(this, args); }, time); } }复制代码