「这是我参与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
}
}
用闭包解决后的代码
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)
}
其实这个问题还有更直接了当的解决方法:把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天,加油!