Javascript闭包到底是什么
有一年以上的Javascript,
关闭总让两夫妻和尚失去理智。继续接触一些封闭的知识,还做了几次,因为他们不了解超过一年也看到一些信息关闭造成的
错误,但还不是很了解,最近看到了附录jQuery基础
教程,在简单和容易理解Javascript闭包附录A找到,所以Jiehuaxianfo总结。
1。定义
闭包:在另一个
函数域中有权访问变量的函数。
创建闭包的常用
方法是在一个函数中创建另一个函数。
直接的例子
函数A(){
var I=0;
函数B(){
警报(+1);
}
返回B;
}
var(a);
(c);
这个代码有两个特点:
1)函数B嵌套在函数A中。
2)函数A返回函数B。
这样,在
执行var C =()之后,变量C实际上指向函数B,然后执行C()将弹出一个窗口来
显示i的值(第一次是1)。这个代码实际上创建了一个壁橱,为什么因为函数A以外的变量C是函数A中的函数B,也就是说:
当函数a的内部函数b由函数a之外的变量引用时,就会创建闭包。
我猜你还是不了解闭包,因为你不知道闭包是什么,让我们继续探索。
2。关闭的效果是什么
总之,封闭的
作用是在执行和返回,关闭使Javascript的垃圾收集机制GC不收回被占用的资源,因为B的内部函数的执行取决于变量,这是一个非常简单的描述封闭作用,不专业、严谨,但它可能意味着它需要一个渐进的过程,理解闭包。
在上面的例子中,由于闭包的存在导致函数A的返回,所以A中的i总是存在的,所以每次C()和i在1之后被添加到警报时,警报的值就会熄灭。
假设另一个
情况是,如果A返回不是函数B,情况完全不同,因为A被执行后,B不返回A的外部世界,而是由A引用,同时A只被B引用,因此,a和b函数互相引用,但不受外界干扰。函数a和b将由GC
恢复(稍后将
详细描述Javascript的垃圾收集机制)。
3,闭包中的微观世界
如果我们想进一步了解关系闭包和函数和嵌套函数B,我们需要引入其他几个概念:函数的执行环境(执行上下文)、活动对象(称为对象),范围(范围),作用域链(作用域链)。这些概念是通过对函数来自定义执行过程为例。
1)当定义函数A时,js解释器将
设置函数A的范围链(范围链)来定义a所在的环境,如果a是全局函数,则范围链只有窗口对象。
2),当函数被执行,一个进入相应的执行环境(执行上下文)。
3)、在创建执行环境的过程中,一个范围的
属性是第一个加入一个,的范围,其价值是在第一步的范围链。是的a.scope = A的作用域链
4),然后执行环境创建一个活动对象(称为对象),活动对象也具有属性的对象,但它没有原型并不能直接通过Javascript代码访问。活动后的对象被创建,活动对象是在这一点上说的A.范围链的顶端,一个包含两个对象的作用域链:一、窗口对象的主动对象。
5),下一步是在活动对象上添加一个
参数属性,它
保存函数A被调用时传递的参数。
6)最后,将A的所有函数的引用和内部函数B的引用添加到A的活动对象中,在这个
步骤中完成函数B的定义,作为第三步,函数B的函数域链被设置为B所定义的环境,即A的范围。
在这种情况下,整体的
功能是完成从步骤定义来执行。在这个时候,一个返回函数B to C的
参考函数,以及函数B的作用域链包含的功能活动对象的引用,也就是说,B可以访问所有的变量和函数A B C函数定义和函数引用的,B是依赖于功能,所以功能将不能恢复的GC后返回。
当函数B被执行,它将为上述步骤相同。因此,B的执行范围链包含3个对象:B对象性活动的对象,一个窗口,而B函数访问一个变量,
搜索顺序是搜索对象本身,如果有,如果有没有回报,将继续寻找为活动对象的查找功能,一个,直到你找到。如果整个作用域链中不难
发现,未返回。如果函数B有一个原型对象,然后找到自己的原型对象后发现自己的活动对象,然后继续看,这是Javascript变量查找机制。
4、关闭应用场景
1)
保护功能中变量的安全性,在第一例中,A只能用函数B访问,不能通过其他方式访问,从而保护I的安全性。
2)在
内存中保持一个变量,仍然是一个先例,因为函数的闭包总是存在于内存中,所以执行时间(c)会给我1。
以上两点是闭包的最基本的应用场景,许多经典案例
都是从这里派生出来的。
5,Javascript的垃圾收集机制
在Javascript中,如果一个对象不再被引用,该对象将被回收的方法。如果两个对象互相引用,不再被第三引用,然后相互引用的对象将被回收。因为函数是由B引用,B被C以外的一个,这就是为什么功能不再生后执行。
Javascript中没有块级范围。通常,为了声明函数仅用于函数的一些变量,我们将使用闭包,这样我们就可以大幅度减少全局范围内的变量,并净化全局范围。
使用闭包的好处当然是成本,这是内存的内存。
你如何理解上面的句子
每个函数的执行创建一个与函数或函数执行上下文相关的函数执行环境,在这个执行上下文中有一个属性范围链(范围链指针)。这个指针指向一个作用域链结构,并在作用域链指针也指在每个范围对应的对象。通常,一个函数创建的执行环境和相应的作用域链通话开始时,释放的执行上下文和相应的作用域链
空间的函数执行后。
语句函数
函数测试(){
Hello World;
console.log(STR);
}
调用函数
测试();
下面的图的结构是在调用函数时在内存中生成的:
但封闭的情况有点特殊,因为闭包函数可以访问外部函数的变量,所以在执行端的外部功能,活动的范围和对象将不会被释放(注意,外部函数的执行范围链和相应的执行环境将结束后,毁灭但引用范围)连锁关闭功能关闭,直到被破坏,活动范围会被外部函数的对象。这就是为什么关闭占用内存。
所以使用闭包是好的和坏的,滥用闭包会导致大量的内存消耗。
使用闭包还有其他的副作用,可以说是bug,或者没有,可能对一个相对不同的业务有不同的看法。
这个副作用是闭包函数只能取到外部函数变量的最终值。
测试代码如下:(这里使用jQuery对象)
缺陷关闭
(函数($){)
var =新数组(),
我= 0;
为了(;;我< 10;我+ +){
结果{函数(){
还我;
};
}
美元。RES1 =结果;
}(jQuery);
执行数组的函数
美元。RES1 { 0 }();
上面的代码首先通过匿名函数打开一个私人领域,这个匿名函数外部函数就是我们上面所说的,外部函数有一个参数$,它也定义了变量的结果和我,通过循环分配一个匿名函数结果数组,这个匿名函数,他访问了,外部函数的变量的我,理论而阵列将返回相应的数组下标值,实际情况却不如。
上的代码的执行结果res10美元10。
为什么会这样,因为我的最终值是10。
下面,让我们详细描述当执行上面的代码时内存中发生的事情。
那么这种副作用有没有办法修复呢当然.
我们可以通过以下代码实现我们的期望。
修补缺陷关闭
(函数($){)
var =新数组(),
我= 0;
为了(;;我< 10;我+ +){
结果{函数=(数字){
返回函数(){
返回num;
}
}(一);
}
美元。RES2 =结果;
}(jQuery);
调用函数
console.log(美元。RES2 { 0 }());
上面代码中的内存中发生了什么我们也使用下面的
图片来详细解释,不难理解下面的图表。
6。简单的例子
首先,从一个经典的错误,页面上有几个div,我们想绑定一个onclick方法给他们,所以我们有下面的代码
0123
0123
$(
文档)Ready(函数(){)
VaR的跨度=美元(# divtest跨度);
对于(var i = 0;i < spans.length;i++){
跨越{我}。onclick =函数(){
警报(一);
}
}
});
简单的函数是错误的,每次警报值是4,简单的
修改就可以了。
无功spans2 = $(# divtest2跨度);
$(文档)Ready(函数(){)
对于(var i = 0;i < spans2.length;i++){
(函数(数字){
我spans2 { }。onclick =函数(){
警报(努姆);
}
})(一);
}
});
7。内部功能
让我们从一些基本知识开始,首先要理解内部函数。内部函数是在另一个函数中定义的函数:
功能outerfn(){
FunctioninnerFn(){ }
}
innerfn是笼罩在内部功能范围outerfn。这意味着调用innerfn outerfn内是有效的,同时呼吁innerfn外outerfn无效。下面的代码会导致Javascript错误:
功能outerfn(){
document.write(外部函数);
功能innerfn(){
document.write(内在功能);
}
}
InnerFn(); / /捕获的referenceerror:innerfn未定义
但叫innerfn在outerfn可以成功
运行:
功能outerfn(){
document.write(外部函数);
功能innerfn(){
document.write(内在功能);
}
InnerFn();
}
outerfn();
8。大逃亡(内部功能是如何逃避外部功能的)
Javascript允许开发人员像任何
类型的数据那样传递函数,也就是说,Javascript中的内部函数可以避免定义外部函数。
有许多方法可以逃避,例如,您可以指定一个内部函数到全局变量:
全局变量的定义转义
无功globalvar;
功能outerfn(){
document.write(外部函数);
功能innerfn(){
document.write(内在功能);
}
globalvar = innerfn;
}
outerfn(); / /外部函数内部函数
globalvar(); / /外部函数内部函数
InnerFn(); / / referenceerror:innerfn未定义
当outerfn被称为全局变量globalvar被修改,当参考了innerfn,然后globalvar为innerfn之称。在这种情况下,调用innerfn直接外outerfn仍然会导致错误,因为内部函数的全局变量保存在不参考,但功能
名字还存在于outerfn范围。
内部函数引用也可以通过父函数的返回值获得。
功能outerfn(){
document.write(外部函数);
功能innerfn(){
document.write(内在功能);
}
返回innerfn;
}
无功fnref = outerfn();
FnRef();
而不是修改全局变量在outerfn,它返回从outerfn的innerfn参考参考。这可以通过调用outerfn获得,并参考可以保存在一个变量。
即使我们把函数的范围,我们仍然可以通过引用调用内部函数的事实,这意味着只要有调用内部函数的可能性,Javascript需要保留引用的函数,Javascript运行时需要跟踪所有的变量引用的内部函数,直到最后变陈旧。Javascript的垃圾收集器可以释放相应的内存空间(红色部分是了解闭包的关键)。
我有一个长期的关系和关闭说,闭包是函数具有访问另一个功能域变量和创建闭包的常用的方法是在函数中创建一个函数,是内部功能我们上面说的,所以刚才说的不是废话,也关系到^ _ ^关闭
9。变量的范围
内部函数也可以有自己的变量,这些变量仅限于内部函数的作用域:
功能outerfn(){
document.write(外部函数);
功能innerfn(){
var innerVar = 0;
innerVar + +;
document.write(内在功能 T );
document.write(innerVar = innerVar + +);
}
返回innerfn;
}
无功fnref = outerfn();
FnRef();
FnRef();
无功fnref2 = outerfn();
fnref2();
fnref2();
每一次的内部函数称为参考或其他手段,新innerVar创建变量,然后1,和最终的显示
外部函数
内部功能innerVar = 1
内部功能innerVar = 1
外部函数
内部功能innerVar = 1
内部功能innerVar = 1
内部函数也可以引用与其他函数一样的全局变量。
无功globalvar = 0;
功能outerfn(){
document.write(外部函数);
功能innerfn(){
globalvar + +;
document.write(内在功能 T );
document.write(globalvar =+ globalvar + );
}
返回innerfn;
}
无功fnref = outerfn();
FnRef();
FnRef();
无功fnref2 = outerfn();
fnref2();
fnref2();
每次调用内部函数时,全局变量的值将不断递增。
外部函数
内部功能globalvar = 1
内部功能globalvar = 2
外部函数
内部功能globalvar = 3
内部功能globalvar = 4
但是,如果这个变量是父函数的局部变量呢因为内部函数将引用父函数的范围,它可以用来理解活动链和活动对象的知识,内部函数也可以引用这些变量。
功能outerfn(){
无功outervar = 0;
document.write(外部函数);
功能innerfn(){
outervar + +;
document.write(内在功能 T );
document.write(outervar =+ outervar + );
}
返回innerfn;
}
无功fnref = outerfn();
FnRef();
FnRef();
无功fnref2 = outerfn();
fnref2();
fnref2();
这是一个非常有趣的结果,也许超出我们的预料。
外部函数
内部功能outervar = 1
内部功能outervar = 2
外部函数
内部功能outervar = 1
内部功能outervar = 2
我们看到的是前两种情况的
影响,并innerfn是通过调用每个参考
独立增量outervar。也就是说,第二电话outerfn不继续遵循outervar价值,但创建并绑定了一个新的outervar实例在第二函数调用的范围,和两个计数器完全无关。
当内部函数在其作用域的范围内被引用时,内部函数就会被关闭。在这种情况下,我们既不称内部函数的局部变量也不称其参数的变量为自由变量。它调用外部函数的一个封闭的封闭环境调用环境。在本质上,如果内部功能是指在外部函数的变量,它相当于授权变量被拖延。因此,当外部函数调用完成后,这些变量的内存不会被释放(最终值保存和关闭),还需要使用它们。
10之间的相互作用。关闭
当有多个内部功能,很可能有意外关闭。我们定义一个递增函数,这个函数的增量是2
功能outerfn(){
无功outervar = 0;
document.write(外部函数);
功能innerfn1(){
outervar + +;
document.write(内在功能1 T );
document.write(outervar =+ outervar + );
}
功能innerfn2(){
outervar = 2;
document.write(内在功能2 T );
document.write(outervar =+ outervar + );
}
返回{FN1 ):innerfn1,Fn2:innerfn2 };
}
无功fnref = outerfn();
fnref.fn1();
fnref.fn2();
fnref.fn1();
无功fnref2 = outerfn();
fnref2.fn1();
fnref2.fn2();
fnref2.fn1();
我们将引用映射到两个内部函数,我们可以引用引用来调用任何内部函数,并将结果调用:
外部函数
内部功能的1 outervar = 1
内部功能的2 outervar = 3
内部功能的1 outervar = 4
外部函数
内部功能的1 outervar = 1
内部功能的2 outervar = 3
内部功能的1 outervar = 4
innerfn1和innerfn2指的是同一个局部变量,所以他们共享同一个封闭的环境,增加innerfn1当一时间outervar,innerfn2设置了很长一段时间outervar新起点,反之亦然。我们也看到一个新的实例,随后调用outerfn将关闭,但也创造一个封闭的环境,主要是创造一个新的对象,自由变量的对象的实例变量和实例方法是该对象的关闭,这些变量都是私人的,因为不在其外部包裹的范围直接从这些变量,从而保证了面向对象的数据保护。
11。混乱
现在我们可以回顾一下示例的开始,并且很容易理解为什么第一种写作风格每次都会保持4级警戒。
对于(var i = 0;i < spans.length;i++){
跨越{我}。onclick =函数(){
警报(一);
}
}
上面的代码将在页面加载后执行,当i的值为4时的判断
条件不成立,这个周期结束,但因为在这个时间内功能为每个跨度的onclick方法,所以我是关闭(关闭
传输参考参考),记忆没有毁灭,我的价值将是4,直到
程序改变或摧毁所有onclick函数(活性功能分配到一个空页或
卸载)将被收回。所以我们每次点击的跨度,onclick函数看起来为i的值(范围链参考的方式)。
检查等于4,然后给我们警报。
第二种方法是使用立即执行函数,在括号中创建一个层闭包函数语句,然后是括号调用,此时立即执行i参数函数,Num每次保存i的值。
你必须知道闭包和我一样。当然,我们需要清楚地了解函数的执行环境和范围链。