問(wèn)答
1.什么是閉包础拨?有什么作用氮块?
- 閉包
閉包就是能夠讀取其他函數(shù)內(nèi)部變量的函數(shù)。在JavaScript中诡宗,只有函數(shù)內(nèi)部的子函數(shù)才能夠讀取內(nèi)部變量滔蝉,因此可以把閉包簡(jiǎn)單理解成“定義在一個(gè)函數(shù)內(nèi)部的函數(shù)∷郑”
比如在函數(shù)內(nèi)部讀取它的內(nèi)部變量:
function f1(){
var n = 999;
function f2(){
console.log(n);
}
return f2;
}
var result = f1();
resulet(); //999
上段代碼中蝠引,函數(shù)f2就是閉包。
- 作用
1.可以讀取函數(shù)內(nèi)部的變量。
2.讓這些變量始終保存在內(nèi)存中螃概。
舉例來(lái)看閉包的作用:
function f1(){
var n = 999;
nAdd = function(){n+=1}
function f2(){
console.log(n);
}
return f2;
}
var result = f1();
result();//999
nAdd();
result();//1000
這段代碼中矫夯,函數(shù)f2就是閉包,它一共運(yùn)行了2次吊洼,第一次是999训貌,第二次是1000,這說(shuō)明了函數(shù)f1的局部變量n一直保存在內(nèi)存中冒窍,并沒(méi)有在f1調(diào)用后清除递沪。
原因是f2被賦給了一個(gè)全局變量result,導(dǎo)致f2一直存在內(nèi)存中综液,而f2的存在依賴于f1款慨,所以f1也始終在內(nèi)存中。
2. setTimeout 0有什么作用
setTimeout函數(shù)用來(lái)指定某個(gè)函數(shù)或某段代碼在多少毫秒之后執(zhí)行意乓,它接受兩個(gè)參數(shù)樱调,要執(zhí)行的代碼和以毫秒表示的時(shí)間。
舉例:
setTimeout(function() {
console.log("jirengu")
},1000);//返回1届良,一秒鐘后打印出jirengu
返回的整數(shù)表示這個(gè)定時(shí)器的編號(hào)笆凌,以后可以用來(lái)取消這個(gè)定時(shí)器,但實(shí)際任務(wù)中士葫,很少這么用乞而。
setTimeout函數(shù)的重點(diǎn)是第二個(gè)參數(shù),第二個(gè)參數(shù)是一個(gè)表示等待多長(zhǎng)時(shí)間的毫秒數(shù)慢显,但是經(jīng)過(guò)該時(shí)間的后執(zhí)行的代碼不一定會(huì)執(zhí)行爪模。
在理解這句話之前先說(shuō)說(shuō)JavaScript的運(yùn)行機(jī)制:
JavaScript是一個(gè)單線程的解釋器,一定時(shí)間之內(nèi)只能執(zhí)行一段代碼荚藻。
為了控制要執(zhí)行的代碼屋灌,就有一個(gè)JavaScript任務(wù)隊(duì)列。這些任務(wù)會(huì)按照將它們添加到隊(duì)列的順序執(zhí)行应狱。
setTimeout()的第二個(gè)參數(shù)告訴JavaScript再過(guò)多長(zhǎng)時(shí)間把當(dāng)前任務(wù)添加到隊(duì)列中共郭。也就是說(shuō)setTimeout()指定的任務(wù)肯定是最后一個(gè)添加到隊(duì)列中的,這個(gè)指定的任務(wù)要等到前面的任務(wù)執(zhí)行完了以后再執(zhí)行疾呻。
所以setTimeout函數(shù)的第二個(gè)參數(shù)應(yīng)該理解為等到前面的任務(wù)執(zhí)行之后再經(jīng)過(guò)指定的毫秒數(shù)后來(lái)執(zhí)行當(dāng)前的任務(wù)除嘹。
舉例來(lái)說(shuō)明吧:
console.log(1)
setTimeout(function() {
console.log(2)
},100);
var t1 = Date.now()
for(var i=0;i<10000;i++){
console.log(3);
}
var t2 = Date.now()
console.log(t2-t1);
從運(yùn)行結(jié)果中看到,console.log(2)
確實(shí)是最后運(yùn)行岸蜗,而且是等到完成for循環(huán)的后再運(yùn)行的,而不是之前理解的100ms之后會(huì)立即運(yùn)行尉咕。
那么最后解釋setTimeout 0就比較容易了,指的是setTimeout指定的任務(wù)在現(xiàn)有的任務(wù)執(zhí)行之后立即執(zhí)行璃岳。
代碼題
1.下面的代碼輸出多少年缎?修改代碼讓fnArr[i]()
輸出i.使用兩種以上的方法
var fnArr = [];
for (var i = 0; i < 10; i ++) {
fnArr[i] = function(){
return i;
};
}
console.log( fnArr[3]() ); //輸出結(jié)果為10
在for循環(huán)中悔捶,i的值并沒(méi)有隨著循環(huán)保存在函數(shù)中,所以關(guān)鍵在于如何保存i的值晦款,所以有了以下閉包的方法:
//方法一:
var fnArr = [];
for (var i =0; i<10; i++){
fnArr[i] = (function(n){
return function(){
return n;
}
})(i)
}
console.log(fnArr[3]());
//方法二:
var fnArr = [];
for(var i=0; i<10; i++){
(function(n){
fnArr[n] = function(){
return n;
}
})(i)
}
console.log(fnArr[3]());
2.使用閉包封裝一個(gè)汽車(chē)對(duì)象炎功,可以通過(guò)以下方式獲取汽車(chē)狀態(tài)
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
實(shí)現(xiàn)代碼:
var Car = (function(){
var speed = 0;
function setSpeed(){
speed = arguments[0];
}
function getSpeed(){
console.log(speed);
return speed;
}
function accelerate(){
speed += 10;
}
function decelerate(){
speed -= 10;
}
function getStatus(){
if(speed > 0){
console.log("running");
return "running";
}else {
console.log("stop");
return "stop";
}
}
return {
"setSpeed":setSpeed,
"getSpeed":getSpeed,
"accelerate":accelerate,
"decelerate":decelerate,
"getStatus":getStatus
}
})()
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';
3.寫(xiě)一個(gè)函數(shù)使用setTimeout模擬setInterval的功能
//方法一
function interval(func,time){
var intv = function(){
func.call(null);
setTimeout(intv,time);
}
setTimeout(interval,time);
}
interval(function(){
console.log(1);
},1000);
//方法二
function interval(func, time){
setTimeout(function(){
func.call(null);
interval(func,time);
},time)
}
interval(function(){
console.log(1);
},1000)
需要注意的是,使用setTimeout并不能完全模擬出setInterval的功能缓溅。
在問(wèn)答1中介紹過(guò)蛇损,setTimeout是等前面的任務(wù)執(zhí)行之后再開(kāi)始計(jì)算時(shí)間間隔。
而setInterval函數(shù)指定的是“開(kāi)始執(zhí)行”之間的間隔坛怪,并不考慮每次任務(wù)執(zhí)行本身所消耗的時(shí)間淤齐,比如,setInterval指定每100ms執(zhí)行一次袜匿,每次執(zhí)行需要5ms更啄,那么第一次執(zhí)行結(jié)束后95毫秒,第二次執(zhí)行就會(huì)開(kāi)始居灯。如果某次執(zhí)行耗時(shí)特別長(zhǎng)祭务,比如需要105毫秒,那么它結(jié)束后怪嫌,下一次執(zhí)行就會(huì)立即開(kāi)始义锥。
4.寫(xiě)一個(gè)函數(shù),計(jì)算setTimeout平均最小時(shí)間粒度岩灭。
function getMini() {
var i = 0;
var start = Date.now();
var clock = setTimeout(function(){
i++;
if(i === 1000){
clearTimeout(clocl);
var stop = Date.now();
console.log((stop-start/1000));
}
else{
var clock = setTimeout(arguments.callee,0);
}
},0)
}
getMini();
5.下面這段代碼輸出結(jié)果是拌倍?為什么?
var a = 1;
setTimeout(function(){
a = 2;
console.log(a);
}, 0);
var a ;
console.log(a);
a = 3;
console.log(a);
由于setTimeout函數(shù)會(huì)把指定的任務(wù)放在最后執(zhí)行噪径,所以以上代碼實(shí)際執(zhí)行順序如下:
var a = 1;
var a ;
console.log(a);//1
a = 3;
console.log(a);//3
setTimeout(function(){
a = 2;
console.log(a);//2
}, 0);
所以最后的輸出結(jié)果為1,3,2.
6.下面這段代碼輸出結(jié)果是柱恤?為什么?
var flag = true;
setTimeout(function(){
flag = false;
},0)
while(flag){}
console.log(flag);
while方法會(huì)無(wú)限循環(huán)找爱,無(wú)任何輸出梗顺。
同上題,setTimeout函數(shù)會(huì)把指定的任務(wù)放在最后執(zhí)行车摄,所以實(shí)際執(zhí)行順序如下:
var flag = true;
while(flag){}
console.log(flag);
setTimeout(function(){
flag = false;
},0)
變量flag一直為true荚守,所以while方法會(huì)無(wú)限循環(huán)。
7.下面這段代碼輸出练般?如何輸出如何輸出delayer: 0, delayer:1...(使用閉包來(lái)實(shí)現(xiàn))
for(var i=0;i<5;i++){
setTimeout(function(){
console.log('delayer:' + i );
}, 0);
console.log(i);
}
實(shí)現(xiàn)代碼:
//方法一
for(var i=0; i<5; i++){
(function(n){
setTimeout(function(){
console.log("delayer:" + n);
},0)
})(i);
console.log(i);
}
//方法二
for(var i=0; i<5; i++){
setTimeout((function(n){
return function(){
return console.log("delayer:" + n);
}
})(i),0)
console.log(i);
}
本文版權(quán)歸本人和饑人谷所有,轉(zhuǎn)載請(qǐng)注明來(lái)源锈候。