閉包
定義
「一個(gè)函數(shù)」+「訪問到的外部變量」= 閉包作用
創(chuàng)建內(nèi)部變量,既不能被外部隨意修改,又可以通過指定的函數(shù)接口來操作。原理
利用變量?jī)H在函數(shù)作用域內(nèi)部可用
的特點(diǎn),用子函數(shù)來操作 / 暴露父作用域內(nèi)的變量,用父函數(shù)來保護(hù)變量不被外界直接修改。-
舉例
function princess() { //從前有一位公主var adventures = []; //她生活在一個(gè)充滿奇幻冒險(xiǎn)的世界里 function princeCharming() { /* ... */ } //她遇到了她的白馬王子 var unicorn = { /* ... */ }, //帶著他騎著獨(dú)角獸開始周游這個(gè)世界 dragons = [ /* ... */ ], //與巨龍戰(zhàn)斗 squirrel = "Hello!"; //巧遇會(huì)說話的松鼠 adventures.push(unicorn, dragons, squirrel, ....); //還有其他一些新奇的事物 return { //但是她不得不回到她的王國(guó)里,面對(duì)那些年老的大臣。 story: function() { //她會(huì)經(jīng)常給那些大臣們分享她作為公主最近在外面充滿奇幻的冒險(xiǎn)經(jīng)歷 return adventures[adventures.length - 1]; } }; } var littleGirl = princess(); //但是在大臣們的眼里,總是認(rèn)為她只是個(gè)小女孩...... littleGirl.story(); //講的是一些不切實(shí)際,充滿想象的故事 即便所有大臣們知道他們眼前的小女孩是真的公主,但是他們卻不會(huì)相信有巨龍或獨(dú)角獸, 因?yàn)樗麄冏约簭膩頉]有見到過。大臣們只會(huì)覺得它們只存在于小女孩的想象之中。 但是我們卻知道小女孩述說的是事實(shí)......
定時(shí)器
setTimeout()
-
作用
設(shè)置一個(gè)定時(shí)器,在定時(shí)器到期后執(zhí)行一次函數(shù) -
語法
var timerId = setTimeout(執(zhí)行函數(shù)
,延遲毫秒
) // 返回一個(gè)整數(shù)
作為該延時(shí)操作的ID -
清除
clearTimeout( ID ) - **setTimeout 0 **
- 解析:setTimeout(f, 0) 即 將定時(shí)器
延遲毫秒
設(shè)置為0 - 作用:間接實(shí)現(xiàn)異步操作
- 原理:JS的運(yùn)行機(jī)制是逐行執(zhí)行代碼,即單線執(zhí)行。
setTimeout的運(yùn)行機(jī)制是將指定函數(shù)移出本輪執(zhí)行等到下一輪再檢查是否到指定時(shí)間,如果沒到繼續(xù)等待下輪執(zhí)行。
setTimeout0 表示盡早執(zhí)行而非立刻執(zhí)行 即 把指定函數(shù)放到最后再執(zhí)行
setInterval()
-
作用
重復(fù)調(diào)用一個(gè)函數(shù),在每次調(diào)用之間以固定的時(shí)間延遲 -
語法
var intervalId = setInterval(執(zhí)行函數(shù)
,延遲毫秒
) // 返回一個(gè)整數(shù)
作為該延時(shí)操作的ID -
清除
clearInterval( ID ) -
缺點(diǎn)
1某些間隔會(huì)被跳過
2多個(gè)定時(shí)器的代碼執(zhí)行時(shí)間可能會(huì)比預(yù)期小
應(yīng)用
- **Q:fnArr[ i ] = 10 **
var fnArr = [];
for (var i = 0; i < 10; i ++) {
fnArr[i] = function(){
return i; ---------------------->i=10 時(shí)滿足跳出條件
};
}
console.log( fnArri ); // 所以總是輸出10
A: fnArr[ i ] = i
方法一:隱藏變量 i
var fnArr = [];
for (var i = 0; i < 10; i ++) {
fnArr = (function(){ ---------------------->保存i每次的變量值,防止繼續(xù)循環(huán)
var n = i;
return function(){ ----------------------> 暴露i此次的值
retur n;
}
} )(i)
}
console.log( fnArr3 ); // 3
方法二:隱藏整個(gè)函數(shù)
var fnArr = [];
for(var i = 0; i < 10; i++){
(function(n){ ----------------------> 保存整個(gè)函數(shù)
var n = i;
fnArr[i] = function(){ ------------> 每次拿到i的變量值 都執(zhí)行一次函數(shù)
return n;
}
})(i)
}
console.log( fnArr3 );
Q:使用閉包封裝一個(gè)汽車對(duì)象,可以通過如下方式獲取汽車狀態(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
**A: **
var car=(function(){
var speed=0;
function setSpeed(n){ return speed=n; }
function getSpeed(){ return speed; }
function accelerate(){ speed +=10; }
function decelerate(){ speed -=10; }
function getStatus(){
if(speed>0){
return "running";
} else{
return "stop";
}
}
return {
setSpeed: setSpeed,
getSpeed: getSpeed,
accelerate: accelerate,
decelerate: decelerate,
getStatus: getStatus
}
})();Q:
用setTimeout 模擬出setInterval
.VSsetTimeout
區(qū)別:
setTimeout
重復(fù)運(yùn)行時(shí),延時(shí)= 定時(shí) + 函數(shù)執(zhí)行時(shí)間
setInterval
運(yùn)行時(shí),延時(shí)= 定時(shí),即已經(jīng)包括 函數(shù)執(zhí)行時(shí)間應(yīng)用:
setTimeout
避免連續(xù)調(diào)用繁復(fù)函數(shù)產(chǎn)生互相干擾的問題
setInterval
要求時(shí)間間隔非常精確實(shí)現(xiàn):
var i = 0;
function intv(){
setTimeout(function(){
console.log(i++);
intv();
},1000);
}
intv();
-
Q: 計(jì)算
setTimeout
平均最小時(shí)間粒度
**A: **setTimeout(f, 0)
即使將第二個(gè)參數(shù)設(shè)為0,實(shí)際上也達(dá)不到0毫秒。根據(jù)html5標(biāo)準(zhǔn),setTimeout推遲執(zhí)行的時(shí)間最少是4毫秒。如果小于這個(gè)值,會(huì)被自動(dòng)增加到4。這是為了防止多個(gè) setTimeout(f, 0)語句連續(xù)執(zhí)行造成性能問題。function getMini(){ var i = 0; var statr = Date.now(); --------------->先獲取當(dāng)前時(shí)間點(diǎn) var clock = setTimeout(function(){ ---->setTimeout(f,0)盡快執(zhí)行函數(shù) i++; if(i === 1000){ ---------------->循環(huán)1000次,積累"盡快值" clearTimeout(clock); var end = Date.now(); ------------>循環(huán)1000次后的時(shí)間點(diǎn) console.log( (end-statr) / i);------->循壞i次所用時(shí)間除以i } clock = setTimeout(arguments.callee,0); },0) } getMini();
Q: 解析代碼
**A: **setTineout(f,0)調(diào)整了執(zhí)行順序
var a = 1;
setTimeout(function(){
a = 2;
console.log(a);--------------->第三次輸出a,a=2
}, 0);
var a ;
console.log(a);--------------->第一次輸出a,a=1
a = 3;
console.log(a);--------------->第二次輸出a,a=3
//輸出 1,3,2
-
Q: 解析代碼
**A: **死循環(huán)阻塞代碼執(zhí)行,無輸出
var flag = true; // 定義flag,并賦值為ture
setTimeout(function(){ // setTimeout(f,0)將函數(shù)放至末尾執(zhí)行
flag = false;
},0)
while(flag){} // 在這里無限循環(huán),后面的代碼都無法執(zhí)行
console.log(flag); // 所以執(zhí)行不到這里,什么也不會(huì)輸出
-
Q: 解析代碼
for(var i=0;i<5;i++){
setTimeout(function(){ // setTimeout(f,0)將函數(shù)放至末尾執(zhí)行
console.log('delayer:' + i ); // 再執(zhí)行到這里,i=5,所以循環(huán)輸出5個(gè)delayer:5
}, 0);
console.log(i); // 所以先執(zhí)行循環(huán),依次輸出01234
}
0
1
2
3
4
"delayer:5"
"delayer:5"
"delayer:5"
"delayer:5"
"delayer:5"
A: 改寫為依次輸出delayer: 0
,delayer:1
...
方法一:
for(var i=0;i<5;i++){
(function(n){ --------------->保存 整個(gè)函數(shù)的輸出值
setTimeout(function(){ --------------->函數(shù)放最后執(zhí)行
console.log('delayer:' + n ); ---------------> 最后遍歷 delayer:i
}, 0);
})(i)
console.log(i); --------------->所以先遍歷i的值
}
0
1
2
3
4
"delayer:0"
"delayer:1"
"delayer:2"
"delayer:3"
"delayer:4"
方法二:
for(var i=0;i<5;i++){
setTimeout((function(n){ --------------->保存單次 delayer:i ,并且延后執(zhí)行
console.log('delayer:' + n );
})(i), 0);
console.log(i); --------------->所以先輸出i,再輸出 delayer:i ,然后再次執(zhí)行
}
"delayer:0"
0
"delayer:1"
1
"delayer:2"
2
"delayer:3"
3
"delayer:4"
4