JS闭包、定时器

1.什么是闭包? 有什么作用

函数的作用域scope取决于声明时,而非调用时。普通函数执行后函数体及内部变量会被垃圾清理机制回收,构建闭包closure能让函数内部的变量驻留在内存中并被外部引用。闭包可以理解为保留函数声明时内部变量作用域&函数体构成的一个环境,通过这个环境能读取函数内部的变量。嵌套函数中子函数返回构成闭包时,返回的内容包括该函数生成时的变量作用域关系,即环境变量+函数。
闭包有两个基本特征:1.父函数嵌套子函数,返回子函数执行结果。2.父函数作用域的变量被子函数引用着。
闭包的实际作用:1.读取函数内部的变量;2.内存不被释放,让变量的值始终保持在内存中(但使用不当会引起内存泄漏消耗过多内存资源)。
闭包的应用场景:1.声明私有变量。2.隔离作用域。3.构建计数器。
经典错误案例

var count = []
for (var i = 0; i < 10; i++) {
 count[i] = function() {
 return i
 }
}
console.log(count[0]()) //10
console.log(count[1]()) //10
console.log(count[2]()) //10
console.log(count[3]()) //10

以上代码count[n]()每次执行都返回结果10。实际预期是count[n]() 返回结果n。每次返回10的原因是for循环运行后count[i]数组中的每个成员实际是一个匿名函数function(){return i},for循环执行完后i变为10,所以执行count[n]()实际上是执行数组成员中的匿名函数即(function(){return i}()),i的值已变为10所以每次执行结果为10。

改进代码后如下:

var count=[];
for(var i=0;i<10;i++){
count[i]=(function t1(){
var n=i
return function t2(){
return n;
}
}())
}
console.log(count[0]()) //0
console.log(count[1]()) //1
console.log(count[2]()) //2
console.log(count[3]()) //3

count[i]数组的成员变成了10个,由立即执行函数t1即(function t1(){}())的执行结果function t2(){return n},这里n是由i赋值得来,当i循环递增时n随之改变,由于子函数t2中变量n始终从父函数t1的作用域上寻找,所以n始终被保留在内存中,每次i循环递增n就赋值并保留下来。当执行count[n]()即执行(function t2(){return n}())


2.setTimeout 0 有什么作用

var timerId = setTimeout(func|code, delay)
delay参数用来指定在当前JS队列中的任务执行完后,延迟多长时间将函数/代码段添加到任务队列尾部并执行。
以下代码运行后返回一个整数,表示定时器的编号,可以用于取消这个定时器。但实际任务中,很少这么用。运行clock01返回整数表示定时器编号。
var clock01=setTimeout(function(){var ccc=10},5000)

setTimeout(function(){},0)表示指定的任务在现有的任务队列执行完毕之后添加并立即执行。


3.下面的代码输出多少?修改代码让fnArri 输出 i。使用两种以上的方法
var fnArr = [];
    for (var i = 0; i < 10; i ++) {
        fnArr[i] =  function(){
            return i;
        };
    }
    console.log( fnArr[3]() );  //

代码如下:

方法一:
var fnArr=[];
for (var i=0;i<10;i++){
fnArr[i]=(function() t1{
var n=i;  //t1函数立即执行,不指定传入参数,在父函数t1中声明变量n,n由i赋值,t1执行返回函数t2
return function() t2{
return n //t2函数执行返回n,每次i改变后n会保留下来
}
}())
}

方法二:

var fnArr=[];
for (var i=0;i<10;i++){
fnArr[i]=(function t1(n){ //这里参数n=i,相当于var n=i
return function t2(){
return n //返回变量n,在父函数t1的作用域中找到n
}
}(i)) //函数立即执行时传入参数i
}

4.使用闭包封装一个汽车对象,可以通过如下方式获取汽车状态
var Car = //todo;
Car.setSpeed(30);
Car.getSpeed(); //30
Car.accelerate();
Car.getSpeed(); //40;
Car.decelerate();
Car.decelerate();
Car.getSpeed(); //20
Car.getStatus(); // 'running';
Car.decelerate(); 
Car.decelerate();
Car.getStatus(); //'stop';
Car.speed; //error

代码如下:

var Car=(function t1(){
 var Speed=0;
 function setSpeed(n){
 Speed=n;
 return Speed;
 }

 function getSpeed(){
 console.log(Speed)
 return Speed;
 }

 function accelerate(){
 Speed+=10;
 return Speed;
 }

 function decelerate(){
 Speed-=10;
 return Speed;
 }

 function getStatus(){
 if(Speed>0){
 console.log("running");
 } else if(Speed===0){
 console.log("stop");
 }
 }

 function error(){
 console.log("error:");
 }

 return {
 "setSpeed":setSpeed,
 "getSpeed":getSpeed,
 "accelerate":accelerate,
 "decelerate":decelerate,
 "getStatus":getStatus,
 "speed":error()
 }
}())

