您的位置:首页 > Web前端 > JavaScript

你不知道的JS---作用域和闭包

2017-08-07 16:13 253 查看

立即执行函数表达式

var a=2;
(function foo() {
var a=3;
console.log(a);
})();
console.log(a);


函数被包含在一个括号内部,因此成为了一个表达式,通过末尾加上另外一个括号可以立即执行这个函数。

闭包的定义

当函数可以记住并访问所在的词法作用域时,就产生了闭包,即使函数是在当前词法作用域之外执行。

循环和闭包

要想每隔1s输出数字1-5.
for(var i=1;i<=5;i++){
setTimeout(function timer(){
console.log(i);
}, i*1000);
}
以每秒一次的频率输出五次6.
原因:延迟函数的回调会在循环结束时才执行,实际情况是 尽管循环中的五个函数是在各个迭代中分别定义的,但他们都被封闭在一个共享的全局作用域中,因此实际上只有一个i。

IIFE会通过声明并立即执行一个函数来创建作用域。
for(var i=1;i<=5;i++){
(function(){
setTimeout(function timer(){
console.log(i);
}, i*1000);
})();
}
但是,通过上面的立即调用函数依然达不到效果,我们的IIFE只是一个什么都没有的空作用域,它需要有自己的变量,用来在每个迭代中储存i的值。
for(var i=1;i<=5;i++){
(function(){
var j=i;
setTimeout(function timer(){
console.log(j);
}, j*1000);
})();
}
对上述代码的改进:
for(var i=1;i<=5;i++){
(function(j){
setTimeout(function timer(){
console.log(j);
}, j*1000);
})(i);
}
在迭代内使用IIFE会为每个迭代都生成一个新的作用域,使得延迟函数的回调可以将新的作用域封闭在每个迭代内部,每个迭代中都会含有一个具有正确值的变量供我们访问。

同样可以实现该功能的代码还有:
for(var i=1;i<=5;i++){
let j=i;
setTimeout(function timer(){
console.log(j);
}, j*1000);
}
for(let i=1;i<=5;i++){
setTimeout(function timer(){
console.log(i);
}, i*1000);
}

变量的生命周期
var func=function () {
var a=1;
return function(){
a++;
alert(a);
}
};
var f=func();
f();//2
f();//3
f();//4
f();//5

当退出函数后,局部变量并没有消失,这是因为当执行var f=func()时,f返回一个匿名函数的引用,可以访问到func()被调用时产生的环境,而局部变量a一直存在这个环境里。

闭包的使用场景

1、函数作为返回值
2、函数作为参数传递
function F1(){
var a=100
return function(){
console.log(a)
}
}
var f1=F1()
function F2(fn){
var a=200
fn()
}
F2(f1)//100

实际开发中闭包的应用

// 闭包实际应用中主要用于封装函数,收敛权限
function isFirstLoad(){
var _list=[]
return function(id){
if(_list.indexOf(id)>=0){
return false
} else {
_list.push(id)
return true
}
}
}

// 使用
var firstLoad=isFirstLoad()
firstLoad(10)
firstLoad(10)
firstLoad(20)
1、封装变量
var mult=(function() {
var cache={};
return function(){
var args=Array.prototype.join.call(arguments, ',');
if(args in cache){
return cache[args];
}
var a=1;
for(var i=0,l=arguments.length;i<l;i++){
a=a*arguments[i];
}
return cache[args]=a;
}
})();
2、延续局部变量的寿命
var report=function(src){
var img=new Image();
img.src=src;
};
report('http://xxx.com/getUserInfo')

var report=(function(){
var imgs=[];
return function(src){
var img=new Image(src){
var img=new Image();
imgs.push(img);
img.src=src;
}
}
})();


闭包的特性:
1、作为函数变量的一个引用,当函数返回时,其处于激活状态
2、闭包就是当一个函数返回时,并没有释放资源的栈区
闭包的原理:
因为闭包只有在被调用时才执行操作,所以它可以被用来定义控制结构,多个函数可以使用同一个相同的环境,这使得他们可通过改变那个环境相互交流
闭包使用场景:
1、采用函数引用方式的setTimeout调用
2、将函数关联到对象的实例方法
3、封装相关的功能
闭包的好处:
1、逻辑连续,当闭包作为另一个函数调用参数时,避免脱离当前逻辑二单独编写额外逻辑
2、方便调用上下文的局部变量。
3、加强封装,可以达到对变量的保护作用
闭包的坏处:浪费内存,闭包中的变量常驻内存,对闭包的使用不当会造成无效内存的产生。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: