var a = "Tom"; //全局变量
function curr () {
var a = "Bob"; //局部变量
function () {
return a;
}
return f(); //将f函数执行的结果返回 Bob
}
curr();
var a = "Tom";
function curr () {
var a = "Bob";
function f () {
return a;
}
return f; //返回的是这个f函数对象
}
curr()(); //f()
当curr()函数执行完毕后,返回的是f函数对象,后面再跟一个圆括号,就是调用f函数。定义curr函数时,创建了一个作用域链,调用curr函数时,会新创建一个对象用来保存这个函数的局部变量和参数,并把这个对象保存在这个作用域上,函数f也定义在了这个作用域链中。当curr()函数执行后,其作用域链依然有效,上面还保存着其局部变量a,嵌套函数f也还在了这个作用域链上,因此此时局部变量a和f函数还是绑定在一起的。最后执行f函数的结果是"Bob"(引用的变量a就是这个局部变量)。
词法作用域规则:JavaScript函数执行的时候用到了作用域链,这个作用域链在定义函数的时候创建的,嵌套的f函数定义在这个作用域链中,其中变量a是局部变量,无论何时何地执行f函数,这种绑定在执行f时依然有效。
要理解闭包,首先理解词法作用域链规则:函数定义时所创建的作用域链到函数执行时依然有效。
首先回顾一下作用域链:定义函数时就会创建作用域链,将作用域链看作是对象列表或链列表。每次调用函数时,就会创建一个新对象用来保存(定义)局部变量,并把这个对象添加到作用域链中。当函数返回时,就将这个对象从作用域链中删除:
1、如果这个函数没有定义嵌套的函数,也没有其它引用指向这个对象,那么这个对象就会被回收掉。functino consFunc () {
var arr = [];
for (var i = 0; i < 10; i ++) {
arr[i] = function () {
return i;
};
}
return arr;
}
var a = consFunc(); //调用并返回consFunc
console.log(a[5]()); //10 看来没有返回我们想要的值
function consFun () {
var arr = [];
for (var i = 0; i < 10; i ++) {
arr[i] = function (num) {
return function () {
return num;
};
}(i);
}
return arr;
}
var a = consFunc();
console.log(a[5]()); //5
function consFun () {
var arr = [];
for (var i = 0; i < 10; i ++) {
arr[i] = function () { //调用匿名函数自身,返回的是值。
return i;
}();
}
return arr;
}
var a = consFunc();
console.log(a[5]); //5
var name = "hello window";
var obj = {
name : "hello obj",
getName : function () {
return function () {
return this.name;
}
}
};
console.log(obj.getName()()); //hello window
上例定义了一个全局变量name和一个对象obj。当obj.getName()方法,返回的是一个匿名函数对象,而匿名函数又返回"this.name",由于getName返回的是一个函数,因此"obj.getName()()"就会立即调用getName返回的匿名函数,此时匿名函数是作为全局对象window的方法调用的,因此匿名函数中的this对象指向window对象,以"this.name"就相当于"window.name",所以最后返回的结果是"hello window"。
上述例子在开始创建前,想要的是匿名函数返回obj对象中的name属性。但匿名函数中的this对象是指向window对象的,无法直接引用到obj对象。我们知道在"obj.getName()"中,getName函数是作为obj对象的方法被调用的,那么其this对象就指向了obj对象,那我们可不可以这样想:如果能使匿名函数(内部函数)引用到getName函数的this对象的话,那么间接地不就引用到了obj对象了么,匿名函数最后返回的不就是想要的"hello obj"么。
var name = "hello window";
var obj = {
name : "hello obj",
getName : function () {
var that = this; //将getName函数的this对象保存在一个变量里。
return function () {
return that.name; //由闭包函数去访问这个变量。
}
}
};
console.log(obj.getName()()); //hello obj
这样,将getName函数的this对象保存在一个变量里,而这个变量可被闭包函数访问,当闭包函数搜索that.name时,就会搜索到getName函数的this.name,getName函数的this对象是引用到obj对象的(that就相当于此this),即使这个函数返回后,that也是引用到obj对象的,那么最后闭包函数返回的就是"hello obj"。
var name = "hello window";
var obj = {
name : "hello obj",
getName : function () {
var that = this; //将getName函数的this对象保存在一个变量里。
return function () {
return that.name + "-" + this.name; // 此处的this对象是指向window对象的。
}
}
};
console.log(obj.getName()()); //hello obj - hello window