什么是闭包
要理解闭包需要先理解变量作用域
作用域
明确几点:
- JavaScript的变量作用域是基于其特有的作用域链的
- JavaScript没有块级作用域
- 函数中声明的变量在整个函数中都有定义
按照作用域区分,变量有全局变量和局部变量,由于作用域链,函数内部是可以直接读取全局变量的,而函数外部无法直接读取函数内的局部变量。
闭包定义
「函数」和「函数内部能访问到的变量」(也叫环境)的总和,就是一个闭包。
本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁
闭包的用途
- 间接访问变量
- 另一个就是让这些变量的值始终保持在内存中
来看一个栗子
function fn(){
// default
var n=66;
// get
function getN(){
return n;
}
// set
function setN(num){
n=num;
}
// add
function addN(){
n++;
}
return {
getN:getN,
setN:setN,
addN:addN
}
}
var test=fn();
console.log(test.getN()); //66
test.setN(666);
console.log(test.getN()); // 666
test.addN();
console.log(test.getN()); // 667
我们通过fn暴露出来的接口访问到了函数内部的n,同时我们对n的值做了修改,可以看到n依然是存在于内存中的。
具体应用方面:
- 块级作用域,防止变量名污染
- 封装私有变量
闭包的注意事项
1)由于闭包会使得函数中的变量都被保存在内存中,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。
2)闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值。
闭包题目收集
让我们理论结合实(mian)践(shi)来理解以下闭包 以下皆为chrome浏览器环境运行的结果
第一题
var name = "The Window";
var object = {
name: "My Object",
getNameFunc: function() {
return function() {
return this.name;
};
}
};
console.log(object.getNameFunc()()); // The Window
拆成两步就很容易看懂
- 第一步:object.getNameFunc()等价于function(){ return this.name; }
- 第二步:(function(){ return this.name; })(),此时调用的是一个匿名行数,this指向的是window。
第二题
var name = "The Window";
var object = {
name: "My Object",
getNameFunc: function() {
var that = this;
return function() {
return that.name;
};
}
};
console.log(object.getNameFunc()()); // My Object
我们同样分两步
- 第一步:object.getNameFunc()===function(){ return that.name }。
- 第二步:(function(){ return that.name })(),此时的that在匿名函数里面并没有申明,所以它会向
上级作用域寻找that,也就是getNameFunc里面的that,that=this,此时的this指向调用getNameFunc的对象object。
第三题
function foo(x) {
var tmp = 3;
function bar(y) {
console.log(x + y + (++tmp));
}
bar(10);
}
foo(2); //16
foo(2); //16
foo(2); //16
foo(2); //16
这里只是函数调用哦,不是闭包哦
function foo(x) {
var tmp = 3;
return function (y) {
console.log(x + y + (++tmp));
}
}
var bar = foo(2);
bar(10); //16
bar(10); //17
bar(10); //18
bar(10); //19
当你return的是内部function时,就是一个闭包。
一个函数访问了它的外部变量,那么它就是一个闭包
第四题
function fun(n,o) {
console.log(o)
return {
fun:function(m){
return fun(m,n);
}
};
}
var a = fun(0); // undefined
a.fun(1); // 0
a.fun(2); // 0
a.fun(3); // 0
这题有点绕,我们来分解一下:
a = fun(0)等价于执行后,console.log(o)没有在当前以及父级作用域中寻找到o,所以输出的是undefined,同时a被赋值为{fun:function(m){return fun(m,0)}}。a.fun(1)执行,简化后为(function(1){return fun(1,0)})()===fun(1,0),***当前作用域里面没有fun,最后找到了顶级的function fun(n,o)***,执行console.log(0)
function fun(n,o) {
console.log(o)
return {
fun:function(m){
return fun(m,n);
}
};
}
var b = fun(0).fun(1).fun(2).fun(3);
// undefined
// 0
// 1
// 2
这链式看的我好慌,我们继续分解一下
fun(0)会输出o,此时没有o所以为undefind,同时fun(0)表达式执行结果为{fun:function(m){return fun(m,0)}}(先忽略掉后面的fun(1)、fun(2)、fun(3))。- 再执行
fun(1)等价于执行(function(1){return fun(1,0)})(),继续输出o,此时o为0,表达式简化为{fun:function(m){ return fun(m,1)}} - 同理继续执行,依次输出1,2
参考资料
- http://www.ruanyifeng.com/blog/2009/08/learning_javascript_closures.html
- https://zhuanlan.zhihu.com/p/22486908?refer=study-fe
- http://www.cnblogs.com/frankfang/archive/2011/08/03/2125663.html