1.1 防抖
javascript
const ipt = document.querySelector('input')
ipt.addEventListener('keyup', function(){
console.log('发送请求了')
})
// 防抖:防止抖动, 你先抖动着, 啥时候停了,再执行下一步
// 我们想要的一个效果应该是什么样子?
// 1. 当我们键盘抬起的时候,不会立马触发这个回调函数, 我们给一个延迟发送的等待时间
// 2. 应该是在等待某个延迟时间之后再触发,比如设定300ms
// 3. 如果在这个300ms之内,又触发了keyup事件,那么就重新计时,重新等待300ms
// 回城 : b ==> 10s城, 如果这中间又按了一下这个键,又重新计时
const sendMsg = function(){
console.log('发送请求了')
}
ipt.addEventListener('keyup', sendMsg)
防抖:debounce 当持续的触发某个事件的时候,一定时间内没有再触发事件,回调函数才会执行一次。 如果我们的等待时间到来之前,又触发了这个事件,重新计时
javascript
// ==> 你先抖动着, 啥时候停了,再执行下一步
const ipt = document.querySelector('input')
const debounce = (fn, ms) => {
console.log(2222)
return function(){
// 控制sendMsg什么时候执行
console.log(333)
}
}
// 需求:让sendMsg这个函数,等待ms的时间才执行
// 可以封装一个debounce防抖函数来操作这个sendMsg,让他500ms后执行
// 在防抖debounce里面去控制什么时候执行sendMsg
ipt.addEventListener('keyup', debounce(sendMsg, 300))
02 debounce 函数中的return
javascript
const input = document.querySelector('input')
const sendMsg = function(){
console.log('发送请求了')
}
// 1. return function外面,绑定keyup事件的时候,就立即执行了
// 因为加了小括号 debounce(sendMsg, 300)
// 2. return function里面的代码, 触发事件的时候才执行,没触发一次,执行一次
const debounce = (fn, ms = 0) => {
console.log(1111)
return function(){
console.log(2222)
}
}
input.addEventListener('keyup', debounce(sendMsg, 300))
// input.addEventListener('keyup', function(){
// console.log(2222)
// })
03 简版的防抖
javascript
<input type="text">
<script>
const input = document.querySelector('input')
const sendMsg = function(){
console.log('发送请求了')
}
// 需求:
// 1. 触发keyup 延迟300ms之后,才执行回调函数
// 2. 如果等待的300之间,又按下了键, 重新计时 setTimeout
const debounce = (fn, ms = 0) => {
// 以后这里的代码还执行不?
// 要保证程序中,只能由一个定时器
let timerId
return function(){
// let timerId // 变量如果放到这里,每次都重新声明,每次都是undefined
// 怎么清除定时器?
clearTimeout(timerId) // undefined
// 开启一个定时器 等待300ms之后,调用fn
timerId = setTimeout(function(){
fn()
}, ms)
}
}
// input.addEventListener('keyup', debounce(sendMsg, 600))
// input.addEventListener('keyup', function(){
// // let timerId // 变量如果放到这里,每次都重新声明,每次都是undefined
// // 怎么清除定时器?
// clearTimeout(timerId) // undefined
// // 开启一个定时器 等待300ms之后,调用fn
// timerId = setTimeout(function(){
// fn()
// }, ms)
// })
// 简版的防抖
function debounce(fn, ms = 0){
// 1. 先声明一个变量,接收定时器的number
let timerId
return function(){
// 2. 先清除定时器
clearTimeout(timerId)
// 3. 开启定时器
timerId = setTimeout(function(){
fn()
}, ms)
}
}
input.addEventListener('keyup', debounce(sendMsg, 300))
04 防抖中的this指向
javascript
<input type="text">
<script>
const input = document.querySelector('input')
const sendMsg = function(){
// console.log(this)
console.log('发送请求了')
}
// 1, 一开始 this指向input
// 2. 写了防抖之后,this指向了window
const debounce = (fn, ms = 0) => {
let timerId
return function(){
clearTimeout(timerId)
timerId = setTimeout(() => {
// 改变this指向
fn.call(this)
}, ms)
}
}
// 当我们使用了debounce之后,原来操作的回调函数,this指向改变了呀
input.addEventListener('keyup', debounce(sendMsg, 300))
05 debounce传参
javascript
const input = document.querySelector('input')
const sendMsg = function(x, y){
console.log(this)
console.log(x + y)
console.log('发送请求了')
}
// 正常调用回调函数如何传参?
// 函数名加括号表示立即执行,调用函数
// input.addEventListener('keyup', sendMsg(1,2))
input.addEventListener('keyup', sendMsg.bind(input, 1,2))
// input.addEventListener('keyup', function(x, y){
// console.log(this)
// console.log(x + y)
// console.log('发送请求了')
// })
// input.addEventListener('keyup', undefined)
// 2. 如果要用防抖,如何传参
const debounce = (fn, ms = 0) => {
let timerId
return function(...args){ // 剩余参数,真数组
// console.log(x, y, z)
clearTimeout(timerId)
// 1. setTimeout外 this指向的this input绑定事件的元素
timerId = setTimeout(() => {
// 2. setTimeout内,如果改成箭头函数了,this也指向input
fn.call(this, ...args)
}, ms)
}
}
input.addEventListener('keyup', debounce(sendMsg, 300).bind(input, 1, 2, 3))
完整版
javascript
const debounce = (fn, ms = 0) => {
let timerId
return function(...args){
clearTimeout(timerId)
timerId = setTimeout(() => {
fn.apply(this, args)
}, ms)
}
}
// 1. 掌握function的return 函数的返回值
// 2. 一般情况的事件绑定,如何传参 ==> sendMsg.bind(input, 1, 2)
// 3. 防抖,如何传参 ==> debounce(sendMsg, 300).bind(input, 1, 2, 3)
// 4. 防抖传参进入函数之后,如何接收
1.2 节流 throttle
持续触发的事件,在一段事件内只允许函数执行一次
javascript
.box{
width: 500px;
height: 500px;
background-color: #ccc;
color: #fff;
text-align: center;
line-height: 500px;
font-size: 100px;
}
javascript
const box = document.querySelector('.box')
let i = 0
const move = (x, y) => {
i++
box.innerHTML = i
}
// box.addEventListener('mousemove', move)
// 节流:throttle
// 持续触发的事件,在一段事件内只允许函数执行一次
// ===> 减少事件在一段时间内的执行频率
// - 用场景
// 1. 浏览器窗口缩放 resize事件
// 2. scroll滚动事件, mousemove事件
// 3. 拖拽事件等
// 需求:想要每隔500ms,执行一次move函数, 用throttle节流函数,控制它的变化
// 可以声明一个开始时间,用当前时间 减去 开始时间 , 如果 >= 500 ms 就执行一次
const throttle = (fn, ms = 0) => {
let start = 0
return function(...args){
let now = Date.now()
// let now = Date.now() // +new Date() new Date().getTime()
if (now - start >= ms){
// fn.call(this, ...args)
fn.apply(this, args)
// 当函数执行完之后,让start等于当前的时间戳
start = Date.now()
}
}
}
// box.addEventListener('mousemove', throttle(move, 300).bind(box, 1, 2))
定时器的写法- 节流 throttle
javascript
<div class="box"></div>
<script>
const box = document.querySelector('.box')
let i = 0
const move = () => {
i++
box.innerHTML = i
}
// box.addEventListener('mousemove', move)
// 每个300ms 执行一次move函数
// 定时器的方式实现?==> setTimeout
const throttle = (fn, ms =0) => {
let timerId // undefined
return function(...args){
// 只让有一个定时器存在, 不要再开启一个新的了
if (timerId) return
// 1
timerId = setTimeout(() => {
fn.call(this, ...args)
timerId = null
}, ms);
}
}
box.addEventListener('mousemove', throttle(move, 600))
javascript
Something went wrong checking access: getaddrinfo ENOTFOUND api.github.com
1.3 防抖节流总结
https://www.30secondsofcode.org/js/s/debounce 目前看见最简写法,best!
防抖和节流的区别
- 防抖:减少执行次数,多次密集的触发只执行一次
- 节流:减少执行频率,有节奏的执行
- 防抖关注结果,节流关注过程
javascript
// 面试 ? 什么是防抖 什么是节流, 有实际使用过吗?自己封装过防抖或者节流函数吗?
// 1.
// 防抖
// 你先抖动着,什么时候停了,我再执行
// ==> 将多次密集的执行,合并为一次
// 节流
// => 减少事件触发的频率
const sendMsg = function(){
console.log('发送请求')
}
// 2. 应用场景
// 防抖的应用场景: 搜索框,不断的输入文字,如果每次输入一个文字就发一个请求,相当消耗性能
// 节流的应用场景:
// 1. scroll滚动事件
// 2. mousemove鼠标移动事件
// 3. 窗口的resize事件等等
// 3. 有没有自己封装或者写过? ==> 好一点的公司会问,大厂才会手写
const debounce = (fn, delay) => {
// 1. return外面 ,立即执行,只执行一次
let timerId
return function(...args){
// 2. return里面,每次触发就执行
clearTimeout(timerId)
// 这里的this指向input
timerId = setTimeout(() => {
console.log(this)
fn.call(this, ...args)
})
}
}
// 4. let timerId 为什么要放到return的外面, 如果放到里面
// 5. 为什么改成箭头函数了?箭头函数没有this,箭头函数中的this,就是function里面的this
// 6. bind后面传的参数,给了谁? fn.bind(obj, 1, 2, 3) ==> 给了return后面的哪个function
// 7. 接收参数的方式,1. 剩余参数, 2. arguments
// 7.1 如果用rest剩余参数接收 (...args) ==> 内部如果使用call改变this指向
// 需要把args这个真数组,展开成参数列表
// 7.2 如果使用arguments来接收参数, 那么也是可以的,注意arguments是一个伪数组
const debounce2 = (fn, delay) => {
let timerId
return function(){
clearTimeout(timerId)
timerId = setTimeout(() => {
console.log(this)
fn.call(this, ...arguments)
})
}
}
// 8. 改变this指向,还可以使用apply改变
const debounce3 = (fn, delay) => {
let timerId
return function(...args){
clearTimeout(timerId)
timerId = setTimeout(() => {
fn.apply(this, args)
})
}
}
const debounce4 = (fn, delay) => {
let timerId
return function(){
clearTimeout(timerId)
timerId = setTimeout(() => {
fn.apply(this, arguments)
})
}
}
// 防抖,如果不改变this,不传参数,就这么调用,就欧克了
input.addEventListener('keyup', debounce(sendMsg, 300))
// 防抖,如果要改变this,并且传参数,使用bind来调用,改变this指向,并且传参数,
// ===> 为什么使用bind,bind不是立即执行的,而是返回一个函数
input.addEventListener('keyup', debounce(sendMsg, 300).bind(input, 1, 2))
节流
javascript
// 节流 减少执行的频率
// 定时器的写法
const throttle = (fn, ms = 0) => {
let timerId
return function(...args){
// 保证当前只有一个定时器,如果触发事件,不要执行后面代码
if (timerId) return
timerId = setTimeout(() => {
fn.apply(this, args)
// 什么时候让可以重新开启定时器
timerId = null
})
}
}
// 节流, 时间戳的写法
const throttle2 = (fn, ms = 0) => {
let start = 0
return function(...args){
// 判断当前时间 - 开始时间 >= ms ,就执行一次函数
let now = Date.now()
if (now - start >= ms) {
fn.call(this, ...args)
start = Date.now()
}
}
}
09 lodash实现节流防抖
javascript
.box{
width: 500px;
height: 500px;
background-color: #ccc;
color: #fff;
text-align: center;
line-height: 500px;
font-size: 100px;
}
javascript
<div class="box"></div>
<script src="./js/lodash.min.js"></script>
<script>
const box = document.querySelector('.box')
let i = 0
const move = (x, y) => {
i++
box.innerHTML = i
}
// box.addEventListener('mousemove', move)
box.addEventListener('mousemove', _.debounce(move, 300))
box.addEventListener('mousemove', _.throttle(move, 300))
</script>
2. 类Class
2.1 创建对象
javascript
// 1. 字面量
// 2. new Object
// 3. 构造函数 ===> class 类
// 类:抽象的,泛指的,一个大类, 描述了一类对象的属性和方法
// 实例/对象 : 具体的某一个
const obj = { name:'练练' }
const obj2 = new Object({ name:'练练' })
function Star(name, age){
this.name = name
this.age = age
}
const ll = new Star('练练', 18)
console.log(ll)
11 使用类创建对象
javascript
// 使用类创建对象 创建了一个明星类
class Star {
// 类的公共属性,放到constructor方法里面
constructor(name, age){
this.name = name
this.age = age
}
}
// 类 class就是一个语法糖,是构造函数的语法糖
const ll = new Star('练练', 18)
console.log(ll)
// ==> 通过class关键字创建类,类名默认首字母大写
// 1. constructor()是类的默认方法,只要通过new创建一个实例,就会执行里面的代码
// 2. 一个类必须有constructor(),如果不写,一个空的constructor会默认添加
// 3. constructor可以接收传递过来的参数,默认返回实例对象this
// 4. 类必须使用new调用,否则会报错
12. 类添加公共方法
javascript
// function Star(name,age){
// this.name = name
// this.age = age
// }
// Star.prototype.sing = function(){}
// Star.prototype.dance = function(){}
class Star {
// 1.类的公共属性,放到constructor方法里面
// constructor => 名字叫做构造函数
constructor(name, age){
this.name = name
this.age = age
}
// 2. 类面的所有函数(方法)都不需要写function
sing(song){
console.log(song)
}
// 3. 多个方法之间,不需要用逗号隔开
dance(){
console.log('跳舞')
}
}
const ldh = new Star('刘德华', 20)
//
// sing这些公共方法,实际上定义在原型上,
// 相当于是 ==> Star.prototype.sing 上面定义的方法
13. 类里面的this指向
javascript
class Star {
constructor(name, age){
this.name = name
this.age = age
// 1. constructor中,this指向 new出来的实例对象
console.log(this)
}
sing(song){
// 2. 类的方法里面,this指向 new出来的实例对象
console.log(song)
// console.log(song) ==> 这个方法,调用后,没有返回值的,表达式的值就是undefined
}
dance(){
console.log('跳舞')
}
}
const ldh = new Star('刘德华', 20)
console.log(ldh.sing('哈哈哈'))
14. 类的本质
javascript
function Person(){
}
console.log(typeof Person)
// class类是构造函数的语法糖
class Star {
dance(){
console.log('跳舞')
}
}
console.log(typeof Star)
// 1. 类的本质就是一个函数,我们可以简单的认为,类就是构造函数的语法糖
// 2. 类有原型,(原型对象)
console.log(Star.prototype)
// 3. 既然类有原型,我们也可以在原型上添加公共方法,但是呢,不推荐
Star.prototype.sing = function(){
console.log(123)
}
// 4. 实例的隐式原型,指向构造函数(class类)的显示原型
const ll = new Star()
console.log(ll.__proto__ === Star.prototype)
15. 类的继承
javascript
// 继承: 子类拥有或者说继承了父类的属性和方法
// ES6 ==> 类的继承 ==> 关键字 extends继承
// ===> extends 是 寄生式组合继承的语法糖
// 定义一个父类
class Animal {
constructor(name, age){
this.name = name
this.age = age
}
run(){
console.log('奔跑')
}
}
// 定义一个子类,继承自父类
class Dog extends Animal {
// 子类中没有些父类的constructor中的属性
// 子类中也没有些方法(继承了父类的方法)
}
const dog = new Dog('wangcai', 3)
console.log(dog)
dog.run()
16. super关键字
javascript
// 定义一个父类
class Father {
constructor(){}
}
// 定义一个子类继承自父类
class Son extends Father {
constructor(){
// 1. 如果子类里面写了constructor方法, 那么这个方法里面必须还要调用super()
super()
}
}
// 2. 如果子类没有写constructor方法,那么子类会默认添加constructor方法,
// 并且内部还会自动调用super()
class Son extends Father {
// constructor(){
// super()
// }
}
const p = new Son()
17. super
javascript
// super关键字用于访问和调用父类上的函数
// 可以调用父类的构造函数constructor , 也可以调用父类的普通函数
class Father {
constructor(x, y){
this.x = x
this.y = y
console.log(x, y)
}
// 如果想要在类的方法中,访问x,y这些属性,必须加this!
sum(){
console.log(this.x + this.y)
}
}
// 定义一个子类继承父类Father
class Son extends Father {
// son本身是一个类
// 1. super() 代表的就是父类的constructor()构造函数
constructor(x, y){
console.log(x, y)
super(x, y) // 表示调用父类的constructor(x, y)
}
}
const p = new Son(1, 2)
console.log(p)
18 .super 必须在this之前调用
javascript
// super关键字用于访问和调用父类上的函数
// 可以调用父类的构造函数constructor , 也可以调用父类的普通函数
class Father {
constructor(x, y){
this.x = x
this.y = y
console.log(this)
console.log(x, y)
}
// 如果想要在类的方法中,访问x,y这些属性,必须加this!
sum(){
console.log(this.x + this.y)
}
}
// 定义一个子类继承父类Father
class Son extends Father {
// son本身是一个类
// 1. super() 代表的就是父类的constructor()构造函数
constructor(x, y, color){
// 2. super() 必须放在this之前,先继承父类的一些属性,再写自己的属性
// 3. Q:super中的this指向谁?指向的是子类的实例
super(x, y) // constructor()
this.color = color
}
// 减法
minusFake(){
// 想要在这里调用父类的那个sum方法
// 4.super还可以作为对象来使用,调用父类的方法
super.sum()
// console.log(1123)
}
}
const p = new Son(1, 2, 'orange')
console.log(p)
p.minusFake()
// 1. super() 作为方法直接使用,表示的是父类的constructor()这个方法
// 2. super作为对象来使用,可以调用父类的方法 super.sum()
// 3. super必须写在this前面
19 面试准备
javascript
// 1. CSS面试题
// 1. 盒模型
// 2. BFC ==> 掘金上搜一搜 Block Format Context 块级格式化上下文,
/// 就是一个独立的空间,内外互不影响
// 3. 回流/重排(宽高结构变化了) 另一个叫重绘(颜色等一些)
// 4. position sticky ==> 粘性定位
// 5. 水平垂直居中 6个
// 6. flex 0 1 auto;
// JS / API 面试题
// 1. DOM事件流
// 2. addEventListener('click', cbFn, useCapture)
// 3. 事件委托
// 4. 定时器 ==> 事件循环(还没讲)
// JS高级
// 1. 作用域链
// 2. 垃圾回收 ==> GC , 回收策略
// 3. 闭包 什么是闭包,它的应用,实际开发中用过了 防抖节流(return)
// 4. this指向! this在定义的时候不能确定指向,执行的时候才确定,但是箭头函数例外
// 5. 创建对象的三种方式
// 6. 数组、 字符串、Number等方法
// 7. 原型 / 原型链的理解 / 熟悉画图
// 8. 继承的实现方式 7 / 原型链 ===> 寄生式组合继承 ES6 ==> extends
// 9. 防抖,节流是什么? 应用场景, 可不可以手写
// 10. ==> 数据类型、堆栈存储,深浅拷贝
// 11. 改变this的指向三种方式,区别?
// 12. var let const 区别
// 13. 检测数据类型的方式
// 14. new的执行过程
// 15. 伪数组转真数组
// 16. 判断是否是数组
// 1. Github / Gitee 注册
// 2. 防抖节流
// 3. 面试题,准备面试了~~~