JS前端闭包是什么?私有变量可以用到闭包

04月19日 收藏 0 评论 2 前端开发

JS前端闭包是什么?私有变量可以用到闭包

转载声明:文章来源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随着函数的退出,执行环境销毁,变量回收


C 2条回复 评论
大大大

前端真的不难,后台确实比前台难一点,奥利给。

发表于 2023-11-14 23:00:00
0 0
逍洛

干货满满,很详细,评论占个坑

发表于 2022-11-23 21:00:00
0 0