Car.setSpeed(30);
Car.getSpeed(); //30
Car.accelerate();
Car.getSpeed(); //40;
Car.decelerate();
Car.decelerate();
Car.getSpeed(); //20
Car.getStatus(); // 'running';
Car.decelerate(); 
Car.decelerate();
Car.getStatus(); //'stop';
Car.speed; //error

5.写一个函数使用setTimeout模拟setInterval的功能
var i=0; //声明初始值由0开始
(function intv(){
 setTimeout(function(){//setTimeout(函数,时间间隔)
 console.log(i++)
 intv();//由于setTimeout只执行一次,所以用递归调用自身做循环
 },1000)
})()

6.写一个函数,计算setTimeout平均最小时间粒度
function mintime(){
var i=0;
var starttime=Date.now();
var timer=setTimeout(function t1(){
i++;
if(i===1000){
clearTimeout(timer);
var endtime=Date.now();
console.log((endtime-starttime)/i)
} else{
timer=setTimeout(t1.arguments.callee,0);
}
},0)
}
mintime();

7.下面这段代码输出结果是? 为什么?
var a = 1;
setTimeout(function(){
 a = 2;
 console.log(a);
}, 0);
var a ;
console.log(a);
a = 3;
console.log(a);

setTimeout会将其参数1中的函数或代码段在JS任务队列中的当前任务执行完后,延迟指定的时间放入队列尾部执行。
以上分析执行过程为

var a=1;
var a;
console.log(a); //返回1
a=3;
console.log(a); //返回3
setTimeout(function(){
a=2;
console.log(a); //放回2
},0)

8.下面这段代码输出结果是? 为什么?
var flag = true;
setTimeout(function(){
 flag = false;
},0)
while(flag){}
console.log(flag);

以上代码分析如下:

var flag=true;
while(flag){}//while循环条件成立,由于没有break/continue会一直执行
console.log(flag);
setTimeout(function(){//待当前队列中所有任务执行完后,延迟0毫秒将函数放入队列尾部执行
flag=false;
},0)

实际执行结果是不显示任何内容,while循环条件成立没有break/continue终止,会一直执行循环,后面的console.log无法执行


9.下面这段代码输出?如何输出delayer: 0, delayer:1...(使用闭包来实现)
for(var i=0;i<5;i++){
 setTimeout(function(){
 console.log('delayer:' + i );
 }, 0);
 console.log(i);
}

实现如下:

//方法一
for(var i=0;i<5;i++){
 setTimeout((function t1() {
 var m=i; //父函数t1下声明变量m,m的值由for循环中的作用域变量i赋值得来,m保留每次i递增后的值
 return function t2() {
 console.log('delayer:' + m )
 }
 }()),0);
 console.log(i);
}

//方法二:写法与方法一类似,只不过t1()立即执行函数执行时传入实际参数i赋值给t1定义的形式参数m
for(var i=0;i<5;i++){
 setTimeout((function t1(m){//定义函数t1形式参数m,m由t1函数立即执行时传入的实际参数i赋值得来,相当于在t1函数中var m=i;
 return function t2(){
 console.log('delayer:' + m )
 }
 }(i)),0);//t1函数立即执行时传入实际参数i
 console.log(i);
}

//方法三:父函数t1是立即执行函数,t1下声明变量m,m的值由for循环的作用域变量i赋值得来。m保留每次i递增后的值
for(var i=0;i<5;i++){
 (function t1(){
 var m=i
 return (setTimeout(function t2(){
 console.log('delayer:'+m)
 },0))
 }())
 console.log(i)
}

//方法四:
for(var i=0;i<5;i++){
 (function t1(m){//父函数t1定义形式参数m,m的值有t1函数立即执行时传入的实际参数i赋值得来,相当于在t1函数中var m=i
 return setTimeout(function t2(){
 console.log('delayer:'+m)
 },0)
 }(i)) //t1立即执行函数传入实际参数i
 console.log(i)
}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,406评论 6 503
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,732评论 3 393
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,711评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,380评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,432评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,301评论 1 301
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,145评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,008评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,443评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,649评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,795评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,501评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,119评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,731评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,865评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,899评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,724评论 2 354

推荐阅读更多精彩内容

  • 什么是闭包? 有什么作用闭包:函数对象可以通过作用域链相互关联,函数体内部的变量可以保存在函数的作用域内。 上述代...
    coolheadedY阅读 727评论 0 0
  • 一、问题 (一)、什么是闭包? 有什么作用 闭包是指能够访问自由变量的函数 (变量在本地使用,但在闭包中定义)。换...
    该帐号已被查封_才怪阅读 397评论 0 1
  • 什么是闭包? 有什么作用? 闭包是指有权限访问另一个函数作用域的变量的函数(就是能够读取其他函数内部变量的函数)。...
    _fin阅读 687评论 0 3
  • 问题 什么是闭包? 有什么作用 闭包可以用来读取函数内部的变量。 由于作用域链表,外部是无法读取到函数内部的变量的...
    不是鱿鱼阅读 390评论 0 0
  • 今天的由于身体的原因,我的感觉陷入到混沌状态,头脑一直处于空白的状态,思维能力急速下降,理解力也脱节,刚才孩子和我...
    尚巾林阅读 206评论 0 0