饿了么|广州蓝景技术分享 — 闭包原理以及使用场景( 二 )



在上面的例子中 , 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 );


想要了解更多前端技术学习 , 可以关注我们广州蓝景 , 也可以评论区留言!