1. 什么是作用域,作用域鏈
作用域,指代碼所在的執(zhí)行環(huán)境。
代碼執(zhí)行時產(chǎn)生的最先產(chǎn)生的執(zhí)行環(huán)境,稱為全局作用域, 如瀏覽器為 window, node 為 global. 作用域內(nèi)部執(zhí)行時產(chǎn)生的、新的作用域為局部作用域。(es5 不存在塊級作用域,作用域僅通過函數(shù)作用域分隔。而 es6 中通過 let 和 const 聲明的變量和常量,在 if 和 for 的執(zhí)行部分擁有塊級作用域。)
// 塊級作用域
for(let i = 0; i < 3; i++) {
console.log(i) // 依次打印 1, 2, 3
}
console.log(i) // 報錯: Uncaught ReferenceError: i is not defined
由此逐層衍生、逐層嵌套的作用域,稱之為作用域鏈。它們擁有以下的特性:
1-1 外層作用域無法調(diào)用內(nèi)層作用域的變量和常量。比如,全局作用域無法調(diào)用局部作用域聲明的變量或常量。
function func(){
var foo = 111;
console.log(foo)
}
func() // 111
console.log(foo) // 報錯: Uncaught ReferenceError: foo is not defined
1-2 代碼執(zhí)行需要時,優(yōu)先引用當前所在執(zhí)行環(huán)境中的變量和常量;當前執(zhí)行環(huán)境不存在引用到的對象時,則追測其所在作用域鏈的上游。因此:任何局部作用域都可引用全局變量。
var foo = 111;
function func(){
console.log(foo)
}
func() // 111
1-3 直至追溯到最外層作用域(如上所說的 window 或 global)仍未定義的引用,即為 not defined.
2. 作用域的特殊情況
es5 非嚴格模式下,當在局部作用域不聲明、賦值一個變量時, 產(chǎn)生的是一個全局變量。
// 非嚴格模式下
function test() {
a = 111;
}
test();
console.log(a); // 111
3. 什么是閉包,如何產(chǎn)生?有哪些應用?優(yōu)缺點分別是什么?
當函數(shù)執(zhí)行完成后,返回一個值時,當前執(zhí)行環(huán)境會被清理掉。但是當函數(shù)返回一個調(diào)用了局部變量的函數(shù)時,外層函數(shù)的執(zhí)行環(huán)境不會被清理掉,此作用域內(nèi)的變量仍能被返回的函數(shù)引用。由此形成的即為閉包。
閉包應用
3-1 例1:對應位置的斐波那契數(shù)
const fib = (() => {
// 利用閉包避免重復計算
let cache = { 1: 1, 2: 1 };
const func = num => {
// 避免誤傳非正整數(shù)造成死循環(huán)
let n = parseInt(num) >= 1 ? parseInt(num) : 1;
if (cache[n]) return cache[n];
// 遞歸
return (cache[n] = func(n - 2) + func(n - 1));
};
return func;
})();
console.log( fib(5) ) // 8
3-2 例2:通過調(diào)用返回的函數(shù), 形成一個外部作用域能夠獲取、修改局部變量的間接渠道。
function closure() {
let n = 0;
return {
add: (m = 1 ) => n+=m,
minus: (m = 1) => n-=m,
getN: () => n
}
}
// 外層作用域無法直接獲取和修改 n; 但是可以通過 closure 返回的函數(shù),進行獲取和操作。
const {add, minus, getN} = closure()
getN(); // 0
add(2);
getN(); // 2
minus();
getN(); // 1
優(yōu)缺點
優(yōu)點:對于可能重復的運算,進行緩存;
缺點:當運算數(shù)字過大時,會產(chǎn)生內(nèi)存占用;影響整體性能;