黑马程序员技术交流社区

标题: 自调用函数与闭包 [打印本页]

作者: 海域王魂    时间: 2017-12-1 19:04
标题: 自调用函数与闭包
本帖最后由 海域王魂 于 2017-12-1 19:08 编辑

       在前端的课程学习中,有一个很特殊的阶段——面向对象,又叫js高级。       这个阶段的课程中,同学们会接触到js的一个比较蛋疼的概念,称之为闭包;同时,还有一个也比较绕的东西 叫做自调用函数。
自调用函数与闭包这两个概念,是很多学员在这个阶段学习中遇到的一个困难点,我就说一说,我对于这两个东西的理解,希望对学习中遇到这方面疑问的同学能有一些帮助。
      首先是自调用函数
             自调用函数,也有称呼为 自执行函数。顾名思义,从它字面上简单理解就是一个函数写完函数体,立刻就执行。
       我们平常在声明函数的时候一般有这几种方式:
              
[JavaScript] 纯文本查看 复制代码
// 第一种
function fn(){ … };
// 第二种
var fn = function(){ … };
// 第三种
var fn = new Function( …… );
      当我们要调用的时候 一般都是 用  “函数名()” 的方式去调用 比如: fn(); 这样当代码解释到了这一行的时候 这个函数也就执行了
      而自调用函数它特别的地方在于它的语法:
      
[JavaScript] 纯文本查看 复制代码
( function( ){ 
    //函数体
} )()
      在写法上有两个注意点。
      第一,它没有函数名,没有函数名的函数 我们称为匿名函数

      第二,在这个匿名函数的函数体完成之后 紧跟着 就写了"()"
      如果我们把这个自调用函数 用我们习惯性的写法来推导一下的话 可以写成
      
[JavaScript] 纯文本查看 复制代码
 
var fn = function(){
        //函数体
}
fn()
      这里用了一个fn作为变量去接收了声明的函数 而自调用函数是匿名的,这就是自调用函数的最基本写法。
      那么它的作用是什么?
      其实就是提供了一个封闭的函数作用域,可以避免变量污染。有时候我们的项目要引用很多个不同的js文件 但是这么多不同的js文件 你怎么才能保证它们里面没有相同的变量名 或者 相同的函数名呢?而自调用函数就提供了一个封闭的环境,可以避免这些情况的发生 最典型的例子就是Jquery
      比如这样
      
[JavaScript] 纯文本查看 复制代码

;(function(window){
    function Jquery(){
     // 函数体
  }
   window.$= window.Jquery = Jquery;
})(window,undefined)
      然后是闭包
    在说闭包之前,要了解一个词:垃圾回收机制。
        所谓垃圾回收机制,意思就是
             找出不再使用的变量,然后释放掉其占用的内存,但是这个过程不是时时的,因为其开销比较大,所以垃圾回收器会按照固定的时间间隔周期性的执行.         
[JavaScript] 纯文本查看 复制代码

function fn(){
    var a = "123";
    console.log(a)
}
fn()  // 当fn执行了之后 变量a就被从内存中释放掉了
       其次我们还需要回顾一下js 变量的作用域,js的变量分为 [size=1.6em]全局变量和局部变量
     js的函数内部可以访问外部的变量,而函数的外部无法访问 js函数内部的变量。
     有的情况下 我们会需要用到函数内部的变量,但是正常手段访问不到,这时候 就用到了我们的闭包
     这里看一段代码
     
[JavaScript] 纯文本查看 复制代码
function f1(){
    var n=999;
    function f2(){
      alert(n); // 999
    }
  }
       根据我们之前学过的作用域链  子函数可以访问父级函数内的变量 那么 我如果把f2作为返回值 返回出去  不就可以在外部使用 n 这个变量了么?

     闭包的特点        
[JavaScript] 纯文本查看 复制代码
function f1(){
    var n=999;
    function f2(){
      return n
    }
    return f2
  }
  var result=f1();
  console.log(result());//999
      
        这就是一个简单闭包 这里最关键的地方在于 f1 这个函数 它的返回值 是 f2 这个函数   那么 我们在代码中就用了一个变量 result 接收了 f1的返回值 也就是 f2 函数的函数体 当result()执行的时候 其实也就是f2在执行 那么就可以把 f1中的n 返回到外部去使用了
        有同学在这里会有一个疑问
         为什么明明 f1 已经执行完了 它里面的变量 n 却没有释放呢? 那是因为n 被f2 引用了 所以就不会被释放掉
         所以闭包还有一个需要注意的地方
         由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。
      
[JavaScript] 纯文本查看 复制代码
function f1(){
    var n=999;
    function f2(){
      return n
    }
    return { "val":f2,"clearVariable":function(){ n=null } }
  }
  var result=f1();
  console.log(result.val());
    // 清除不再使用的闭包内变量
        result.clearVariable()

作者: 郭俊峰老师    时间: 2017-12-1 20:07
这好像是前端比较难的部分之一 谢谢分享
作者: 唐杰    时间: 2017-12-1 20:13
大神,带带我吧!
作者: 老哥哥    时间: 2017-12-4 16:07
很好~




欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2