js中的闭包以及闭包的实际运用

function create() {

let res = []

for (var i = 0; i < 5; i++) {

res[i] = function() {

return i

}

}

return res

}

console.log(create()) // 结果: [4,4,4,4,4]

分析:

由于每个函数的作用域中都保存着同一个 函数的活动对象(create 函数), 所以他们引用的都是同一个变量 i.

如何解决这个问题?

我们可以使用匿名函数来创建一个闭包, 让闭包的行为来符合预期

function create() {

let res = []

for (var i = 0; i < 5; i++) {

res[i] = (function(num) {

return function() { return num }

})(i)

}

return res

}

console.log(create()) // 结果: [0,1,2,3,4]

上面将 create 函数进行了一个重写.

1.现在没有直接把闭包赋值给数组,而是再定义一个匿名函数, 将变量 i 当参数传进去由num来接收并立即执行.

2.在每次执行这个匿名函数时都会创建一个属于自己的 活动对象/作用域/变量对象.

3.然后再里面返回一个 只访问 num 的闭包函数, 这时取到的 num 都是独立的值

2.2 闭包的概念和特性

首先看个闭包的例子:

function makeFab () {

let last = 1, current = 1

return function inner() {

[current, last] = [current + last, current]

return last

}

}

let fab = makeFab()

console.log(fab()) // 1

console.log(fab()) // 2

console.log(fab()) // 3

console.log(fab()) // 5

分析:

makeFab的返回值就是一个闭包,makeFab像一个工厂函数,每次调用都会创建一个闭包函数,如例子中的fab。

注意:fab每次调用都不需要传参数,都会返回不同的值,因为在闭包生成的时候,它记住了变量last和current,以至于在后续的调用中能够返回不同的值。

PS:能够记住函数本身所在作用域的变量,这就是闭包和普通函数的区别所在

MDN中给出闭包的定义:函数与其状态即词法环境的引用共同构成闭包

这里的“词法环境的引用”,可以简单理解为“引用了函数外部的一些变量”,例如上述例子中每次调用makeFab都会创建并返回inner函数,引用了last和current两个变量。

2.2 闭包的用途


例子1

以实现一个可复用的确认框为例,比如在用户进行一些删除或者重要操作时,为了防止失误操作,我们可能会通过弹窗让用户再次确认操作。

比如在触发弹窗中的确认/取消事件时异步操作,这时候我们就需要使用两个回调函数完成操作,弹窗函数confirm接受三个参数,一个时提示语,一个是确认回调函数,一个是取消回调函数:

function confirm (confirmText, confirmCallback, cancelCallback) {

// 插入提示框DOM,包含提示语句、确认按钮、取消按钮

// 添加确认按钮点击事件,事件函数中做dom清理工作并调用confirmCallback

// 添加取消按钮点击事件,事件函数中做dom清理工作并调用cancelCallback

}

之前我还一直想不通,原来是闭包在作祟!!!我服了!!!

因为使用闭包,confirm中则包含两个回调函数,那么我们就可以通过confirm传递回调函数,并且根据不同的结果完成不同的动作,比如我们根据id删除一条数据可以这样这:

function removeItem(id){

confirm(‘确认删除吗?’,()=>{

//用户点击确认,发送远程ajax请求

api.removeItem(id).then(xxx)

},

()=>{

//用户点击取消

console.log('取消删除‘)

}

}

在这里插入图片描述

解析:在这个例子中,confirm 的回调函数正式利用了闭包,创建了一个引用上下文id变量的函数!

例子2使用闭包实现防抖、节流函数

前端很常见的一个需求就是远程搜索,根据用户输入框的内容自动发送ajax请求,然后从后端把搜索结果请求回来。

为了简化用户的操作,有时候我们不会专门放置一个按钮来点击触发搜索事件,而是直接监听内容的变化来搜索(比如微信小程序中根据关键字搜索商品)

这时候为了避免请求过于频繁,我们可以利用”防抖“技术,即当用户停止输入一段事件时(比如500ms)后才执行

  • 可以使用如下代码实现

分析:dobounce函数每次调用时,都会创建一个新的闭包函数,该函数保留了对事件逻辑处理函数func以及防抖时间间隔time以及定时器标志timer的引用

例子3—使用闭包实现节流函数

function throttle(func,time){

var lastTime=0;

return function(){

var nowTime=new Date();

if(nowTime-lastTime>time){

func.call(this);

lastTime=nowTime;//由于闭包的作用原理,这里调用外层函数的局部变量,每次调用这个lastTime变量都会发生改变

}

}

}

例子4–使用闭包解决按钮多次连续点击问题

问题:

用户点击一个表单提交按钮时,前端会向后端发送异步请求,请求还没有返回,用户焦急又多点了即此按钮,这样带来的后果就是:①消耗服务器资源②修改了后台的数据

解决:通常办法是定义一个标记变量,即在响应函数所在的作用域声明一个布尔变量lock,响应函数被调用时,先判断lock的值,为true则表示上一次请求还没有返回来,此时点击无效;为false则将lock设置为true,然后发送请求,请求结束后将lock改为fasle

很显然,lock变量会污染函数所在的作用域,而生成闭包伴随着新的函数作用域的创建,利用这一点,刚好可以解决这个问题,下面是简单的例子:

let clickButton = (function () {

let lock = false;

//postParams为发送请求时需要的参数

return function (postParams) {

if (lock) return

lock = true

//使用axios发送请求

axios.post(‘urlxxxxx’, postParams).then(

).catch(error => {

//请求失败

console.log(err)

}).finnally(() => {

//不管失败还是成功,都解锁

lock = false

})

}

})()

说明:这样lock变量就会在一个单独的作用域里,一次点击请求发出以后,必须等请求回来,才会开始下一次请求。

当然,为了避免各个地方都声明lock,修改lock,我们可以把上述逻辑抽象以下,实现一个装饰器,就像节流/防抖函数一样。

最后

你要问前端开发难不难,我就得说计算机领域里常说的一句话,这句话就是『难的不会,会的不难』,对于不熟悉某领域技术的人来说,因为不了解所以产生神秘感,神秘感就会让人感觉很难,也就是『难的不会』;当学会这项技术之后,知道什么什么技术能做到什么做不到,只是做起来花多少时间的问题而已,没啥难的,所以就是『会的不难』。

我特地针对初学者整理一套前端学习资料

前端路线图

说明:这样lock变量就会在一个单独的作用域里,一次点击请求发出以后,必须等请求回来,才会开始下一次请求。

当然,为了避免各个地方都声明lock,修改lock,我们可以把上述逻辑抽象以下,实现一个装饰器,就像节流/防抖函数一样。

最后

你要问前端开发难不难,我就得说计算机领域里常说的一句话,这句话就是『难的不会,会的不难』,对于不熟悉某领域技术的人来说,因为不了解所以产生神秘感,神秘感就会让人感觉很难,也就是『难的不会』;当学会这项技术之后,知道什么什么技术能做到什么做不到,只是做起来花多少时间的问题而已,没啥难的,所以就是『会的不难』。

我特地针对初学者整理一套前端学习资料

[外链图片转存中…(img-WaDqcE2o-1718714276110)]

vue.js的36个技巧

  • 15
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值