1. 什么是作用域,作用域链
作用域,指代码所在的执行环境。
代码执行时产生的最先产生的执行环境,称为全局作用域, 如浏览器为 window, node 为 global. 作用域内部执行时产生的、新的作用域为局部作用域。(es5 不存在块级作用域,作用域仅通过函数作用域分隔。而 es6 中通过 let 和 const 声明的变量和常量,在 if 和 for 的执行部分拥有块级作用域。)
// 块级作用域
for(let i = 0; i < 3; i++) {
console.log(i) // 依次打印 1, 2, 3
}
console.log(i) // 报错: Uncaught ReferenceError: i is not defined
由此逐层衍生、逐层嵌套的作用域,称之为作用域链。它们拥有以下的特性:
1-1 外层作用域无法调用内层作用域的变量和常量。比如,全局作用域无法调用局部作用域声明的变量或常量。
function func(){
var foo = 111;
console.log(foo)
}
func() // 111
console.log(foo) // 报错: Uncaught ReferenceError: foo is not defined
1-2 代码执行需要时,优先引用当前所在执行环境中的变量和常量;当前执行环境不存在引用到的对象时,则追测其所在作用域链的上游。因此:任何局部作用域都可引用全局变量。
var foo = 111;
function func(){
console.log(foo)
}
func() // 111
1-3 直至追溯到最外层作用域(如上所说的 window 或 global)仍未定义的引用,即为 not defined.
2. 作用域的特殊情况
es5 非严格模式下,当在局部作用域不声明、赋值一个变量时, 产生的是一个全局变量。
// 非严格模式下
function test() {
a = 111;
}
test();
console.log(a); // 111
3. 什么是闭包,如何产生?有哪些应用?优缺点分别是什么?
当函数执行完成后,返回一个值时,当前执行环境会被清理掉。但是当函数返回一个调用了局部变量的函数时,外层函数的执行环境不会被清理掉,此作用域内的变量仍能被返回的函数引用。由此形成的即为闭包。
闭包应用
3-1 例1:对应位置的斐波那契数
const fib = (() => {
// 利用闭包避免重复计算
let cache = { 1: 1, 2: 1 };
const func = num => {
// 避免误传非正整数造成死循环
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:通过调用返回的函数, 形成一个外部作用域能够获取、修改局部变量的间接渠道。
function closure() {
let n = 0;
return {
add: (m = 1 ) => n+=m,
minus: (m = 1) => n-=m,
getN: () => n
}
}
// 外层作用域无法直接获取和修改 n; 但是可以通过 closure 返回的函数,进行获取和操作。
const {add, minus, getN} = closure()
getN(); // 0
add(2);
getN(); // 2
minus();
getN(); // 1
优缺点
优点:对于可能重复的运算,进行缓存;
缺点:当运算数字过大时,会产生内存占用;影响整体性能;
网友评论