目录
1.能够在函数外部 访问到内部的变量(原本函数外部无法访问函数内部的局部变量,但函数内部的函数可以访问本函数内的局部变量)
var fnn2 = fn() 本来执行一次内部变量a就应该销毁,但是因为被赋值了有引用关系,局部变量就一直保存
1.产生:闭包在完成嵌套函数(成立的三大要素)时就产生了(而不是调用)
3.解决:f=null ;让闭包机制清除,f指接收外部函数的那个变量
3.节流throttle:减少一段时间的触发频率(规定多久以后才能触发一次)
4.防抖debounce:通过setTimeout的方式,在一的时间间隔内,将多次触发变成一次触发,执行最后一次
一:闭包的三大要素:
1.函数存在嵌套关系
2.内部函数必须引用外部函数的局部变量
3.外部函数必须调用
二:闭包优点:
1.能够在函数外部 访问到内部的变量(原本函数外部无法访问函数内部的局部变量,但函数内部的函数可以访问本函数内的局部变量)
2.延长局部变量的生命周期(本质:)
var fnn2 = fn() 本来执行一次内部变量a就应该销毁,但是因为被赋值了有引用关系,局部变量就一直保存
三: 闭包的缺点
1.滥用闭包,其执行环境一直都在 造成内存溢出 和内存泄漏
四:闭包的生命周期
1.产生:闭包在完成嵌套函数(成立的三大要素)时就产生了(而不是调用)
2.死亡:接收闭包的变量称为垃圾对象时 (null)
五:闭包缺点的解决
1.内存泄漏:内存无法释放
2.内存溢出:内存被撑爆
3.解决:f=null ;让闭包机制清除,f指接收外部函数的那个变量
六:闭包作用域:在函数被创建的地方开始向上寻找
1.函数作为返回值被返回
2.函数作为参数被传递
七:闭包的应用场景
1.做缓存
<script>
function mycache(){
let cache = {}
return {
setN:function(key,value){
cache[key] = value
},
getN:function(key){
return cache[key]
}
}
}
let a = mycache()
a.setN(1,'嘿嘿嘿')
console.log(a.getN(1)) //嘿嘿嘿
// 以下不会正常输出(因为调用了两次 内存中生成了两次cache变量 两次操作的不是同一个)
mycache().setN(2,'嘿嘿嘿')
console.log(mycache().getN(2)) //undefined
</script>
2.节流,防抖:限制函数的执行次数
3.节流throttle:减少一段时间的触发频率(规定多久以后才能触发一次)
①定时器版本
<body>
<button id="btn">节流定时器版本</button>
<script>
let btn = document.getElementById('btn')
function fn(arguments){
console.log(this.innerHTML); //节流定时器版本
console.log(arguments) // PointerEvent {isTrusted: true, pointerId: 1, ...}
}
let delay = 3000
btn.addEventListener('click',throttle(fn,delay))
function throttle(fn,delay){
let timer = true //时间已到
return function(){
if(timer){
setTimeout(()=>{
fn.apply(this,arguments)
timer = true //异步三秒后变为true,可再次点击按钮执行fn
},delay)
}
timer = false //立即为false
}
}
</script>
</body>
②时间戳版本:
<body>
<button id="a">节流时间戳版本</button>
<script>
let a = document.getElementById('a')
function fn(arguments){
console.log(this.innerHTML)
console.log(arguments);
}
let delay = 3000
a.addEventListener('click',throttle(fn,delay))
function throttle(fn,delay){
let time = 0
return function(){
let newtime = new Date().getTime()
if(newtime - time > delay ){
fn.apply(this,arguments)
time = newtime
}
}
}
</script>
</body>
4.防抖debounce:通过setTimeout的方式,在一的时间间隔内,将多次触发变成一次触发,只执行最后一次
注意this指向和参数arguments问题
<body>
纠正this指向及event对象!
<button id="btn">防抖</button>
<script>
let btn = document.getElementById('btn')
let delay = 1000 //延迟时间
// 参数:输出e:触发的鼠标点击事件相关信息,接收arguments
function fn(e){
console.log(this.innerHTML) //防抖
console.log(e) //PointerEvent {isTrusted: true, pointerId: 1, width: 1, …}
}
btn.addEventListener('click',debounce(fn,delay))
function debounce(fn,delay){
let timer = null;
return function(){
// 下一个定时器运行时,清除上一个定时器
if(timer) clearTimeout(timer) //null为false,timer有值才为true
//this:用箭头函数写,this可以指向btn,否则是window
timer = setTimeout(()=>{
//this:修改fn()方法的this指向
fn.apply(this,arguments) //参数:传递arguments,不传是undefined
console.log(this) //<button id="btn">防抖</button>
},delay)
}
}
</script>
</body>
八:闭包面试题
1.不是闭包
The Window
2.是闭包
My Object
3.是闭包(难)
function fun(n, o) {
console.log(o)
return {
fun: function (m) {
return fun(m, n)
}
}
}
var a = fun(0);
// 将0传入n中 n=0 o没传值, 所以 console.log(0)为undefined
//1. 此时 a是一个对象fun函数的返回值
// console.log(typeof a) //object
/* var a = {
fun: function (m) {
return fun(m, n)
}
} */
//2. a.fun是匿名函数 f (m){ return fun(m, n) }
// console.log(typeof a.fun) //function
a.fun(1); //f (1){ return fun(1, n) } n去外层找 n=最开始传入的0
// 根据返回的公式 去找有名函数fun并传入参数n=1 ,o=0 输出console.log(0)为0
a.fun(2); //f (2){ return fun(2, n) } n去外层找 n=最开始传入的0
// 根据返回的公式 去找有名函数fun并传入参数n=2 ,o=0 输出console.log(0)为0
a.fun(3); //f (3){ return fun(3, n) } n去外层找 n=最开始传入的0
// 根据返回的公式 去找有名函数fun并传入参数n=3 ,o=0 输出console.log(0)为0
var b = fun(0).fun(1).fun(2).fun(3);
//1. var b = fun(0)
/* var b = {
fun : fun(m){
return fun(m,0)
}
}
输出: n = 0 o = undefined */
//2. var b = fun(0).fun(1)相当于
/* var b = {
fun:function(1){
return fun(1,n) //n是之前的0 return fun(1,0)
}
}
输出: n = 1 o =0 */
//3. var b = fun(0).fun(1).fun(2)相当于
/* var b = {
fun:function(2){
return fun(2,n) //n是之前的 1
}
}
输出: n = 2 o = 1 */
//4. var b = fun(0).fun(1).fun(2).fun(3)相当于
/* var b = {
fun:function(3){
return fun(3,n) //n是之前的 2
}
}
输出: n = 3 o = 2 */
var c = fun(0).fun(1);
/* var c = fun(0) n=0 o=undefined 先输出一个undefined
相当于一个对象,内部有属性为匿名函数,传入参数m
var c = fun(0).fun(1); 相当于 给匿名函数传参为 1
此时 m = 1 n = 0
*/
c.fun(2);//就是调用return里面属性的匿名函数
// 传入m = 2
// m=2 n=1
c.fun(3);
// 传入m = 3
// m = 3 n= 1