函数的执行依赖于作用域,这个作用域时在函数定义时决定的,而不是函数调用时决定的。函数可以通过作用域链相互关联起来,函数体内部的变量都可以保存在函数作用域内,这种特性在计算机科学中就称之为“闭包”。可以说,所有的JavaScript都是闭包。
首先熟悉作用域:
var scope = "global";
function constructFunction(){
var scope = "local";
function f(){return scope};
return f();
}
constructFunction(); //local
在上述代码中,执行函数constructFunction,执行函数f,返回函数f的返回值,返回函数constructFunction的值。
作用
闭包可以从外部读取内部数据
var scope = "global";
function constructFunction(){
var scope = "local";
function f(){return scope};
return f;
}
constructFunction()(); //local
JavaScript函数执行的过程中用到了作用域链,作用域链是在函数定义的时候创建的,嵌套函数f定义在作用域链中,其中的变量是局部变量,无论何时何地执行函数f(),这种绑定在执行时都是有效的。换言之,闭包可以捕捉到局部变量,并一直保存下来,看起来就像变量绑定到了在其中定义他们的外部函数
闭包不会破坏作用域链。
“外部函数定义的局部变量在函数返回之后就不存在了”这句话在JavaScript中是不对的。
每次调用函数的时候,就会创建一个新的对象来保存局部变量,把整合各对象添加到作用域中。当函数返回的时候,就从作用域链中将这个绑定变量的对象删除。如果不存在嵌套的函数,也没有其他引用指向这个绑定对象,那么他就会被垃圾回收。如果定义了一个嵌套函数,每个嵌套的函数都对应一个作用域链,并且这个作用域链指向一个变量绑定对象。但如果这些嵌套的函数对象在外部函数中保存下来,那么他们也会和所指向的变量绑定对象一样被垃圾回收。但是如果这个函数定义了嵌套函数,并将它作为返回值返回或者存储在某个属性中,这是就会有一个外部引用指向这个嵌套的函数,就不会被垃圾回收。
闭包的安全性:将变量放在函数内部,避免将变量重置。
var cnt = (function(){
var count = 0;
return function(){
return count++;
}
}());
cnt(); //0
cnt(); //1
内部私有属性的共享
//constfuncs方法循环创建了十个闭包,存储在数组中,由于这些闭包都是在同一个函数中定义的,所以函数中的变量i共享
function constfuncs(){
var funcs = [];
for(var i = 0; i < 10; i++){
funcs[i] = function(){return i}
}
return funcs;
}
var funcs = constfuncs();
funcs[5]();
闭包使用注意事项:
- 大量的闭包会造成网页的性能问题,容易导致内存泄漏。
- 闭包可以改变外部函数的变量
- 需要注意this的指向,有调用对象指向对象,没有调用对象就指向window,举个栗子。
var name = "global";
var object = {
name : "local",
getNameFunc : function(){
console.log(this);
return function(){
console.log(this);
return this.name;
};
}
};
console.log(object.getNameFunc()());
控制台输出如下:
object调用getNameFunc,这个时候this指向object,执行返回匿名函数function的引用,此时的执行环境就变成了全局,this指向了window。
修改:
var name = "global";
var object = {
name : "local",
getNameFunc : function(){
var self = this;
console.log(this);
return function(){
console.log(self);
return self.name;
};
}
};
console.log(object.getNameFunc()());
控制台输出: