作用域的概念
同級作用域
在一個作用域中宣告相同名稱的變數會發生變數名沖突的問題,假如在作用域 A 中宣告一個變數 a,作用域 B 也宣告一個變數 a,兩個作用域的變數都互不影響,
// 作用域 A
{
let a = 0;
console.log(a);
}
// 作用域 B
{
let a = 10;
console.log(a);
}
第一個列印 0,第二個列印 10,
嵌套作用域
作用域是可以嵌套的,作用域 A 嵌套作用域 B,此時兩個作用域分別宣告變數 a,也是不沖突的,
{// 作用域 A
let a = 0;
console.log(a);
{ // 作用域 B
let a = 10;
console.log(a);
}
}
第一個列印 0,第二個列印 10,
在嵌套作用域中,子作用域 B 可以訪問父作用域 A 的變數,也可以影響它的父作用域 A,
{
let a = 0;
{
a = 10;
console.log(a);
}
}
最終列印的結果是 10,
函式的閉包
JS 的類實際上就是在使用函式的閉包,每執行一次函式就是在生成一個新的作用域,這些新的作用域就像是上面提到的,它們互不影響,只有它們的子作用域可以影響父作用域,即閉包,
閉包快取資料
function Counter(x) {
return {
add: y => {
return (x = x + y);
},
del: y => {
return (x = x - y);
}
};
}
add 和 del 都是父函式 Counter 的子函式,它們之間是一個閉包關系,創建兩個 Counter 的實體:
let c1 = Counter(10);
console.log(c1.add(20));
console.log(c1.del(40));
當執行c1.add()時,左邊有一個 Closure,說明已經形成了一個閉包:
Closure 中只有 x 是受閉包影響被快取下來的資料,也就是父函式 Counter 的變數,Local 代表 add 函式自身的變數,沒有與其他函式之間(或作用域)形成關系,也就不符合閉包的存在條件,只能由 add 函式自己來使用,
再繼續往下執行,可以看到此時 Closure 中的 x 已經是 30:
再往下執行,呼叫 del 函式,再執行函式的相減操作之間,我們可以看到 Closure 中的 x 還是上一次的結果:30,
再接著往下執行一次,del 函式相減之后,Closure 中的 x 的結果是 -10:
本節小結
受閉包的影響,父函式的資料被快取下來,子函式可以自由地使用,而且再記憶體中也不會被銷毀,
閉包的好處
仔細觀察上面的例子,add 和 del 函式都依賴了相同的變數 x,而這個 x 是父函式給的,再閉包中被快取起來,add 和 del 只需要傳遞新的引數就可以參與運算,也就是說,閉包可以減少我們函式的引數傳遞,使得我們一個計算操作更加連貫,且降低代碼耦合度,
假如不使用閉包,通過函式的引數傳遞來計算,替代上面的閉包函式:
function add(x, y) {
return (x = x + y);
}
function del(x, y) {
return (x = x - y);
}
let res = add(10, 20);
let ser = del(res, 40);
就很沒有必要,何不如把 x 抽離出來呢?變成一個全域變數:
let x = 10;
function add(y) {
return (x = x + y);
}
function del(y) {
return (x = x - y);
}
let res = add(20);
let ser = del(40);
可以,但是不推薦,全域作用域中,變數 x 被宣告一次,假如代碼越寫越多,變數是不是會沖突,代碼是不是變得難以維護?閉包可以把 add 和 del 以及 x 都囊括在一個作用域里,也不影響其他的作用域,
本節小節
閉包可以把一塊代碼容納在一個里面,形成一個整體,一個不受其他作用域影響的作用域,是不是很像模塊開發?沒錯,我猜測 CommonJS 就是使用的閉包,
總結
-
閉包可以讓我們使用模塊開發思想來寫代碼,把一系列代碼揉進閉包里,是一個有機的結合,類就是使用的閉包,在早期通過閉包來實作模塊的開發,
-
閉包可以快取父函式的變數,子函式可以使用,子函式修改父函式的變數,其他子函式也跟著改變,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/503141.html
標籤:其他
