立即执行函数、闭包

立即执行函数、闭包

立即执行函数

立即执行函数,就是为了做一个局部变量。

!function () {
    var abc = 100
    function hehe() {
        console.log(abc)
    }
    hehe()
}.call()

// 立即执行实现了局部变量

闭包

闭包,就是有一个数据和一个操作数据的函数,只把函数暴露出来给外面的人使用。

function bibao() {
    var abc = 100

    function hehe() {
        console.log(abc += 1)
    }
    
    return hehe
}

var usebibao = bibao()
usebibao()

// usebibao 接收 bibao() 调用后 返回的 hehe函数,然后 usebibao() 调用 等于 hehe() 调用。
// 于是,闭包把 hehe 函数暴露出去给外面的人使用,外面的人可以通过hehe函数操作变量abc+1,但是却读取不到abc(因为abc是局部变量)。

立即执行函数+闭包

立即执行函数+闭包,就是有一个数据和一个操作数据的函数,把函数挂在window上给外面的人使用。

!function () {
    var abc = 100
    
    window.hehe = function () {
        console.log(abc += 1)
    }
}.call();

// !function (){}.call() 是立即执行函数,是为了做局部变量。
// 变量abc + 使用这个变量的函数 = 闭包
// 闭包的函数赋值给window.hehe,是为了暴露出来给被人使用这个函数。
// 于是,别人能通过window.hehe来操作abc的数据+1,但是却不知道abc的值,实现了隐藏数据的作用。

来看一个例子

崩崩崩.html

<script src="充值.js"></script>
<script src="查询余额.js"></script>

充值.js

!function () {
    var player = {
        name: '空中劈叉的清洁工',
        money: 100
    }
    window.charge = function () {
        player.money += 1
        return player.money
    }
}.call();

// player是局部变量,window.charge是全局变量。

查询余额.js

!function () {
    var newMoney = window.charge()
    console.log(newMoney)
}.call();

// 不知道player,但是能操作play的money充值。

为了局部变量而使用立即执行函数

网页要实现模块化

<script src="充值.js"></script>
<script src="查询余额.js"></script>

不能使用全局变量

// 充值.js
var player = 'sakura'
// 查询余额.js
var player = 'mei'

// 充值.js 和 查询余额.js 都使用了全局变量 player,它们互相覆盖,冲突了。

要使用局部变量,ES5里只有函数有局部变量

function () {
	var player = '空中劈叉的清洁工'
}.call()

// player是局部变量,函数也是匿名函数,完美!

但是 chrome 浏览器 会报错,语法错误,试出来一种方法不报错

!function () {
	var player = '空中劈叉的清洁工'
}.call()

// 在函数前面加!就行

更多解决 chrome浏览器 报语法错误的方法:

(function(){alert('我是匿名函数')} ()) // 用括号把整个表达式包起来
(function(){alert('我是匿名函数')}) () //用括号把函数包起来
!function(){alert('我是匿名函数')}() // 求反,我们不在意值是多少,只想通过语法检查。
+function(){alert('我是匿名函数')}()
-function(){alert('我是匿名函数')}()
~function(){alert('我是匿名函数')}()
void function(){alert('我是匿名函数')}()
new function(){alert('我是匿名函数')}()

为了隐藏数据细节而使用闭包

充值.js

!function () {
    var player = {
        name: '空中劈叉的清洁工',
        money: 100
    }
    window.charge = function () {
        player.money += 1
        return player.money
    }
}.call();

// 只暴露window.charge,不暴露 player。
// 变量player + 使用这个变量的函数 = 闭包

查询余额.js

!function () {
    var newMoney = window.charge()
    console.log(newMoney)
}.call();

// 不知道player,但是能通过window.charge操作player的money充值。

立即执行函数+闭包

总结

!function () {
    var player = {
        name: '空中劈叉的清洁工',
        money: 100
    }
    window.charge = function () {
        player.money += 1
        return player.money
    }
}.call();

// !function (){}.call() 是立即执行函数,是为了做局部变量。
// 变量player + 使用这个变量的函数 = 闭包
// 闭包的函数赋值给window.charge,是为了暴露出来给被人使用这个函数。
// 于是,别人能通过window.charge来操作player的数据+1,但是却不知道player的值,实现了隐藏数据的作用。

一个著名的面试题

var liList = ul.getElementsByTagName('li')
for(var i=0; i<6; i++){
  liList[i].onclick = function(){
    alert(i) // 为什么 alert 出来的总是 6,而不是 0、1、2、3、4、5
  }
}

为什么 alert 的总是 6 呢,因为 i 是贯穿整个作用域的,而不是给每个 li 分配了一个 i。

alert(i) //这里的i一直在遍历的过程中从0变到5,最后停在5.

那么怎么解决这个问题呢?用立即执行函数给每个 li 创造一个独立作用域即可(当然还有其他办法):

var liList = ul.getElementsByTagName('li')
for(var i=0; i<6; i++){
  !function(xxx){
    liList[xxx].onclick = function(){
      alert(xxx) // 0、1、2、3、4、5
    }
  }(i)
}

在立即执行函数执行的时候,i 的值被赋值给 xxx,此后 xxx 的值一直不变。

i 的值从 0 变化到 5,对应 6 个立即执行函数,这 6 个立即执行函数里面的 xxx 「分别」是 0、1、2、3、4、5。