相信很多人只知道闭包这个词但是具体是怎么回事就不太清楚了,最近在群里有很多小伙伴讨论这个问题但还是蒙眬眬的赶脚。索性就写了这篇文章来帮助大家一起理解闭包。
变量作用域
闭包其实想明白了很简单,但是在理解闭包之前,我们先温习一下作用域的概念不多说 直接上代码来的直接
- 全局变量
var a = 1;
function f1(){
a++;
console.log(a);
}
f1();//2
f1();//3
- 局部变量
function f1(){
var a = 1;
a++;
console.log(a);
}
f1();//2
f1();//2
我们大家都知道在函数内部定义的变量(使用var
)为局部变量,在函数内部可以访问函数外部的变量和函数,但是在函数外部就访问不了函数内部的变量和函数了
闭包就是能够让我们在函数外部能访问到函数内部的变量和函数下面我们看一个史上最简单的一个闭包,真的不能再简单,你相信我
function f1(){
var str ='我在函数里面,外面的坏人找不到我!!哈哈';
var aa = function(){
alert(str);
}
return aa;
}
var b = f1();
b();
在这个例子中f1中的函数aa
就是一个闭包,什么 aa
不是一个函数么?那闭包也是一个函数咯? 对,闭包说白了 就是一个函数,只不过这个函数比较特殊而已,特殊到什么地方,从例子不难发现 aa
是声明在函数f1
内部的,同时又把这个函数aa
当成返回值给return 出来了。然后 我们执行 var b = f1()
就相当于 var b = aa
但是 aa
是在函数内部定义的 通常情况下外部是无法访问的,这个时候我们就把这个函数aa
当成函数的返回值给抛出去,这样在外层就能访问到 aa
了 由于 aa
又是在函数f1
内部声明的 所以 我们变相的可以访问到函数f1
中的变量。这就是闭包最基本的作用
同时它还有很多好处:
- 希望一个变量长期驻扎在内存中
- 避免全局变量的污染
- 私有成员的存在
闭包的特性:
1.函数嵌套函数
2.函数内部可以引用外部的参数和变量
3.参数和变量不会被垃圾回收机制回收
简单的相信大家都能看懂,但是遇见复杂的就蒙蔽的,特别是和对象在一块
就拿今天群里讨论的一个例子来说吧
var sister = '大桃花';
var obj = {
sister:'大妹哒',
sis:function(){
var sister = '小妹哒';
return function(){
console.log(sister);
console.log(this.sister);
};
}
};
function place(){
var sister = '大福晋';
var girl = obj.sis();
girl();
}
place();
在这个例子中 糊弄人的就是 sister
在不同的作用域中都有定义,大兄弟莫慌张!我们一起来揭开它这神秘的面纱,看看到底是大妹哒还是小妹哒,place
调用 place
内部声明了一个 sister
然后紧接着 又定义一个 girl = obj.sis()
;obj的sis方法 的返回值是一个函数 也就是说 girl
最后被赋值为一个函数,先不要管这个函数是在哪,反正就是一个函数,然后紧接着girl
被调用,再被调用之前 你要明白下面的这些东西函数声明的时候会创建一个作用域链 这个链条上面都有神马东西
this
- 函数内部的变量 --->此函数声明时所在的作用域中的变量 --->....--->
window
(浏览器中的最顶层) - 函数参数(
arguments
)
这3项中其中后两项是在函数声明的时候就被确定下来了,也就是说 函数的作用域链条是在函数声明的时候就确定了,而非是函数调用的时候,然而this
在函数声明是时不确定,而是在函数被调用的时候所确定的,至于怎么确定那就要看函数被调用时所在的对象,函数被调用无非就2中形式:
- 直接调用(比如
getName()
) - 通过对象.函数的形式调用(比如
obj.getName()
)
这两种方式调用 第一种的调用方式中 函数中的this
是永远指向 window
的(这里抛开 call
和apply
);而第二种调用方式 函数中(这里通常称作方法)的this 是指向点前面的obj
下面我们再看上面的例子
先说 console.log(sister)
由于girl是一个函数的引用 ,而这个函数是在 obj.sis 函数中声明的 只不过是一个匿名函数而已,由于这个匿名函数内部和匿名函数的参数中都没有 sister
这个变量, 所以顺着作用域链往上查找 找到 obj.sis
函数的作用中声明了 sister
这个变量 找到之后就不再去往上查找(js中的很多机制都是这中情况 比如 原型集成中对象属性的查找规则也类似这样)这个时候 控制台就会输出 obj.sis
中定义的 sister
变量的值。
再来看 console.log(this.sister)
girl
这个函数被调用时的环境 是 place
函数内部 而 place
是在整个window
作用域下的 所以 这里的this
是指向 window
的 因而 控制台输出结果 会是 window
下的 sister
再看下面的例子
var a = 'window';
var sys = {
a:'sys',
getA:function(){
alert(this.a);
},
getAA:function(){
return function(){
alert(this.a);
}
}
}
sys.getA();//sys
sys.getAA()();//window
这个例子中sys.getA()
不用说 大家都能理解,第二个 可以这么来看 先看sys.getAA()
这个结果其实是一个函数你可以把他当成一个函数的名字 a
,变换一下写法 就是 sys.getAA()()
等价于:
var a = sys.getAA();
a();
由于 这是函数的直接调用 所以 这里的this
指向 window
最后结果 就是 'window'.
就先说这么多吧 思路可能有点混乱,如果你有什么好的建议或者意见,欢迎指正!