今天看到了一段关于闭包的代码:
!function(){
var num=1;
var exp={};
function add(num){
return num++;
}
exp.getAddNum=function(){
return add(num);
}
window.a=exp;
}()
console.log(a.getAddNum()); // 1
console.log(a.getAddNum()); // 1
!function(){
var num=1;
var exp={};
function add(){
return num++;
}
exp.getAddNum=function(){
return add();
}
window.a=exp;
}()
console.log(a.getAddNum()); // 1
console.log(a.getAddNum()); // 2
谁能解释下这2段代码的区别吗?考验大家基本功的时候到啦~~~~
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号
第一个里面是你传递进去的,他会使用当前作用域接收到的这个形参的值,它并没有去改变外层num的值,因此你每次用它来传递,值都是1。
而第二个的'add'方法中并没有num变量,他会通过作用域链找到外层的num,那么你这样调用时每次都是操作的外层变量的值,而这个值在你
return之后是会累加的。关于上下文和作用域链你可以看看这篇文章
图解Javascript上下文与作用域
与闭包密切相关的两个概念是作用域链和词法作用域。简单解释下:
作用域链:JavaScript不存在大括号级的作用域,但有函数作用域,在函数内声明的变量在函数外不可见,而在代码块内声明的变量在代码块外是可见的。同时,一个作用域可以访问其内部变量,也可以访问其父级作用域的变量,比如函数内可以访问全局变量。
词法作用域:在声明一个函数的时候就会创建该函数的作用域环境,当其被调用的时候,它可以访问其内部作用域的变量,以及其父级作用域的变量。即便变量是在其之后声明的,一样可以访问。因为记录的是作用域范围,而不是作用域内的具体变量名。
举个例子
全局函数
a的声明是在全局作用域,b的赋值虽然在其后,但仍然可以访问。而局部函数d的声明则是在函数c的局部作用域里面,因此在全局作用域不能访问。这时候闭包的作用就出来了!
来看一个最简单的代码
变量
f虽然是在闭包的局部作用域里,但由于e引用到了返回的匿名函数,而这个匿名函数处在变量f所在的作用域,可以访问之,因此在全局作用域里仍然可以得到f的值。所以,闭包能够有效地减少对全局变量的依赖,并且保护局部变量(不能直接访问局部变量),同时延续局部变量寿命(在上面例子中,变量
e保留了对返回的匿名函数的引用,因而其作用域没有在自调函数运行结束后被回收,变量f也就延续了寿命)。好哒,最后我们来看一看,闭包如何面向对象进行设计。
闭包可以模仿其他面向对象语言的private和public。来举个PHP的例子吧:
那如果是在JavaScript里面我们怎么写呢?
第一,可以直接创建对象
第二,使用构造器
以上是使用JavaScript来设计对象,但
name作为对象的属性,在JavaScript里面是完全暴露的,我们并不想这样。那怎么办?这样,
name变量就完成了对private的模拟,同时返回了具有Getter和Setter作用的对象,可以操作name变量。这么跟你讲,一个养猪场里有一个火腿肠加工厂,猪送进去进行加工最后运出火腿肠,这个加工过程不会对猪场的其他猪产生影响,这个加工厂就叫做 闭! 包!
片段A里
add(num)里操作的是自己的参数……每次调用的时候都是add(1),当然返回也是1.片段B里
add()里操作的才是闭包里那个变量num。你把A里的函数改成
就好理解了
这都是不合理的变量命名造成的混淆……不在于对闭包的理解
红皮书上有一章讲函数的参数是值传递去看下会有帮助
第一个例子中的add方法将num作为形参传递进方法,在该方法的内部实际是该变量的一个副本,因此自增时是不会改变外部num的值的,所以每次调用的结果都为1。 第二个例子中没有将num传递进add方法,那么每次调用add时会从上级的作用域中去寻找该变量。因此add方法内num自增时也就会对外部的num值修改了。
第一个例子中的add方法有混淆视听的作用,add中的参数就是一个形参,还定义成num,迷惑人。
我只想说,这个例子太啰嗦了,要是想对比,下面这样就足够了
上面的代码对应例子A
上面的代码对应例子B
运行代码还是
在第一段代码中,首先
var num = 1为值类型,在调用add(num)时中的实参num复制了var num = 1的值,然后将值传递给定义函数时的命名参数num,在函数调用运行时,这个命名参数num是作为此函数的局部变量运行。在第二段代码中在调用
add()时,此时的var num = 1是作为嵌套函数add外部作用域的变量被使用,形成了闭包,因此结果被累加。可以看看我的这篇文章,执行环境与作用域链以及函数执行
这是理解闭包的前提
关于函数参数传递可以看看这篇文章js中值的访问与参数传递的问题