函数作用域:变量在定义的环境中以及嵌套的子函数中处处可见;
块级作用域:变量在离开定义的块级代码后立即被回收。
函数作用域
在ES6之前,js的作用域只有两种:函数作用域和全局作用域。使用var声明的变量,都存在变量提升的过程。
console.log(a); //undefined
console.log(c); //undefined
console.log(d); //function d() {}
console.log(b); //报错,b is not defined
var a = 0;
let b = 1;
var c = function () {
};
function d() {
}
由此可见使用let声明的变量不会提升,函数声明会完全提升。上面的代码等价于:
var a;
var c;
function d() {}
console.log(a);
console.log(c);
console.log(d);
console.log(b);
a = 0;
let b = 1;
c = function () {
};
块级作用域
ES6中定义了块级作用域,使用let声明的变量只能在块级作用域里访问,有“暂时性死区”的特性(也就是说声明前不可用)。
'use strict';
var test = 1;
function func(){
//打印test的值
console.log(test); //报错, test is not defined.
let test = 2;
};
func();
函数会从自身的活动对象开始,一层层向上寻找自己所需的变量,该函数在寻找test时发现,自己的作用域里声明了let,它就不会再往上找,而let在声明之前是不可用的,所以就会报错。
自己的理解
虽然《js高程》里说,ES6之前,js没有块级作用域。但我认为with语句和try-catch语句有类似于块级作用域的地方。
var l = {
x: 0,
y: 1
};
function f() {
with(l) {
console.log(x); //0
var a = 1;
}
cosnole.log(a); //1
console.log(x); //报错,x is not defined.
}
f();
函数f利用with语句扩展了自己的作用域链,相当于with语句开始执行时,参数l中所包含的所有变量都被临时加入到该函数的活动对象中,等到语句执行结束,它们就会被移除。它们只在with语句块中有效。还有一点就是,with语句中声明的变量a,使用var声明的变量会自动被添加到离它最近的环境中,离a最近的环境就是函数的局部环境,所以a就会被添加到函数的活动对象中,即使在with语句之外也能被访问。