JS中的闭包、递归、回调函数的使用

599 阅读4分钟

「这是我参与2022首次更文挑战的第24天,活动详情查看:2022首次更文挑战

大家好,我是 摸鱼小公举,真正的强者,不会怨天尤人,如果想不被别人看轻,你就只有付出比别人多十倍百倍的努力,才能站的比别人更高。上一篇文章是 分享几个项目中常用的js封装 ;今天我们来学习一下JS中的闭包函数、递归函数和回调函数的使用。

闭包函数

简介: 要想搞懂闭包,那么必须要理解JS特殊的变量作用域(全局变量和局部变量)。

JS语言的的特殊之处,就在于函数内部可以访问全局变量

var a=11
function fn(){
console.log(a)
}
fn(); //11

但是函数外部不能访问内部的局部变量

function fn(){
var a=11
}
console.log(a); //error

然后函数内部变量去掉var,则变量提升为全局变量

function fn(){
 a=11
}
fn()
console.log(a) // 11

闭包用途:可以通过闭包访问函数内部的变量,也可以让这些变量始终保持在内存中。

注意:闭包会让函数中的变量都被保存在内存中,内存消费很大,所以不要滥用闭包,不然会造成网页的性能问题,在IE中可能导致内存泄漏;然后解决的办法是在退出函数之前,将不应用的局部变量全部删除

1,用闭包解决变量污染问题

刚开始的代码

var list = document.getElementById("ulDemo").getElementsByTagName("li");
for (var i = 0; i < list.length; i++) {
    var li = list[i];
    li.onclick = function () {
        console.log(i) //  怎么点击都打印出4
    }
}

click.gif

用闭包解决后的代码

var list = document.getElementById("ulDemo").getElementsByTagName("li");
    for (var i = 0; i < list.length; i++) {
        var li = list[i];
        (function(i){
            li.onclick = function () {
              console.log(i); // 0 1 2 3
        }
        })(i)
    }

click1.gif

其实这个问题还有更直接了当的解决方法:把for中的var改成let即可。

2,可以访问函数内部的变量

这里我们要访问的是f1里面的b变量关键的地方有两个,第一个是f1里面的 return b,第二个是n=f1(),然后打印n,如果没有加这两个地方的代码,则访问不了函数内部变量,打印出来是undefind

function fn(){
  let a=1;
  function f1(){
  let b=2;
  console.log(a);
  return b;
}
n=f1();
console.log(n)
};
fn(); // 1 2

递归函数

简介 递归函数就是函数自己调用自己,当满足条件之后才会停止自调用,反之则继续调用。一定要记得加个结束条件,要不然变成死循环。

我们先来看一个累加的例子

function add(num1, num2) {
  var num = num1 + num2;
  if (num2 + 1 > 50) {  //当num2+1大于50返回 num 刚好加到50久停止自调用了
    return num;
  } else {
    return add(num, num2 + 1);
  }
}
console.log(add(1, 2)); // 1275

递归函数不能定义为内联函数。

根据上述代码

var addOne=add
add=null
console.log(addOne(1, 2)) //add is not a function

那么我们来看一下解决方法,用arguments.callee去代替函数名。

在函数内部,有两个特殊的对象:arguments 和 this。其中,arguments 的主要用途是保存函数参数, 但这个对象还有一个名叫 callee 的属性,该属性是一个指针,指向拥有这个 arguments 对象的函数。

function add(num1, num2) {
  var num = num1 + num2;
  if (num2 + 1 > 50) {
    return num;
  } else {
    return arguments.callee(num, num2 + 1);
  }
}
var addOne=add
add=null
console.log(addOne(1, 2)) // 1275

回调函数

简述:在项目中常用到回调函数来做异步操作,个人感觉比async/await还实用。js代码不会一直都是一条线下去执行的,有时候需要等到这个操作完成才能进行下一个操作,这时候就会用到回调函数。

回调函数的优点:可以把通用的逻辑抽出来,避免代码重复,加强代码的可维护性和可读性。

示例一个简单回调函数的例子,如此得知这里的callback参数是可以传一个函数进去的。

 function c(callback) {
   callback(6, 5);
 }
 c((a, b) => {
   //这里回调之后的操作
   console.log(a + b);  //11
 });

项目中实际应用

(1)这里是接口请求成功后,code != 4003并且 传进来的callback必须是个函数才能回调 getTitleList()做异步操作(弹出弹框并更新vuex的数据)

//api接口请求 (通用请求列表接口)
getTitleList( callback ){
  getTitleListData().then((res)=>{
  if(res.code != 4003){
    if(typeof callback ==="function") callback()
  }
  })
}

//弹框的事件调用
showCheckBox(){  
  this.getTitleList(()=>{  
   this.isShowCheckBox =true
   this.SET_IS_EDIT( true )
  })
}

(2)稍微复杂点的回调函数的调用,这里通用方法不仅传了fn(相当于callback)参数,还传了多了一个id来进行判断,而且还把要当参数传的fn函数放在了外面进行一系列操作,这回调函数的运用可以说用得很灵活了。但是回调的逻辑还是跟上面一样,只是多了个参数判断。

submitApply(fn, id) {  //通用方法
   this.$api.student.saveStudent({
	...this.applyData,
	id: id
	}).then((res) => {						
	if (res.code === 1 && res.data.student) {
           this.studentId = [res.data.student.id];
           this.tipShow = false
	   typeof fn === "function" && fn();
	} 
   })
}

formSubmit: function(e) {  //表单提交事件
let fn = ()=>{
//这里是表单校验的一些逻辑代码以及api请求的代码
};
 if (this.num == 1 && this.list.length == 0) {
   if (this.studentId.length > 0) {
     this.submitApply(fn, this.studentId.join(","));
   } else {
   this.submitApply(fn);
 }
} else {
 fn();
 }
}

结语:

好了文章到此就结束了,欢迎大家( 点赞+评论+关注 ) 有问题可以来互相交流一下。希望这篇文章对大家有帮助,也希望大家多多支持我,今天是我参与2022首次更文挑战的第24天,加油!