在Javascript中,有全局作用域和函数作用域,ES6中又引入了块级作用域。
作用域就是变量可以访问的范围。
块级作用域
function test() {
let a = 'apple';
{
let b = 'pear';
console.log('b:', b);
}
console.log('a:', a);
console.log('b:', b);
}
test();
如图,let标识符和{} 创建了块级作用域,b变量只能在所属的块级作用域中访问。
作用域链
在查找变量的过程中,是从 当前的作用域 -> 父级作用域 -> 爷爷作用域...一步步向外查找的,查找过程中作用域像链条一样关联起来,这种隐性的作用域链接叫做作用域链。
function grandFather () {
let a = 'grandfather';
function father () {
let a = 'father';
function son () {
let a = 'son';
console.log( a);
}
son()
}
father();
}
grandFather();// son
function grandFather () {
let a = 'grandfather';
function father () {
let a = 'father';
function son () {
console.log( a);
}
son()
}
father();
}
grandFather();// father
function grandFather () {
let a = 'grandfather';
function father () {
function son () {
console.log( a);
}
son()
}
father();
}
grandFather();// grandfather
从上面的例子可以看到a变量的查找会是从son->father->grandFather。
闭包
在JavaScript中,在外部作用域访问内部作用域变量的方法叫做闭包。
如下图,可以看到father函数建立了一个Closure,里面有变量a、变量b。
function father() {
let a = 'apple';
let b = 'pear';
return function son () {
debugger
console.log(a);
console.log(b);
}
}
const fn = father();
fn();
闭包的本质是一组变量的集合,closure里面的变量、可以访问closure的函数,会存放到堆内存的老生代区域(对于老生代的内存设计可以看文章V8的垃圾回收机制)不会被垃圾回收机制主动回收,除非手动将其置为undefined或null, 即让其不再被引用。
闭包和全局变量一样,默认情况是在Tab页关闭的时候被回收。