在上面的例子中 , foo()
执行完毕之后 , 按照常理 , 其执行环境生命周期会结束 , 所占内存被垃圾收集器释放 。 但是通过fn = innerFoo
, 函数innerFoo
的引用被保留了下来 , 复制给了全局变量fn
。 这个行为 , 导致了foo的变量对象 , 也被保留了下来 。 于是 , 函数fn在函数bar内部执行时 , 依然可以访问这个被保留下来的变量对象 。 所以此刻仍然能够访问到变量a的值 。
这样 , 我们就可以称foo为闭包 。
所以 , 通过闭包 , 我们可以在其他的执行上下文中 , 访问到函数的内部变量 。 比如在上面的例子中 , 我们在函数bar的执行环境中访问到了函数foo的a变量 。 个人认为 , 从应用层面 , 这是闭包最重要的特性 。 利用这个特性 , 我们可以实现很多有意思的东西 。
不过读者朋友们需要注意的是 , 虽然例子中的闭包被保存在了全局变量中 , 但是闭包的作用域链并不会发生任何改变 。 在闭包中 , 能访问到的变量 , 仍然是作用域链上能够查询到的变量 。
对上面的例子稍作修改 , 如果我们在函数bar中声明一个变量c , 并在闭包fn中试图访问该变量 , 运行结果会抛出错误 。
var fn = null;function foo() {
var a = 2;
function innnerFoo() {
console.log(c); // 在这里 , 试图访问函数bar中的c变量 , 会抛出错误
console.log(a);
fn = innnerFoo; // 将 innnerFoo的引用 , 赋值给全局变量中的fnfunction bar() {
var c = 100;
fn(); // 此处的保留的innerFoo的引用foo();bar();
闭包形成的条件
- 函数嵌套
- 内部函数引用外部函数的局部变量
初级前端了解以上闭包面试即可 , 以下内容属于中高级前端理解范围 。 大家可以暂时跳过 , 等学有一定基础积累 , 再回头看看 。
除了面试 , 在实践中 , 闭包有两个非常重要的应用场景 。 分别是模块化与柯里化 。
柯里化
模块化
在我看来 , 模块是闭包最强大的一个应用场景 。 如果你是初学者 , 对于模块的了解可以暂时不用放在心上 , 因为理解模块需要更多的基础知识 。 但是如果你已经有了很多JavaScript的使用经验 , 在彻底了解了闭包之后 , 不妨借助本文介绍的作用域链与闭包的思路 , 重新理一理关于模块的知识 。 这对于我们理解各种各样的设计模式具有莫大的帮助 。
(function () {
var a = 10;
var b = 20;
function add(num1 num2) {
var num1 = !!num1 ? num1 : a;
var num2 = !!num2 ? num2 : b;
return num1 + num2;
window.add = add;)();add(10 20);
在上面的例子中 , 我使用函数自执行的方式 , 创建了一个模块 。 add是模块对外暴露的一个公共方法 。 而变量a , b被作为私有变量 。
为了验证自己有没有搞懂作用域链与闭包 , 这里留下一个经典的思考题 , 常常也会在面试中被问到 。
利用闭包 , 修改下面的代码 , 让循环输出的结果依次为1 ,2 ,3 ,4 ,5
for (var i=1;
i<=5; i++) {
setTimeout(
function timer() {
console.log(i);
i*1000 );
想要了解更多前端技术学习 , 可以关注我们广州蓝景 , 也可以评论区留言!
- 伊隆·马斯克|网传学习通1.7亿密码泄露!有什么补救措施?
- 6月21日消息|饿了么免单,金额少的十几元,多的有上百元
- 当24款不同的头显原型机一起呈现在眼前时|四大vr原型机面世,下一代vr头显会是什么样?
- MacBook Air|M2 MacBook Air是所有win轻薄本无法打败的梦魇,那么应该怎么选?
- 折叠屏|618已过,大浪淘沙剩下什么机型?这几款都是经典
- 华硕|大学生换笔电怎么选?12代酷睿+RTX3050独显,618换新优选它
- iPhone|还等什么iPhone 14?618返场大促看这3款真香手机,错过委屈半年
- 青岛市|孟晚舟成为华为的一把手,任正非果然反悔了,这其中发生了什么
- 华为鸿蒙系统|即将上线的鸿蒙系统3.0版本,将带来什么样的全新体验?
- 三星手机|为什么中国越来越少人用三星手机了?是哪里不好吗?