axios post请求参数,axios本身是封装的吗

  

  本文介绍了js中的一个重要概念——闭包。其实就算是最初级的前端开发者也应该接触过。   

  

  一、闭包的概念和特征。首先,看一个闭包的例子:   

  

  函数makeFab () { let last=1,Current=1 return function inn(){=return last } } let fab=makeFab()console . log(fab())//1 console . log(fab())//2 console . log(fab())//3 console . log(fab())//5这是一个生成make fab的返回值是一个闭包。makeFab就像一个工厂函数。每个调用都会创建一个闭包函数,比如示例中的fab。Fab不需要每次调用都传递参数,但是会返回不同的值,因为它在闭包生成的时候记住了变量last和current,这样在后续的调用中就可以返回不同的值。一个可以记住函数本身作用域的变量,这就是闭包和普通函数的区别。   

  

  MDN中给出的闭包的定义是,一个函数与对其状态的引用即词法环境一起形成一个闭包。这里的“对词法环境的引用”可以简单理解为“对函数外的一些变量的引用”。例如,在上面的示例中,每次调用makeFab都会创建并返回内部函数,该函数引用最后一个和当前变量。   

  

  二、闭包——函数式编程的灵魂JavaScript和python这两种动态语言都强调一个概念:一切都是对象。自然,函数也是对象。在JavaScript中,我们可以像普通变量一样在代码中随意使用函数,然后在某个时候调用它们。这叫做函数式编程。函数式编程灵活简洁,语言对闭包的支持让函数式编程有了灵魂。   

  

  以一个可重用的确认框的实现为例。例如,当用户执行一些删除或重要操作时,为了防止误操作,我们可能会通过弹出窗口要求用户再次确认操作。因为确认框是通用的,所以确认框组件的逻辑要足够抽象,只负责弹出、触发确认和触发取消事件,而触发确认/取消事件是异步操作。这时,我们需要使用两个回调函数来完成操作。弹出函数confirm接收三个参数:提示语句、确认回调函数和取消回调函数:   

  

  函数confirm(确认文本,确认回调,取消回调){//插入提示框dom,里面包含提示语句,确认按钮,取消按钮//添加确认按钮的click事件,在事件中做dom清理并调用confirmCallback //添加取消按钮点击事件,在事件函数中做DOM清理并调用cancelCallback}这样我们就可以通过回调函数根据不同的结果来确认完成不同的动作。例如,如果我们根据id删除一条数据,我们可以这样写:   

  

  删除项目(ID){确认('您确定要删除它吗?',()={//用户点击确认,发送远程ajax请求API.removeitem (id)。然后(XXX)},()={//用户点击取消,console.log ('undelete')}}这个例子中confirmCallback只是利用闭包来创建一个引用上下文中ID变量的函数,试想一下,如果语言不支持闭包,这些变量会做什么?都作为参数传递给confirm函数,然后在调用confirmCallback/cancelCallback时作为参数传递?显然,这里的闭包提供了很大的便利。   

  

  三。闭包的一些例子。防抖和节流功能前端的一个常见需求是远程搜索,根据用户输入框的内容自动发送ajax请求,然后从后端请求回搜索结果。为了简化用户的操作,有时候我们不会专门放置一个按钮点击触发搜索事件,而是直接监控内容变化来搜索(比如vue的官网搜索栏)。此时,为了避免过于频繁的请求,我们可能会使用“防抖”的技术,即当用户停止输入一段时间(例如500ms)后,执行发送请求。你可以写一个简单的防抖函数来实现这个功能:   

  

  函数去抖(func,time) {设定时器=0返回函数(.args){ timer clear time out(timer)timer=setTimeout(()={ timer=0 func。应用(this,args) },time)} }输入。onkey press=de bounce(function(){ console。日志(输入。值)//事件处理逻辑},500)去抖函数每次调用时,都会创建一个新的闭包函数,该函数保留了对事件逻辑处理函数功能以及防抖时间间隔时间以及定时器标志计时器的引用。类似的还有节流函数:   

  

  功能节流   

(func, time) { let timer = 0 // 定时器标记相当于一个锁标志 return function (...args) { if (timer) return func.apply(this, args) timer = setTimeout(() => timer = 0, time) }}2. 优雅解决按钮多次连续点击问题用户点击一个表单提交按钮,前端会向后台发送一个异步请求,请求还没返回,焦急的用户又多点了几下按钮,造成了额外的请求。有时候多发几次请求最多只是多消耗了一些服务器资源,而另外一些情况是,表单提交本身会修改后台的数据,那多次提交就会导致意料之外的后果了。无论是为了减少服务器资源消耗还是避免多次修改后台数据,给表单提交按钮添加点击限制是很有必要的。怎么解决呢?一个常用的办法是打个标记,即在响应函数所在作用域声明一个布尔变量lock,响应函数被调用时,先判断lock的值,为true则表示上一次请求还未返回,此次点击无效;为false则将lock设置为true,然后发送请求,请求结束再将lock改为false。 很显然,这个lock会污染函数所在的作用域,比如在vue组件中,我们可能就要将这个标记记录在组件属性上;而当有多个这样的按钮,则还需要不同的属性来标记(想想给这些属性取名都是一件头疼的事情吧!)。而生成闭包伴随着新的函数作用域的创建,利用这一点,刚好可以解决这个问题。下面是一个简单的例子: 2. 优雅解决按钮多次连续点击问题用户点击一个表单提交按钮,前端会向后台发送一个异步请求,请求还没返回,焦急的用户又多点了几下按钮,造成了额外的请求。有时候多发几次请求最多只是多消耗了一些服务器资源,而另外一些情况是,表单提交本身会修改后台的数据,那多次提交就会导致意料之外的后果了。无论是为了减少服务器资源消耗还是避免多次修改后台数据,给表单提交按钮添加点击限制是很有必要的。

  

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

  

很显然,这个lock会污染函数所在的作用域,比如在vue组件中,我们可能就要将这个标记记录在组件属性上;而当有多个这样的按钮,则还需要不同的属性来标记(想想给这些属性取名都是一件头疼的事情吧!)。而生成闭包伴随着新的函数作用域的创建,利用这一点,刚好可以解决这个问题。下面是一个简单的例子:

  

let clickButton = (function () { let lock = false return function (postParams) { if (lock) return lock = true // 使用axios发送请求 axios.post('urlxxx', postParams).then( // 表单提交成功 ).catch(error => { // 表单提交出错 console.log(error) }).finally(() => { // 不管成功失败 都解锁 lock = false }) }})()button.addEventListener('click', clickButton)这样lock变量就会在一个单独的作用域里,一次点击的请求发出以后,必须等请求回来,才会开始下一次请求。

  

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

  

function singleClick(func, manuDone = false) { let lock = false return function (...args) { if (lock) return lock = true let done = () => lock = false if (manuDone) return func.call(this, ...args, done) let promise = func.call(this, ...args) promise ? promise.finally(done) : done() return promise }}默认情况下,需要原函数返回一个promise以达到promise决议后将lock重置为false,而如果没有返回值,lock将会被立即重置(比如表单验证不通过,响应函数直接返回),调用示例:

  

let clickButton = singleClick(function (postParams) { if (!checkForm()) return return axios.post('urlxxx', postParams).then( // 表单提交成功 ).catch(error => { // 表单提交出错 console.log(error) })})button.addEventListener('click', clickButton)在一些不方便返回promise或者请求结束还要进行其它动作之后才能重置lock的地方,singleClick提供了第二个参数manuDone,允许你可以手动调用一个done函数来重置lock,这个done函数会放在原函数参数列表的末尾。使用例子:

  

let print = singleClick(function (i, done) { console.log('print is called', i) setTimeout(done, 2000)}, true)function test () { for (let i = 0; i < 10; i++) { setTimeout(() => { print(i) }, i * 1000) }}print函数使用singleClick装饰,每次调用2秒后重置lock变量,测试每秒调用一次print函数,执行代码输出如下图:

  

  

可以看到,其中一些调用没有打印结果,这正是我们想要的结果!singleClick装饰器比每次设置lock变量要方便许多,这里singleClick函数的返回值,以及其中的done函数,都是一个闭包。

  

3. 闭包模拟私有方法或者变量“封装”是面向对象的特性之一,所谓“封装”,即一个对象对外隐藏了其内部的一些属性或者方法的实现细节,外界仅能通过暴露的接口操作该对象。js是比较“自由”的语言,所以并没有类似C++语言那样提供私有变量或成员函数的定义方式,不过利用闭包,却可以很好地模拟这个特性。

  

比如游戏开发中,玩家对象身上通常会有一个经验属性,假设为exp,"打怪"、“做任务”、“使用经验书”等都会增加exp这个值,而在升级的时候又会减掉exp的值,把exp直接暴露给各处业务来操作显然是很糟糕的。在js里面我们可以用闭包把它隐藏起来,简单模拟如下:

  

function makePlayer () { let exp = 0 // 经验值 return { getExp () { return exp }, changeExp (delta, sReason = '') { // log(xxx),记录变动日志 exp += delta } }}let p = makePlayer()console.log(p.getExp()) // 0p.changeExp(2000)console.log(p.getExp()) // 2000这样我们调用makePlayer()就会生成一个玩家对象p,p内通过方法操作exp这个变量,但是却不可以通过p.exp访问,显然更符合“封装”的特性。

  

最后送福利了,自己是从事了五年的前端工程师,整理了一份最全面前端学习资料,只要私信:“前端"等3秒后即可获取地址,里面概括应用网站开发,css,html,JavaScript,jQuery,Vue、Ajax,node,angular等。等多个知识点高级进阶干货的相关视频资料,等你来拿

  

四、总结闭包是js中的强大特性之一,然而至于闭包怎么使用,我觉得不算是一个问题,甚至我们完全没必要研究闭包怎么使用。我的观点是,闭包应该是自然而然地出现在你的代码里,因为它是解决当前问题最直截了当的办法;而当你刻意想去使用它的时候,往往可能已经走了弯路。

相关文章