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