JS中闭包直接调用外层函数和把函数赋值给变量再调用有区别吗?

摘要

针对一个问题:**JS中闭包直接调用外层函数和把函数赋值给变量再调用有区别吗?**或者说简单点,直接调用函数和把函数赋值后调用一样吗?看如下程序:

注释:fun1()()的意思是执行子函数,但注意,如果想这样执行,函数结构必须是这样,fun1的函数体里要return 子函数(这种多级括号操作不仅在js中,在很多高级语言都可以实现。)

function fun1(){
	var a=2 
	return function fun2(){
		a+=1
		console.log(a)
	}	
}
fun1()()   //结果为3  
fun1()()   //3
var f=fun1()
f()     //3
f()     //4
f()     //5

运行上述js程序,各项结果如注释所示, 执行的两个fun()()或者说无论执行多少次fun()()结果都是3,而如果想将外层函数fun1()赋值给一个变量f后,再次调用f(),发现结果值在依次递增,为什么会出现这种现象?难道fun1()()不等于f()吗?为什么后者累加前者不变呢?
我首先了解了一下js中的函数的赋值和声明问题(以下是用sublime运行的截图,作者太懒,不想编辑代码):

(1)直接声明函数

在这里插入图片描述
一般函数的执行放在声明的后面,可是我先写了bar(),然后声明了函数,发现执行打印没问题,因为:函数的声明会优先读取,无论函数的执行语句放在声明前面还是后面都会正常执行

(2)表达式方法定义函数

在这里插入图片描述
这次首先对函数定义,这种操作其实等同于下面(注意bar后面没有括号):

function bar(){
	console.log("嘤嘤嘤")
}
var f=bar
f()

所以说f()不能再放在函数前面了,否则会报错,即:函数的定义不会优先加载,函数的执行语句必须放在定义的后面,否则会报错

(3)混合研究下:

在这里插入图片描述
第六行的b打印出的结果是1,这就说明var b=bar();这种赋值方式赋值给b的是函数的返回值;而var b = a; 这种赋值方式才是把整个函数赋值过去

回到最初的问题:

function fun1(){
	var a=2 
	return function fun2(){
		a+=1
		console.log(a)
	}	
}
fun1()()   //结果为3  
fun1()()   //3
var f=fun1()
f()     //3
f()     //4
f()     //5

为什么会出现这种现象?难道fun1()()不等于f()吗?为什么后者累加前者不变呢?
在这个程序中,var f=fun1();这一行操作 " f " 获取的是fun1()的返回值(返回值就是fun2函数的地址),而非这个函数本身,我们可以通过调试工具(刚使用sublime,还不知道它怎么玩)观察程序每一遍的运行步骤,如何跳转等。如果不用调试工具,可以这样运行一下程序:(比之前在第3行多加了一个输出a的语句)
在这里插入图片描述
比之前在第3行多加了一个输出a的语句,并且先把第二种赋值输出注释掉,得到了(2 3 2 3)四个运行结果,这说明第一个fun1()()运行了整个函数,第一个输出先a=2,然后在子函数中加了1,出来第二个值3,然后第二个fun1()()执行时,又一次把变量a已经为3的值重置为2了,这样后面再加1还是3,所以无论调用几次fun1()()都会是2 3 2 3…循环下去。
看第二种输出:
在这里插入图片描述
第二种赋值后调用输出,三个f()函数却只输出了4个值(2 3 4 5)说明只有第一个f()运行了2个输出console.log(a),后面两个f()函数直接运行的fun2(),也就是说程序不再经过fun1()下的语句(var a=2)没用重置a的值,所以会一直累加。
在一个闭包中,外部直接调用fun2()是不可能的,可是这种赋值在调用的方式居然只在第一次经过fun1(),如果刨根问底问更深的原因,问什么有这种机制,我只能说我也不知道。(纯属菜鸟的自学)

如果上面的解释读者懂了,那么你也应该知道下面两个问题的答案:

  • 将上面原程序的第二行的 " var "去掉后,运行结果还和之前一样吗?
  • 将第二行放在程序的第一行(函数外)运行结果还一样吗?

解释:

首先应知道var的概念:一段javascript代码中,声明在方法或者对象外部的变量称为全局变量。可以用var修饰,也可以不用。而声明在方法或者对向内部的变量,有var修饰的 就是局部变量 ,没有var修饰的依然是全局变量
显然,两个问题的操作都是把局部变量a变成了全局变量a,但是位置不同,前者仍然在函数fun1()下,后者在函数外,既然都是全局变量,位置不同会有影响吗?当然会的。
仔细分析一下程序就知道了:

问题一:没有变化
在这里插入图片描述
即使变成了全局变量,可是程序运行的顺序依旧如原程序,所以结果没有什么不同。(可以在最外层再设置个全局变量输出,就能看出不同了)

问题二:改变了,结果如图
在这里插入图片描述
同理,函数体中没有了a的重置,全局变量每运行一次都加1,如图中所示:五个输出会一直累加。

另:

在其它语言中该程序也能这么玩:如python:
在这里插入图片描述
出来的结果和源js一样,js程序的变法也一摸一样,不过仔细观察会发现,多了一行:第四行“ nonlocal a ” ,这个是python关键字声明,不多解释,这就引出了另一个问题,不加这一行不行吗?为啥不加会报错?
解释:
这是因为在函数内部对变量赋值进行修改后,该变量就会被Python解释器认为是局部变量而非全局变量,当程序执行到a+=1的时候,因为这条语句是给n赋值,所以n成为了局部变量,那么在执行print(a)的时候,因为n这个局部变量还没有定义,自然就会抛出这样的错误。所以python玩这种不全局还有局部作用域的时候,没赋值语句还好,有的话还必须借助global关键字或者nonlocal关键字,不然根本无法运行,再看一下一个简单的java程序:
变量在函数中是这个样子:
在这里插入图片描述
如果将变量a移到类里面,则下面的a++同样不能直接运行,eclipse提示:不能对非静态字段 a 进行静态引用。当然这和python的情况可能也不太一样,除非前面加上 " static ",变静态然后…(作者java太渣,不深入研究这个了)

总而言之,js中不存在以上问题,一个声明足以。(咳咳:这并不能说js很strong,也不能说那些通用性语言比专用型的strong,语言无优劣,存在即合理,骂它的人越多越伟大)

  • 5
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值