静态作用域:
一段代码,在它执行之前就已经确定了它的作用域,
简单来说就是在执行之前就确定了其可以应用哪些地方的作用域(意指:该作用域下的变量)。
以下例子为《JavaScript权威指南》中经典的一个例子:
例一:
var scope = "global scope";
function checkScope() {
var scope = "local scope";
function fn() {
return scope;
}
return fn();
}
checkScope(); // local scope
分析:
在函数checkScope中定义了函数fn,并将函数fn的返回值当作函数checkScope的返回值返回。
首先在函数fn的内部作用域中找变量scope,没找到变量,所以在函数fn当前所在的作用域下找变量scope,找到变量,直接返回,所以返回的结果是local scope。
例二:
var scope = "global scope";
function checkScope() {
var scope = "local scope";
function fn() {
return scope;
}
return fn;
}
checkScope()(); // local scope
分析:
在函数checkScope中调用函数fn,函数fn同作用域下定义了变量scope,所以输出的是local scope。
在函数checkScope中定义了函数fn,并将函数fn当作函数checkScope的返回值返回,此时函数checkScope返回的是一个函数对象,不管这个返回的函数在哪里执行,当他定义时,他的作用域就已经确定了。
所以首先在函数fn的内部作用域中找变量scope,没找到变量,所以在函数fn当前所在的作用域下找变量scope,找到变量,直接返回,所以返回的结果还是local scope。
修改词法作用域(不推荐)
在代码书写时,作用域(词法作用域)就已经确定了,但是可不可以再修改呢?
通过eval和with都可以用来修改词法作用域。
动态作用域:
函数的作用域是在函数调用的时候才决定的
在调用某个变量时,会从当前作用域逐级向上查找。
如果在当前作用域找到,就调用该变量,如果没找到,就继续向父级作用域查找,以此类推。
如果一直查找到最外层的全局作用域,都没有找到该变量,那么就表明没有该变量。
注意区别于作用域链:函数在定义时,不光确定了它内部的作用域,还确定了它外部的作用域,也就是作用域链。
// 父级作用域变量num1
var num1 = 1;
function fn() {
// 当前作用域变量num2
var num2 = 2;
// 在当前作用域没有找到变量num1,向父级作用域查找,在父级作用域找到变量num1,直接调用
console.log(num1); // 1
// 在当前作用域找到变量num2,直接调用
console.log(num2); // 2
// 在当前作用域没有找到变量num3,向父级作用域查找,在父级全局作用域也没找到变量num3,证明该变量未定义
console.log(num3); // ReferenceError: num3 is not defined
}
fn();
函数作用域
函数作用域的含义:属于这个函数的全部变量可以在整个函数的范围内使用及复用。
块级作用域
{
let a = '块级作用域'
}
console.log(a); //undefined
1.with
with也是块作用域的一个例子。
2.try/catch
try/catch的catch分句也会创建一个块作用域,其中声明的变量仅在catch内部有效。
3.let
let关键字可以将变量绑定到所在的任意作用域中,换句话说,let为其声明的变量隐式地劫持了所在的块作用域。
用let将变量附加在一个已经存在的块作用域的行为是隐式的。如果没有密切关注哪些块作用域中有绑定的变量,并且习惯性移动这些块或者包含在其他块中,就会导致代码混乱。
但是,用let进行的声明不会在块作用域中进行提升。声明的代码被运行之前,声明并不存在。
4.垃圾收集
5.let循环
for循环头部的let不仅将i绑定到for循环的块中,事实上它将重新绑定到了循环的每一个迭代中,确保使用上一个循环迭代结束时的值重新进行赋值。
for(let i=0;i<10;i++){
console.log(i);
}
console.log(i);
6.const
ES6引入了const,同样可以用来创建块作用域变量,但是其值是固定的。之后试图修改值的操作都会引起错误。