一秽五、立即執(zhí)行函數(shù)的出現(xiàn)
我們都知道地梨,在ES 6出現(xiàn)之前,前端開發(fā)人員在JavaScript中只能在函數(shù)中才能使用局部變量:
var a = 1 //全局變量
function createB(){
var b = 2 //局部變量
console.log(b)
}
createB() // 2
console.log(a) // 1
console.log(b) // b is not defined
在上面的代碼中 createB()
這個函數(shù)在聲明之后立即調(diào)用,在控制臺打印出了變量b的值掖棉。
在C語言或C++中钦睡,我們可以使用 {}
將代碼包裹起來實現(xiàn)局部變量蒂窒,但是JavaScript中有變量提升這個東西,所以在JS中這種方法并不可行赎婚。
但是這種方法還是使用到了全局變量刘绣,在哪里呢?函數(shù) createB()
挣输,雖然變量B在函數(shù)內(nèi)是一個局部變量纬凤,但是函數(shù)本身還在全局作用域中。所以我們在調(diào)用函數(shù) createB
時還是使用到了全局變量撩嚼。
怎么辦呢停士,這里我們將函數(shù) createB()
改寫成一個匿名函數(shù),并且立即調(diào)用它:
function(){
var b = 2
console.log(b)
}.call()
這種方法雖然解決了局部變量的問題完丽,但是在瀏覽器中執(zhí)行的時候會報錯:
function(){
var b = 2
console.log(b)
}.call() //Uncaught SyntaxError: Unexpected token (
于是廣大程序員試出來一些方法使得瀏覽器不報語法錯誤:
//第一種恋技,在函數(shù)前加取反符號 !
!function(){
var b = 2
console.log(b)
}.call()
//第二種逻族,在函數(shù)前加 +/- 號
-function(){
var b = 2
console.log(b)
}.call()
(function(){
var b = 2
console.log(b)
}).call()
//第三種蜻底,用()將函數(shù)體包裹起來
(function(){
var b = 2
console.log(b)
}).call()
// 還有其他方法不再一一介紹
雖然第一、第二種方法會改變函數(shù)的返回值聘鳞,但是由于我們不在乎函數(shù)的返回值薄辅,因此并不會有影響。
二抠璃、總結(jié)立即執(zhí)行函數(shù)
- 我們程序員不想用全局變量
- 我們想用局部變量
- ES 6之前站楚,只有函數(shù)有局部變量
- 于是我們聲明一個 function xxx(){},然后調(diào)用這個函數(shù)搏嗡,xxx.call()
- 但是函數(shù)xxx也是全局變量(全局函數(shù))窿春,因此我們不能給函數(shù)名字
- 就有了匿名函數(shù) function(){}.call()
- 但是這種寫法在 Chrome 中會報語法錯誤
- 程序員試出來一些方法使得瀏覽器不報錯
三拉一、閉包的出現(xiàn)
在上文中我們知道了立即執(zhí)行函數(shù)的出現(xiàn)以及作用就是實現(xiàn)局部變量,那么現(xiàn)在就出現(xiàn)了新的問題旧乞,假設(shè)我現(xiàn)在有兩個 .js
文件肯夏,這兩個文件想要相互訪問要如何實現(xiàn)份帐?
// module1.js
!function(){
var person = {
"name": "wky",
"age": 18
}
window.person = person
}.call()
// module2.js
!function(){
var person = wondow.person
console.log(person)
}.call()
我們將變量person添加到window對象上锐想,這樣其他的函數(shù)內(nèi)部也可以訪問到變量person然走,但是為了避免其他函數(shù)直接操作變量person,我們修改一下這個函數(shù):
// module1.js
!function(){
var person = {
"name": "wky",
"age": 18
}
window.personGrowUp = function(){
person.age += 1
return person.age
}
}.call()
// module2.js
!function(){
var newAge = window.personGrowUp
console.log(newAge) // 19
}.call()
這里module1.js函數(shù)中只暴露了一個函數(shù) personGrowUp()
决瞳,在module2.js中只能使用這個使年齡加一的函數(shù)货徙,而不能直接使用變量person,這樣就起到了一個隱藏變量的功能皮胡,在module1.js中就生成了一個閉包痴颊,因為函數(shù) personGrowUp()
使用了函數(shù)之外的一個變量person。
四屡贺、總結(jié)閉包的使用
- 立即執(zhí)行函數(shù)使得變量 person 無法被外部訪問
- 閉包使得匿名函數(shù)可以操作 person
- window.personGrowUp 保存了匿名函數(shù)的地址
- 任何地方都可以使用 window.personGrowUp 操作 person 蠢棱,但是不能直接訪問 person
五、改寫函數(shù)
我們對上面的函數(shù)進行一些簡單的改寫就可以得到一個標準的閉包:
var fn = function(){
var person = {
"name": "wky",
"age": 18
}
return function(){
person.age += 1
return person.age
}
}
var personGrowUp = fn()
personGrowUp()
這里函數(shù)fn在聲明之后立即調(diào)用甩栈,這也是一個立即執(zhí)行函數(shù)泻仙,函數(shù)fn返回一個匿名函數(shù),因此我們可以再次調(diào)用 personGrowUp
函數(shù)量没。