闭包
是指在一个函数中声明一个函数,在外部函数结束的时候返回内部函数的引用,那么在执行外部函数的时候就会
产生一个内部函数的引用,因为外部函数执行完成,理论上外部函数的ao的引用是应该被删除或者说释放的。
但因为有了内部函数的引用,这个内部函数在声明的时候就已经指向了外部函数的ao以及go,所以,即使外部函数
ao的引用已经释放,由于内部函数被保存在了外部,导致外部函数申明与执行所产生的ao被保留下来。
<body> <script type="text/javascript"> function a(){ function b(){ var bbb = 234; consloet.log(aaa); };
var a = 123 return b; } var golb = 100; var demo = a(); demo(); </script> </body>
此例的输出结果是:123
按原来的理解,b函数中是没有a这个变量的,正常如果没有外部的a函数,就会报错 a is not define
但是这里,由于在a执行完的时候,把b的引用保存在了函数a之外,而b这个函数在声明的时候,它的scope chain仍然和a执行是同一个,
即:0:aAO;1:GO,就是把a中的执行上下文保存在了b函数中。在执行函数b的时候回生成一个bAO放在scope chain的0位置,执行完的
时候删除这个bAO,而在1、2位置的0:aAO;1:GO被永久的保留了下来。
aAO中是由a = 123这个变量的,故而最终的结果是打印出a的值!~
这 就是闭包!
**注意点**------当内部函数被保存到外部时,将会生成闭包。闭包会导致原有scope chain(作用域链)不释放,造成内存泄漏
内存泄漏:由于内存一直占用,导致可用内存空间减小,感觉就像内存泄漏了一样,就叫内存泄漏了~~!会导致加载非常慢!!
scope chain 中的没生成一个新的ao,必然继承外部函数产生的ao与go,这里的继承是指指向了原来那个AO、GO,而不是生成一个一模一样的
ex:
<body>
<script type="text/javascript">
function text(){
var num = 100;
function b(){
num ++;
console.log(num);
}
function b(){
num --;
console.log(num);
}
return [a,b];
}
var myArr = text();
myArr[0]();
myArr[1]();
</script>
</body>
输出的结果是:101 100
而不是101 99,注意其中的区别!
所以再修改来自外部函数的ao时,是修改的同一个AO
ex:
<body>
<script type="text/javascript">
function a(){
var num = 100;
function b(){
num ++;
console.log(num);
}
return b;
}
var demo = a();
demo();
demo();
</script>
</body>
此例的结果是:101
102
有累加的效果
闭包的作用:1实现共有变量----->函数累加器
2可以做缓存(存储结构)----->eater
3可以实现封装,属性私有化------>Person();
4模块化开发,防止污染全局变量
ex: 实现共有变量----->函数累加器
<body>
<script type="text/javascript">
function add(){
var count = 0;
function demo(){
count ++;
consloet.log(count);
};
return demo;
}
var counter = add();
counter();
counter();
counter();
counter();
counter();
counter();
counter();
counter();
</script>
</body>
输出结果: 1 2 3 4 5 6 7
ex:可以做缓存(存储结构)----->eater 3
<body>
<script type="text/javascript">
function eater(){
var food = "";
var obj = {
eat : function(){
console.log("i am eating!" + food);
food = "";
},
push : function(myfood){
food = myfood;
}
}
return obj;
}
var demo = a();
erter1.push('banana');
eater1.eat();
</script>
</body>
输出结果; i am eating banana
其中实际有两个过程,一个把banana放入myfood,第二个过程才是打印 i am eating banana
立即执行函数:js提供的唯一一个可以立即销毁函数的东西
功能:除了执行完就被释放之外和普通函数没有任何区别(有参数,有返回值(返回值又一个变量保存,类似变量的赋值),有预编译,作用域......)
有些函数出生只是执行一次,执行完就没必要保留,因此需要立即执行函数,可以优化内存。比如初始化功能;计算某个参数的值,得出后就不再使用此函数,此函数只被执行一次。
语法:括号中放一个匿名函数(没有函数名的函数或者说不需要函数名的函数)
(function (){}())
ex:
(function (){
var a = 123;
var b = 234;
console.log(a + b);
})
匿名函数深入:
匿名函数其实不是js可以做出来的,而是人们在使用函数的时候不经意发现的一种用法,类似css中的float,出生的时候并不是为了浮动的,而只是为了文字环绕图片这个功能,只是在人们的使用中发现了一个更有价值的功能。
匿名函数并不是明文规定的语法,只是利用了()的性质特点(有执行的作用,也有运算符优先级的作用---先算小括号内的)
官方写法:(function(){}());W3C组织建议这种
(function (){})();
只有表达式才能被执行符号:()执行
函数申明与函数表达式是俩东西:
函数申明:function test (){}
函0数表达式:var fun = function(){}
为什么function test (){}后面加一对括号不能使用呢?
因为function test (){}虽然和表达式一样,但它叫“函数声明”
var fun = function(){}在后面加一对括号:var fun = function(){}()就能被执行
能被执行符号执行的表达式,这个函数的名字就会被自动忽略
如:var fun = function(){}()在执行之后,在使用fun,就会报错:undefined,也就是不再是一个函数
函数申明前面加一个正号“+”
+ function test(){
console.log("a");
}
这个照样能被执行,加负号“-"(这里不能看成加号减号),逻辑与“&&” 逻辑或“||”前面得加东西,逻辑非“!”都是可以的。
注意:乘号除号是不行的
原因:
一个表达式在前面加了一个运算符,在趋势上就会把它理解成数字,也就理解成表达式了,类似数学中的a=x
理解成一个赋值,而a = x+3就可以理解成一个表达式;
(function test (){
console.log("a");
})()
这个也照样能被执行
(function test (){
console.log("a");
}())
这个照样能被执行,原因是:先看最外层的()
一个表达式在前面加了一个运算符,在趋势上就会把它理解成数字,也就理解成表达式了,类似数学中的a=x
理解成一个赋值,而a = x+3就可以理解成一个表达式;
,js就会把它当做一个
阿里巴巴笔试题:变态恶心
function test (a,b,c,d){
console.log(a+b+c+d);
}(1,2,3,4);
这个题看着理论上是坚决不能执行的,但是!!js会尽量识别成能不报错的样子:
function test (a,b,c,d){
console.log(a+b+c+d);
}
(1,2,3,4);
上下部分分家;虽然不报错也不会执行,没意义。
************************闭包的解决************************
ex:
<body> <script type="text/javascript"> function test(){ var arr = []; for(var i = 0, i<10;i++){ arr[i] = function(){ console.log(i); } } return obj; } var myArr = test(); for(var j = 0; j < 10; j++){ myArr[j](); } </script> </body>
此例输出的结果为:10 10 10 10 10 10 10 10 10 10
原因为闭包中保存出来的函数每一个函数都拥有外部函数test的AO,并且是同一个AO,也就是说每一个函数修改的都是同一个i值,当数组中的是个函数形成的时候,经过for循环i已经变成了10,因此在调用myArr里面的函数的时候,打印出来的全部是十。
一对十
问题来了,如果我们想要的结果如果是:0 1 2 3 4 5 6 7 8 9 就需要解决上面的问题。
<body> <script type="text/javascript"> function test(){ var arr = []; for(var i = 0, i<10;i++){ arr[i] = function(){ console.log(i); } } return obj; } var myArr = test(); for(var j = 0; j < 10; j++){ myArr[j](); } </script> </body>
超级简单粗暴的方法:
</!DOCTYPE html>
<html lang="en">
<head>
<title>这是一个闭包 </title>
</head>
<body>
<script type="text/javascript">
function test(){
var arr = [];
for (var i = 0; i < 10; i++) {
arr[i] = function(){
//console.log(i);
document.write(j);
}
}
return arr;
}
var myArr = test();
for (var j = 0; j < 10; j++) {
myArr[j]();
}
</script>
</body>
</html>
自己想的原因是:因为闭包只是保存了myArr这个包含十个函数的数组,里面的函数是定义好的,函数内部包含一个未声明的变量,本来在执行的时候应该会报错 j is not define。但我们在形成闭包的时候并不执行函数,所以不存在报错。
而在外部使用这些函数的时候,由于for循环中有变量j,故而在执行的时候生成的ao里面产生了j这个变量,所以产生了这个结果~~
太有意思了,哈哈哈
正经方法:使用立即执行函数
</!DOCTYPE html>
<html lang="en">
<head>
<title>这是一个闭包 </title>
</head>
<body>
<script type="text/javascript">
function test(){
var arr = [];
for (var i = 0; i < 10; i++) {
(function(j){
arr[j] = function(){
console.log(j);
}
}(i))
}
return arr;
}
var myArr = test();
for (var j = 0; j < 10; j++) {
myArr[j]();
}
</script>
</body>
</html>
理解:闭包是由于循环的时候i在变化中无法直接保存到函数外部,导致最后十个函数打印出的都是10
那么解决的思路就是,在循环进行的时候,直接把函数保存成打印循环进行时候具体的每个i的值
方法,用一个立即执行函数接收循环变量i,把这个i的值传入arr中的函数内部,那么在闭包形成后,
myArr中的每个函数打印的结果都不是10了,而是从0-9的数字。
浙公网安备 33010602011771号