美文网首页
一次性理解立即执行函数和闭包

一次性理解立即执行函数和闭包

作者: 不讨喜的大雄 | 来源:发表于2018-08-18 16:18 被阅读0次

    Immediately-Invoked Function Expression(IIFE):立即调用函数表达式

    使用全局变量可能会造成一些不必要的麻烦
    因此我们要使用局部变量

    在 java 语言中我们可以用 { } 将代码包裹起来,使它们成为局部变量
    但是这在 ES5 中不行,因为 var 有变量提升

    {
      var a = 1;
    }
    // 上面代码等同于
    var a 
    {
      a = 1;
    }
    

    ES6 里面引入了 let 的概念,于是有了块级作用域
    解决了这个问题,IIFE 似乎就变得不那么必要了

    {
      let a = 1;
    }
    a 
    // Uncaught ReferenceError: a is not defined
    

    但是了解 IIFE 可以让我们初学者更好得理解函数表达式、变量提升等的概念,以及了解前端语言的发展进程,知道前人一步步将原本不那么美好的 js 变得规范化的过程,学习他们的智慧。

    ES5 里面,没有块级作用域,只有函数(函数作用域)可提供局部变量环境
    于是我们声明一个 function xxx,然后 xxx.call()

    function xxx(){
      var a = 1;
    }.call()
    

    但是这个时候 xxx 也是全局变量(全局函数)
    所以我们不能给这个函数名字,即需要使用匿名函数

    function(){
      var a = 1;
    }.call()
    // Uncaught SyntaxError: Unexpected token (
    

    但是这在 Chrome 里会报错,js 语法错误

    这是由于浏览器认为这是一个函数声明
    解决办法:让浏览器去解析这个函数表达式

    如给函数取反

    ! function(){
      var a = 1;
    }.call() 
    // 我们不在乎这个匿名函数的返回值,所以加个 ! 取反没关系
    

    类似的还有

    +function(){}()
    -function(){}()
    ~function(){}()
    void function(){}()
    new function(){}()
    // function(){}() === function(){}.call()
    //用 .call() 更清楚
    

    如果实在不想改变函数返回值,也可以用圆括号

    (function(){}).call() 
    (function(){}.call())
    

    但是方法会有不好的情况:如果括号前面有东西

    a
    (function(){}).call() 
    //等价于
    a().call() 
    //这不是我们想要的
    //同理
    a
    (function(){}.call())
    //等价于
    a()
    

    所以不推荐

    你也可以用一个随机变量名函数而不用匿名函数,永远都不会变量污染的那种变量(这种方法好吗?)

    function ada231231284u3lkda(){
      var a = 1;
    }.call()
    

    当然最推荐的还是用取反 !,这就好像是个警示:我要开始用 IIFE 了


    IIFE 达到了局部变量的效果,外面访问不到立即执行函数里面的变量(避免变量污染)

    但有时候我们需要访问 IIFE 里面的内容,怎么办?
    最简单的方法是直接用全局变量 window

    ! function(){
      var a = window.a = {
        n: 1
      }
    }.call() 
    // 另一个立即执行函数通过 window.a 访问
    ! function(){
      var b = window.a
      console.log(a)
    }.call() // {n: 1}
    

    我们还可以在 IIFE 里面使用闭包来隐藏 a

    ! function(){
      var a = {
        n: 1
      }
      // 声明一个匿名函数保存于 window.nAdd1 
      window.nAdd1 = function(){
        a.n += 1
        // return 新的 a.n
        return a.n
      }
    }.call() 
    // 匿名函数可以操作函数外面的变量 a ,这就是闭包
    // 另一个立即执行函数通过 window 访问
    ! function(){
      var b = window.nAdd1()
      console.log(newN)
    }.call() // 2
    

    IIFE 使得 a 成为局部变量而无法被外部访问
    闭包使得匿名函数可以操作 a
    window.nAdd1 保存了匿名函数的地址
    任何地方都可以使用 window.nAdd1
    => 任何地方都可以使用 window.nAdd1 操作 a,但是不能直接访问 a
    这就是 window 和闭包联合作用的效果

    闭包

    将上面代码整合在一起,就是闭包的常见形式

    function fn(){
      var a = {
        n: 1
      }
      function fn2(){
        a.n += 1
        return a.n
      }
      return fn2
    }
    var fn1 = fn()
    fn1() // 2
    // 上面代码其实就是
    (function(){
      var a = {
        n: 1
      }
      return function(){
        a.n += 1
        return a.n
      }
    }.call()) // IIFE
    .call()// 2
    

    现在我们明白了为什么闭包一般在 IIFE 里出现
    IIFE 提供局部变量的环境,才有闭包的用武之地
    return 的作用
    上面代码中的 return 和 window 的作用一样,是为了让外部能够访问函数作用域内部的函数,当然 return a.n 的作用也是如此

    而闭包的作用,至此应该理解了


    本文仅作为个人学习使用

    相关参考:
    Immediately-Invoked Function Expression (IIFE)
    「每日一题」什么是立即执行函数?有什么作用?
    「每日一题」JS 中的闭包是什么?
    JS标准参考教程——IIFE
    ES6入门——let#块级作用域

    相关文章

      网友评论

          本文标题:一次性理解立即执行函数和闭包

          本文链接:https://www.haomeiwen.com/subject/aulebftx.html