预备知识
首先要明白,JS的作用域是静态作用域。即当定义一个function时,function的作用域是定义function时所在的作用域,而不是调用function时所在的作用域。
静态作用域指的是一段代码,在它执行之前就已经确定了它的作用域,简单来说就是在执行之前就确定了它可以应用哪些地方的作用域(变量)。
// 静态作用域:
var a = 10;
function fn() {
var b = 1;
console.log(a + b);
}
fn(); // 11
在创建(定义)fn函数的时候就已经确定了它可以作用全局作用域中的变量,如果函数fn里面有变量a就直接操作变量a,如果没有就往上一级查找,这就是静态作用域。
动态作用域指的是函数的作用域是在函数调用的时候才决定的。
// 动态作用域:
function foo() {
console.log(a);
}
function bar() {
var a = 3;
foo();
}
var a = 2;
bar(); // 2;
bar 调用,bar里面foo被调用,foo函数需要查找变量a,由于JavaScript是词法作用域(即静态作用域),foo被解析是在定义它的全局作用域中进行的,.所以只能在全局作用域中找a,输出结果为2,而非bar作用域中的a。如果js采用的时动态作用域,那么foo在bar中调用,就会先在bar中查询a,输出为3。
什么是闭包?
从结构上来说就是一个函数中嵌套另一个函数,并且父函数返回子函数,那么每次调用父函数都会创建一个新的闭包,把闭包赋值给一个新的全局变量,那么闭包所指向的自由变量就会被保存下来,后续调用每个闭包函数都会更新闭包内的变量。
function iCantThinkOfAName(num, obj) {
// This array variable, along with the 2 parameters passed in,
// are 'captured' by the nested function 'doSomething'
var array = [1, 2, 3];
function doSomething(i) {
num += i;
array.push(num);
console.log('num: ' + num);
console.log('array: ' + array);
console.log('obj.value: ' + obj.value);
}
return doSomething;
}
var referenceObject = { value: 10 };
var foo = iCantThinkOfAName(2, referenceObject); // closure #1
var bar = iCantThinkOfAName(6, referenceObject); // closure #2
foo(2);
/*
num: 4
array: 1,2,3,4
obj.value: 10
*/
bar(2);
/*
num: 8
array: 1,2,3,8
obj.value: 10
*/
referenceObject.value++;
foo(4);
/*
num: 8
array: 1,2,3,4,8
obj.value: 11
*/
bar(4);
/*
num: 12
array: 1,2,3,8,12
obj.value: 11
*/
这里foo和bar就是两个闭包。
MDN对闭包的定义是:
闭包是指向独立(自由)变量的函数,换句话说,定义在闭包里的函数“记住”了它创建时的环境。父函数调用的子函数记住了定义子函数时的环境(作用域),调用这个父函数产生闭包,它是有权访问该作用域中变量的函数。
(注:自由变量是指那些既不是局部变量,也不是作为参数传递的变量)
var num=10;
function checkNumber() {
num++;
console.log(num);
}
function numberGenerator() {
var num = 1;
checkNumber;
return checkNumber;
}
var number = numberGenerator();
number();//11
number();//12
number();//13
var num=10;
function numberGenerator() {
// num是局部“自由”变量
var num = 1;
function checkNumber() {
num++;
console.log(num);
}
return checkNumber;
}
var number = numberGenerator();
number();//2
number();//3
number();//4
品一下这两个代码作用域的不同;
当调用一个function时,就会创建一个新的作用域,在这个作用域中用var定义的变量就是局部变量,一般情况下function执行结束之后这个调用时所创建的作用域就会被系统回收。
但是在function嵌套定义时,如果里面定义的function被保存了下来(如:赋给一个全局变量)。那么它所在的作用域(调用外面function时所创建的作用域)也必须一起保存下来(也就是说外面function执行结束时这个调用时所创建的作用域不会被系统回收,要等到保存下来的function被系统回收时系统一起回收)。 作用域保存下来在这个作用域中的所有局部变量当然也都保存了下来。