Hi!这里是面试遇到闭包的JustHappy,想想当时回答的并不好,所以就让我们一起回顾一下吧🚀🚀
简单的来说JS中的闭包就一句话!
对于函数内自由变量的查找,是要在函数定义的地方向外查找,而不是函数调用或者返回的地方查找!!!
概念闭包的定义:闭包是指一个函数和其周围的状态(词法环境)的组合。闭包让你可以从内部函数访问外部函数作用域中的变量,即使外部函数已经执行完毕。
一些概念的补充
什么是作用域
作用域就是变量可以被调用的区域
JS作用域有以下三种:
- 全局作用域:在全局上下文中定义的变量。
- 函数作用域:在函数中定义的变量
- 块级作用域:由
let
和const
声明的变量,其作用域限制在它所在的{}
中
什么是自由变量?
自由变量是指该在函数中未被声明但是被调用,那么我们就会向函数之外的作用域查找该变量,就像下面这样
let a1 = 0
function fn1(){
console.log(a1)
}
自由变量会一层一层的作用域查找,如果到全局作用域还没找到,那就会报错xx is undefine
闭包简单实例
当函数作为返回值
function set(){
const a = 100
return function(){
console.log(a)
}
}
const fn = set()
const a = 200
fn()
// 输出:100
很显然还是那句话:对于函数内自由变量的查找,是要在函数定义的地方向外查找,而不是函数调用或者返回的地方查找!!!
当fn
函数被调用时,它会查找其作用域链中的变量a
。由于fn
函数是在set
函数的作用域中定义的,它会优先查找set
函数内部的变量a
,而不是全局变量a
。
当行数作为参数传递
const p = (fn()) => {
const a = 200
fn()
}
const a = 100
function fn(){
console.log(a)
}
//输出:100
很显然还是那句话:对于函数内自由变量的查找,是要在函数定义的地方向外查找,而不是函数调用或者返回的地方查找!!!
fn
函数访问的是全局变量a
,而不是p
函数内部的a
。这确实体现了作用域链的查找机制,但并不是闭包的典型例子。
闭包实战!手写防抖、节流
防抖
防抖主要是为了防止某一行为被高频触发,其核心是维护一个计时器timer
function debounce(fn,delay){
let timer;
return function(...args){
clearTimeout(timer)
timer = setTimeout(() => fn.apply(this,args),delay)
}
}
// 用法如下:以防止输入触发搜索为例,以下是等用户输入完500毫秒再执行搜索
const handleInput = debounce((e) => { console.log("哈哈你别急,会等你输入完的") } , 500)
document.getElementById("search").addEventListener("input",handleInput)
闭包的作用: timer变量在debounce内部被保存,确保多次触发时候可以清除上一次的定时器
节流
节流是为了限制函数的执行频率,比如说我们写了个监听滚动事件的函数,如果不使用节流,就会导致每下拉1px都会触发一次函数,这显然是没有必要的
节流的核心也是维护一个计时器
function throttle(fn,delay){
let lastTime = 0
return function(...args){
const new = Date.now()
if(now - lastTime > delay){ //计算时间差,如果大于节流限制时间就触发
fn.apply(this,args)
lastTime = now
}
}
}
// 用法:以限制滚动触发为例
const handlScroll = throttle((e) => { console.log("被触发")},1000)
window.addEventListener("scroll", handlScroll)
闭包的作用: 保存lastTime变量