es6有閉包。在es6中,在一個函數(shù)內(nèi)部創(chuàng)建另一個函數(shù),把內(nèi)嵌的函數(shù)稱為閉包,它可以訪問外部函數(shù)的局部變量;簡單來說,閉包指有權(quán)訪問另一個函數(shù)作用域中變量的函數(shù)。閉包的主要作用:延伸了變量的作用范圍。由于閉包會使得函數(shù)中的變量都被保存在內(nèi)存中,內(nèi)存消耗很大,所以不能濫用閉包,否則會造成網(wǎng)頁的性能問題,在IE中可能導(dǎo)致內(nèi)存泄露。
前端(vue)入門到精通課程:進(jìn)入學(xué)習(xí)
Apipost = Postman + Swagger + Mock + Jmeter 超好用的API調(diào)試工具:點擊使用
本教程操作環(huán)境:windows7系統(tǒng)、ECMAScript 6版、Dell G3電腦。
一、變量作用域
變量根據(jù)作用域的不同分為兩種:全局變量和局部變量。
-
函數(shù)內(nèi)部可以使用全局變量。
-
函數(shù)外部不可以使用局部變量。
-
當(dāng)函數(shù)執(zhí)行完畢,本作用域內(nèi)的局部變量會銷毀。
二、什么是閉包?
在es6中,閉包(closure)指有權(quán)訪問另一個函數(shù)作用域中變量的函數(shù)。簡單理解:一個作用域可以訪問另外一個函數(shù)內(nèi)部的局部變量。
閉包:在一個函數(shù)內(nèi)部創(chuàng)建另一個函數(shù),把內(nèi)嵌的函數(shù)稱為閉包,它可以訪問外部函數(shù)的局部變量
// fun 這個函數(shù)作用域 訪問了另外一個函數(shù) fn 里面的局部變量 num function fn(){ let num = 10 function fun(){ console.log(num) } fun() } fn() //10
閉包的主要作用:延伸了變量的作用范圍。
// fn 外面的作用域可以訪問fn 內(nèi)部的局部變量 function fn(){ let num = 10 // 方法一: 先定義再返回函數(shù) function fun(){ console.log(num) } return fun //返回 fun函數(shù) } let f = fn() f() //10
// fn 外面的作用域可以訪問fn 內(nèi)部的局部變量 function fn(){ let num = 10 // 方法二: 直接返回函數(shù) return function(){ console.log(num) } } let f = fn() f() //10
三、閉包的使用場景
(1)用來返回值
//以閉包的形式將 name 返回 function fun(){ let name = 'woniu' //定義閉包 return function f1(){ return name } } let ft = fun() //因為fun函數(shù)的返回值是f1函數(shù),ft實質(zhì)是一個函數(shù) let na = ft() //調(diào)用ft函數(shù),實際調(diào)用的就是f1函數(shù) console.log(na); //woniu
(2)函數(shù)賦值:在函數(shù)內(nèi)部定義函數(shù)表達(dá)式
var f2 function fn(){ let name = '曹操' f2 = function(){ //閉包,將外部函數(shù)的name變量作為閉包的返回值 return name } } fn() //必須先調(diào)用fn函數(shù),否則f2不是一個函數(shù) console.log(f2()); //曹操
(3)把閉包作為函數(shù)的參數(shù)
function fn(){ let name = '蝸牛學(xué)苑' //定義閉包 return function callback(){ return name } } let f1 = fn() //將fn函數(shù)的返回值callback賦給f1 function f2(temp){ console.log(temp()) //輸出temp函數(shù)的返回值,實際調(diào)用了閉包callback } //調(diào)用f2函數(shù):將f1作為實參傳遞給temp f2(f1)
(4)立即執(zhí)行函數(shù)中使用閉包
//立即執(zhí)行函數(shù) (function(){ let name = '蝸牛學(xué)苑' let f1 = function(){ return name } fn2(f1) //調(diào)用fn2函數(shù),將閉包f1作為實參傳遞給fn2函數(shù) })() function fn2(temp){ //temp是一個形參,接收f1 console.log(temp()); //對temp的調(diào)用,實際調(diào)用的是閉包f1 }
(5)循環(huán)賦值
(function(){ for (let i = 1; i <= 10; i++) { ( function(j){ setTimeout(function(){ console.log(j); },j*1000) } )(i) } })()
(6)將閉包封裝到對象中
function fun(){ let name = '蝸牛學(xué)苑' setName = function(na){ //setName是閉包,用來設(shè)置外部函數(shù)的變量值 name = na } getName = function(){ //getName是閉包,用來返回外部函數(shù)的變量值 return name } //外部fun函數(shù)的返回值,將閉包封裝到對象中返回 return { setUserName:setName, getUserName:getName } } let obj =fun() //將fun函數(shù)返回值(對象)賦給obj console.log('用戶名:',obj.getUserName()) //蝸牛學(xué)苑 obj.setUserName('石油學(xué)苑') console.log('用戶名:',obj.getUserName()) //石油學(xué)苑
(7)通過閉包實現(xiàn)迭代
let arr = ['aa','bb','cc'] function fn(temp){ //外部函數(shù)的返回值是閉包 let i = 0 //定義閉包:迭代獲取數(shù)組元素并返回 return function(){ return temp[i++] || '數(shù)組已經(jīng)遍歷結(jié)束' } } let f1 = fn(arr) console.log(f1()) //aa console.log(f1()) //bb console.log(f1()) //cc console.log(f1()) //數(shù)組已經(jīng)遍歷結(jié)束
(8)、首次區(qū)分(相同的參數(shù),函數(shù)不會重復(fù)執(zhí)行)
var fn = (function(){ var arr = [] //用來緩存的數(shù)組 return function(val){ if(arr.indexOf(val) == -1){ //緩存中沒有則表示需要執(zhí)行 arr.push(val) //將參數(shù)push到緩存數(shù)組中 console.log('函數(shù)被執(zhí)行了',arr); //這里寫想要執(zhí)行的函數(shù) } else { console.log('此次函數(shù)不需要執(zhí)行'); } console.log('函數(shù)調(diào)用完打印一下,方便查看緩存的數(shù)組:',arr); } })() fn(10) fn(10) fn(1000) fn(20) fn(1000)
注意
(1)搞清除誰是閉包函數(shù)
(2)分清楚閉包的返回值、外部函數(shù)的返回值
四、閉包總結(jié)
-
閉包是什么:閉包是一個函數(shù)(一個作用域可以訪問另外一個函數(shù)的局部變量)。
-
閉包的作用是什么:延伸變量的作用范圍。
沒有產(chǎn)生閉包,因為并沒有局部變量,所以訪問到的是全局變量
The Window
let name = 'The Window' let object = { name: 'My Object', getNameFunc(){ return function(){ return this.name } } } let f = object.getNameFunc() console.log(f()) //The Window
產(chǎn)生了閉包:因為 this 在函數(shù)內(nèi)部被賦值給了 that,指向的是 object 這個對象。
let name = 'The Window' let object = { name: 'My Object', getNameFunc(){ let that = this return function(){ return that.name } } } let f = object.getNameFunc() console.log(f()) //My Object
使用閉包的注意點
1)由于閉包會使得函數(shù)中的變量都被保存在內(nèi)存中,內(nèi)存消耗很大,所以不能濫用閉包,否則會造成網(wǎng)頁的性能問題,在IE中可能導(dǎo)致內(nèi)存泄露。解決方法是,在退出函數(shù)之前,將不使用的局部變量全部刪除。
2)閉包會在父函數(shù)外部,改變父函數(shù)內(nèi)部變量的值。所以,如果你把父函數(shù)當(dāng)作對象(object)使用,把閉包當(dāng)作它的公用方法(Public Method),把內(nèi)部變量當(dāng)作它的私有屬性(private value),這時一定要小心,不要隨便改變父函數(shù)內(nèi)部變量的值。
【推薦學(xué)習(xí):javascript視頻教程】