JavaScript 有两种作用域:全局作用域和函数作用域。
var n = 1;
function f1() {
console.log(n);
}
f1() // 1
函数内部可以直接读取全局变量,但是函数外部不能读取函数内部声明的变量。
function f1() {
var n = 1;
}
console.log(n)
// Uncaught ReferenceError: n is not defined
如果出于种种原因,需要得到函数内的局部变量。正常情况下,这是办不到的,只有通过变通方法才能实现。那就是在函数的内部,再定义一个函数。
function f1() {
var n = 1;
function f2() {
console.log(n); // 1
}
}
JavaScript 语言特有的”链式作用域”结构(chain scope),子对象会一级一级地向上寻找所有父对象的变量。父对象的所有变量,对子对象都是可见的,反之则不成立。因此对于上述例子中,f2
可以使用f1
作用域中的变量。
function f1() {
var n = 1;
function f2() {
console.log(n);
}
return f2;
}
var result = f1();
result(); // 1
闭包就是函数f2
,即能够读取其他函数内部变量的函数。
由于在 JavaScript 语言中,只有函数内部的子函数才能读取内部变量,因此可以把闭包简单理解成“定义在一个函数内部的函数”。
闭包最大的特点,就是它可以“记住”诞生的环境,比如f2
记住了它诞生的环境f1
,所以从f2
可以得到f1的内部变量。
在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。
关于作用域链,只要记住先从自己的作用域下找,找不到从上层作用域找就可以了。
题1
var a = 1
function fn1(){
function fn2(){
console.log(a)
}
function fn3(){
var a = 4
fn2()
}
var a = 2
return fn3
}
var fn = fn1()
fn() // 找出输出函数是 fn2,但是其作用域下无 a 的声明,再找上一级 fn1,a=2
题2
var a = 1
function fn1(){
function fn3(){
var a = 4
fn2()
}
var a = 2
return fn3
}
function fn2(){
console.log(a)
}
var fn = fn1()
fn() // fn2 里没有,全局 a=1
题3
var a = 1
function fn1(){
function fn3(){
function fn2(){
console.log(a)
}
var a
fn2()
a = 4
}
var a = 2
return fn3
}
var fn = fn1()
fn() // fn2 没有,fn3 有,但是根据声明顺序,最终结果是 undefined