转载声明:文章来源https://zhuanlan.zhihu.com/p/476843971
什么是闭包?
百度百科定义:闭包就是能够读取其他函数内部变量的函数。创建闭包的通常方式,是在一个函数内部创建另一个函数
function aaa(){
var name = "xxx"
return function bbb(){
alert(name);
}
}
如上代码,bbb函数内可以访问aaa函数作用域内的变量
闭包解决了什么问题?
1. 可以读取函数内部的变量;
2. 让这些变量的值始终保持在内存中。不会在函数调用后被清除;
闭包的定义及其优缺点
闭包是指有权访问另一个函数作用域中的变量的函数,创建闭包的最常见的方式就是在一个函数内创建另一个函数,通过另一个函数访问这个函数的局部变量
闭包的缺点就是常驻内存,会增大内存使用量,使用不当很容易造成内存泄露。
闭包是javascript语言的一大特点,主要应用闭包场合主要是为了:设计私有的方法和变量。
一般函数执行完毕后,局部活动对象就被销毁,内存中仅仅保存全局作用域。但闭包的情况不同!
avascript的垃圾回收原理
(1)、在javascript中,如果一个对象不再被引用,那么这个对象就会被GC回收;
(2)、如果两个对象互相引用,而不再被第3者所引用,那么这两个互相引用的对象也会被回收。
使用闭包的好处
那么使用闭包有什么好处呢?使用闭包的好处是:
1.希望一个变量长期驻扎在内存中
2.避免全局变量的污染
3.私有成员的存在
全局变量
函数可以访问由函数内部定义的变量,如:
function myFunction() {
var a = 4;
return a * a;
}
函数也可以访问函数外部定义的变量,如:
var a = 4;
function myFunction() {
return a * a;
}
计数器困境
设想下如果你想统计一些数值,且该计数器在所有函数中都是可用的。
你可以使用全局变量,函数设置计数器递增:
var counter = 0;
function add() {
return counter += 1;
}
add();
add();
add();
// 计数器现在为 3
但问题来了,页面上的任何脚本都能改变计数器,即便没有调用 add() 函数。
如果我在函数内声明计数器,如果没有调用函数将无法修改计数器的值:
function add() {
var counter = 0;
return counter += 1;
}
add();
add();
add();
// 本意是想输出 3, 但事与愿违,输出的都是 1 !
JavaScript 嵌套函数
所有函数都有权访问全局作用域。
事实上,在 JavaScript 中,所有函数都有权访问它们“上面”的作用域。
JavaScript 支持嵌套函数。嵌套函数可以访问其上的作用域。
在本例中,内部函数 plus() 可以访问父函数中的 counter 计数器变量:
function add() {
var counter = 0;
function plus() {counter += 1;}
plus();
return counter;
}
这样即可解决计数器困境,如果我们能够从外面访问 plus() 函数。
我们还需要找到只执行一次 counter = 0 的方法。
我们需要闭包(closure)。
JavaScript 闭包
还记得函数自我调用吗?该函数会做什么?
var add = (function () {
var counter = 0;
return function () {return counter += 1;}
})();
add();
add();
add();
// 计数器为 3
例子解释
变量 add 的赋值是自调用函数的返回值。
这个自调用函数只运行一次。它设置计数器为零(0),并返回函数表达式。
这样 add 成为了函数。最“精彩的”部分是它能够访问父作用域中的计数器。
这被称为 JavaScript 闭包。它使函数拥有“私有”变量成为可能。
计数器被这个匿名函数的作用域保护,并且只能使用 add 函数来修改。
闭包指的是有权访问父作用域的函数,即使在父函数关闭之后。
技巧1: 用闭包解决递归调用问题
function factorial(num) {
if(num<= 1) {
return 1
} else {
return num * factorial(num-1)
}
}
var anotherFactorial = factorial
factorial = null
anotherFactorial(4) // 报错 ,因为最好是return num* arguments.callee(num-1),arguments.callee指向当前执行函数,但是在严格模式下不能使用该属性也会报错,所以借助闭包来实现
// 使用闭包实现递归
function newFactorial = (function f(num){
if(num<1) {return 1}
else {
return num* f(num-1)
}
}) //这样就没有问题了,实际上起作用的是闭包函数f,而不是外面的函数newFactorial
技巧2:用闭包模仿块级作用域
es6没出来之前,用var定义变量存在变量提升问题:
for(var i=0;i<10; i++){
console.info(i)
}
alert(i) // 变量提升,弹出10
//为了避免i的提升可以这样做
(function () {
for(var i=0; i<10;i++){
console.info(i)
}
})()
alert(i) // underfined 因为i随着函数的退出,执行环境销毁,变量回收
前端真的不难,后台确实比前台难一点,奥利给。
干货满满,很详细,评论占个坑