閉包及其作用
- 閉包是能夠讀取其他函數(shù)內(nèi)部變量的函數(shù)污淋;相當(dāng)于在草原上用柵欄單獨(dú)圍一片地只留一個(gè)進(jìn)出口;只有通過返回出的函數(shù)才能去修改其內(nèi)部的變量叶洞。
- 有兩個(gè)作用:①讀取/修改其他函數(shù)內(nèi)部的變量鲫凶,形成單獨(dú)的一個(gè)空間,不受外部影響衩辟,可用于封裝螟炫。②使其他函數(shù)內(nèi)部的變量不會被銷毀而始終存在于內(nèi)存中。
setTimeout 0 的作用
- setTimeout 0 的作用并不在于其延時(shí)0的這個(gè)字面意思艺晴,而是js對它的處理機(jī)制上昼钻;對被setTimeout包裹的代碼段,不管其延時(shí)多久封寞,都會被js放到代碼的尾部然评,只有等其他代碼執(zhí)行完畢后才會按延時(shí)時(shí)間去運(yùn)行其內(nèi)部的代碼;
- 根據(jù)這個(gè)特性狈究,我們可以把需要最后執(zhí)行的代碼用setTimeout 0進(jìn)行包裹碗淌,確保最后執(zhí)行;在部分事件上抖锥,例如我需要一個(gè)div顯示用戶輸入文本框的內(nèi)容亿眠,這個(gè)時(shí)候我使用onkeydown來綁定更新輸入值,當(dāng)鍵盤被按下時(shí)開始更新輸入值磅废;但是第一次按下鍵盤輸入時(shí)纳像,此時(shí)js的處理結(jié)果還沒出來,但由于我綁定按下這個(gè)條件就會更新輸入值拯勉,所以等于第一次輸入去獲取更新時(shí)是空的竟趾,這樣在用戶看來就是我按下第二個(gè)鍵才顯示之前輸入的內(nèi)容;那么我們可以怎么做呢宫峦?對岔帽,在獲取更新的這個(gè)代碼上加setTimeout 0 ,讓其跑到j(luò)s處理結(jié)果之后去更新斗遏,這樣就能實(shí)時(shí)顯示輸入值了山卦!
小練習(xí)
下面的代碼輸出多少鞋邑?修改代碼讓fnArri 輸出 i
var fnArr = [];
for (var i = 0; i < 10; i ++) {
fnArr[i] = function(){
return i;
};
}
console.log( fnArr[3]() ); // 輸出為10
方法1 使用閉包:
var fnArr = [];
for (var i = 0; i < 10; i ++) {
fnArr[i] = function(i){
return function(){
return i;
};
}(i);
}
console.log( fnArr[2]() ); // 2
方法2 把i綁定到函數(shù)上 作為函數(shù)的一個(gè)屬性存儲起來
var fnArr = [];
for (var i = 0; i < 10; i ++) {
var print=function(){};
print.index=i;
fnArr[i]=print;
}
console.log( fnArr[5].index ); // 5
方法3 用兩個(gè)立即執(zhí)行函數(shù)返回i 賦給fnArr[i]诵次;相當(dāng)于fnArr[i]=i账蓉,數(shù)組里面放的是固定的數(shù)(好像沒什么。逾一。铸本。但是fnArr[i]可以輸出i)
var fnArr = [];
for (var i = 0; i < 10; i ++) {
fnArr[i] = function(){
return function(){
return i;
}();
}();
}
console.log( fnArr[7] ); // 7
使用閉包封裝一個(gè)car對象
var Car = function(){
var speed=0;
return{
setSpeed:function (num){
speed=num;
},
getSpeed:function (){
console.log(speed);
},
accelerate:function (){
speed=speed+10;
},
decelerate:function (){
speed=speed-10;
},
getStatus:function (){
if(speed>0)
console.log('running')
else console.log('stop')
}
}
}();
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
使用setTimeout模擬setInterval的功能
function newInterval(func,time){ //定義偽間隔函數(shù) 一個(gè)形參為‘函數(shù)’,另一個(gè)形參為‘間隔時(shí)間’
setTimeout(function(){
func();
newInterval(func,time);
},time);
}
var i=1;
function count(){ //舉個(gè)例子 使用計(jì)數(shù)函數(shù)
console.log(i++);
}
newInterval(count,1000);
計(jì)算setTimeout最小時(shí)間粒度
function min(){
var t1=Date.now();
var i=0;
var clock=setTimeout(function(){
i++;
if (i==5000){
clearInterval(clock);
console.log((Date.now()-t1)/i);
}
clock=setTimeout(arguments.callee,0)
},0);
}
min(); // 4.102 4.0982 4.0968 最小粒度應(yīng)該為4ms
解釋如下代碼的輸出
var flag = true;
setTimeout(function(){
flag = false;
},0)
while(flag){} //沒有輸出結(jié)果 因?yàn)閟etTimeout所包裹的代碼將會被放到末尾執(zhí)行 flag一直是true 導(dǎo)致循環(huán)一直在進(jìn)行遵堵,后面的代碼在排隊(duì)等候中
console.log(flag);
下面這段代碼輸出箱玷?如何輸出delayer: 0, delayer:1...(使用閉包來實(shí)現(xiàn))
for(var i=0;i<5;i++){
setTimeout(function(){
console.log('delayer:' + i ); //
}, 0);
console.log(i); // 先輸出 0 1 2 3 4 ,再輸出 delayer:5
}
讓其輸出delayer: 0, delayer:1的方法如下:
for(var i=0;i<5;i++){
(function(i){
setTimeout(function(){
console.log('delayer:' + i );
}, 0);
console.log(i); //先輸出 0 1 2 3 4 再輸出 delayer:0 delayer:1 delayer:2 delayer:3 delayer:4
})(i);
}