前言
今天我们来聊一下JavaScript中的闭包,在进入主题之前,我们先聊一聊调用栈以及作用域链。
调用栈
调用栈一个数据结构,通常以栈的形式组织,用于存储函数的调用信息。
当一个函数被调用时,相关的信息,如函数参数、局部变量和返回地址等,会被压入调用栈的顶部。当该函数执行完毕并返回结果时,相关信息会从栈顶弹出,程序控制流回到调用该函数的地方。
下面我们来看个案例
function greet(name) {
console.log("Hello, " + name + "!");
}
function sayHello() {
let myName = "Tom";
greet(myName);
}
function main() {
sayHello();
}
main();
运行结果为:
我们来分析一下这个代码执行流程
1.main() 函数被调用,将其添加到调用栈中。
调用栈:
- main()
2.在 main() 函数内,我们调用 sayHello() 函数。这将导致 sayHello() 函数被添加到调用栈的顶部。
调用栈:
- main()
- sayHello()
3.在 sayHello() 函数内,我们创建一个局部变量 myName 并将其设置为 "Alice"。然后,我们调用 greet(myName) 函数,将其添加到调用栈的顶部。
调用栈:
- main()
- sayHello()
- greet(myName)
4.在 greet(myName) 函数内,我们使用参数 name 拼接问候语,并将其打印到控制台。完成后,greet(myName) 函数从调用栈中弹出。
调用栈:
- main()
- sayHello()
5.sayHello() 函数执行完毕,也从调用栈中弹出。
调用栈:
- main()
6.最后,main() 函数也执行完毕,并从调用栈中弹出。
调用栈:
(空)
作用域链
作用域链是指在编程语言中,特定变量的可访问性和作用域范围的链式结构。它是用于确定在程序中查找和访问变量时的规则,决定了哪些变量在当前上下文中可见和可用。
下面我们来看个案例
function outerFunction() {
var outerVar = 10;
function innerFunction() {
var innerVar = 20;
console.log(outerVar + innerVar);
}
innerFunction();
}
outerFunction();
运行结果为:
在这个示例中,outerFunction内部包含了outerVar变量,而innerFunction内部包含了innerVar变量。当innerFunction内部尝试访问变量时,它首先查找自己的作用域,如果找不到,就向上查找到外部作用域,即outerFunction的作用域,最终找到了outerVar变量。这就是作用域链的工作原理。
闭包
在js中,根据词法作用域的规则,内部函数总是可以访问其外部函数中声明的变量的,当内部函数被返回到外部函数之外时,即使外部函数执行结束了,但是内部函数引用了外部函数的变量,那么这些变量依旧会被保存在内存中,我们把这些变量的集合称为闭包。
下面我们来看个案例
// 闭包
function foo() {
var myName = '旭旭'
let test1 = 1
let test2 = 2
var innerBar = {
getName: function () {
console.log(test1);
return myName
},
setName: function (newName) {
myName = newName
}
}
return innerBar
}
var bar = foo()
bar.setName('浪哥')
console.log(bar.getName());
运行结果为:
下面我们来分析原因工作流程
1.创建一个foo执行上下文,变量环境中有myName 并被赋值为'旭旭'。词法环境中有test1 并被赋值为 1,test2 并被赋值为 2。innerBar这个对象返回出去赋给变量bar,到这里foo执行上下文就结束了
2.foo执行上下文的旁边还会有一个内存空间用于存放引用出去的变量(test1和myName)
3.我们调用了 setName 方法,并将 '浪哥' 作为参数传递。这会将 myName 的值从 '旭旭' 修改为 '浪哥'。
4.最后,通过 bar.getName(),我们调用了 getName 方法。在这个方法内部,它打印了 test1 的值,然后返回 myName。
闭包优点
变量私有化:是将变量和数据封装在某种程度上,以限制其在程序中的可见性和访问性。
闭包缺点
内存泄漏:是在计算机程序中,分配给程序的内存无法被正常释放或回收,导致程序持续占用更多内存资源,最终耗尽系统的可用内存。
总结
闭包是一种非常有用的编程概念,它可以帮助实现封装、模块化,同时允许在函数之间共享数据等,我相信通过我的讲解,可以使你更进一步了解它。