什么是立即执行函数,什么是闭包?

142 阅读5分钟

立即执行函数

what:什么是立即执行函数

字面意思就是,声明一个函数然后调用这个函数,就是执行函数,而立即执行函数和执行函数有什么区别呢?

function a(){
    console.log("111");
}
a()

简单来说,声明一个匿名函数(省掉a的名字),然后立刻执行它,这种做法就是立即执行函数

但是这样做,浏览器会报错,要想一个办法让它不报语法错误

function (){
    console.log("111");
}()

how:如何形成立即执行函数

如何让刚才的立即执行函数不报错呢?用括号把整个表达式包起来

然后就变成了这样,现在就形成了规范的立即执行函数

(
function(){
    console.log("111");
}()
) // 用括号把整个表达式包起来

但是这样还是会有问题,就是如果后面跟着一个数组的map方法,就会出错,因为上面立即执行函数的return 是 undefined

(
function(){
console.log("111");
}()
)
[1,2,3].map(item=>console.log("item"));

所以更安全的方法就是在后面加当然还有其他很多写法,可以在MDN上查询

(
function(){
console.log("111");
}()
);
[1,2,3].map(item=>console.log("item"));

why:立即执行函数的作用

知道了什么是立即执行函数,而立即执行函数有什么用呢?

因为我们知道var声明的变量具有变量提升,是一个全局变量

(假设现在是ES6之前 也就是说没有let 和 const)

有些场景我们确实需要使用局部变量,但又不支持怎么办呢?那能不能利用函数曲线救国形成一个局部变量呢?因为写到函数里面肯定外面就不能访问嘛

function fn(){
    var a = 10;
}
console.log(a) //访问不到,因为在函数内部

立即执行函数就是用来创建一个局部变量的(下面的a现在就是局部变量了)

//ES6之前 用立即执行函数创建局部变量
(function (){
    var a = 10;
}());
consloe.log(a) //访问不到

//ES6之后 可以用let + block创建局部变量
{
    let b = 5;
}

闭包

what:什么是闭包

闭包是JS的一种语法特性

闭包 = 函数 + 自由变量(与自由变量相对应的是全局变量)

对函数来说,变量分为:全局变量,本地变量(是函数自己的变量),自由变量(在函数外面,但又不是本地变量和全局变量)

那如何用函数+自由变量形成闭包呢?

how:如何形成闭包

我们已经说了闭包是函数加自由变量,那么这个自由变量要如何形成,也就是说要放到哪个位置呢?

  1. 先看这样一段代码,add访问了外部的变量count,但这样不是闭包,因为函数本来就可以访问var全局变量,这不是一个语法特性
var count = 0;
function add(){ 
  count += 1;
}
  1. 那这样写呢?此时count是add的本地变量,本来就可以访问,也不是闭包
function add(){
    let count = 0; 此时count是add的局部变量,本来就可以访问,也不属于闭包
    count += 1;
}

那怎么样才能形成闭包呢?根据说法,我们需要把上面代码放在非全局环境里,才能形成自由变量。

根据之前的铺垫我们知道了,立即执行函数会创建一个局部作用域。所以我们创建出一个立即执行函数

{
    let count = 0; //现在count既不是代码1的全局变量
    //也不是下面代码2的局部变量,属于自由变量
    function add(){
        count += 1;
    }
}

why:闭包有什么用

但是这个代码现在看起来什么用也没有,那么闭包用来解决什么问题呢?

比如现在这个count是游戏中的lives生命数,必然是不能放在全局变量的,因为这样任何人都能修改了,很危险

    var lives = 5;
    lives = 0;//在全局,谁都可以修改
    lives = 100;

那么我放到局部变量呢?那么直接从谁都能访问变成了谁都不能访问

    {
        let lives = 5;
    }
    consloe.log(lives) //访问不到

但是使用闭包,可以提供一个函数,通过调用提供的函数,间接访问到里面的live,,这样既不会被随意修改,也能安全的访问到live,所以把上面的代码改成闭包(函数+自由变量), function(){return lives}是函数,let lives = 3是自由变量

{
    let lives = 3
    window.getlives = function(){return lives}
    window.addlives = ()=>{ lives += 1}
}
window.getlives() //间接访问

不挂在window上,用api

更常见的写法,也就是闭包是函数嵌套函数的说法,下面就是一个函数,返回也是函数

var api = function(){
    let lives = 3
    return{
        getlives:()=>{lives}
        addlives:()=>{ lives += 1}
    }
}
//使用
api.getlives()

各种常见写法:看到了要知道其实就是闭包

const add2 = function(){
  let count = 0; //自由变量
  const add = ()=>{count += 1};
  return add
}()
add2()
// 相当于add(),add()相当于count += 1
//但是这个代码什么用也没有,所以我们需要 return add ,即
const add2 = function(){
  let count = 0; //自由变量
  return function add(){ 
    count += 1;
  }
}()
add2()
// 相当于add(),add()相当于count += 1

完整的来说闭包就是:声明一个变量add2 等于一个立即执行函数(add2后面那一坨),然后立即执行函数里面有:一个自由变量count 和一个函数,add对变量进行操作,这个时候执行add2就和执行add是一样的效果了,所以上面那一坨代码,是一个立即执行函数里面放了一个闭包

总结:闭包的作用

  • 避免污染全局环境,隐藏一个变量
  • 提供对局部变量的间接访问,如果不支持闭包,这个地方的function(){return lives}就不能访问到外面的let lives = 3
  • 维持变量不被垃圾回收

注意 闭包不会造成内存泄露,而是闭包使用不当造成内存泄漏,并且这是在IE上的一个bug

function test(){
  var x = {name:'x'};
  var y = {name:'y',content:".....很长的一块儿内容"}
  return function fn(){
    return x;
  }
}

const myFn = test(); //myFn就是fn了
const myX = myFn();//myx就是x了
//请问y会消失吗?

对于一个正常的浏览器来说,y是会在一段时间后消失的,但是IE仿佛把x y 绑定了一样,即使没有用到,也依旧存在

参考文章:JS 中的闭包是什么?